Merge "Add TransactionCompleteCallback"
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 6459805..72c03bf 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -68,15 +68,15 @@
apex::kApexPackageVendorDir};
for (const auto& dir : apex_dirs) {
// Cast call to void to suppress warn_unused_result.
- static_cast<void>(apex::scanPackagesDirAndActivate(dir));
+ static_cast<void>(apex::ScanPackagesDirAndActivate(dir));
}
- return apex::getActivePackages();
+ return apex::GetActivePackages();
}
static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) {
for (const apex::ApexFile& apex_file : active_packages) {
const std::string& package_path = apex_file.GetPath();
- base::Result<void> status = apex::deactivatePackage(package_path);
+ base::Result<void> status = apex::DeactivatePackage(package_path);
if (!status.ok()) {
LOG(ERROR) << "Failed to deactivate " << package_path << ": "
<< status.error();
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 7aac7da..b21010d 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -494,7 +494,7 @@
sp<BpBinder> bpBinder = binder->remoteBinder();
if (bpBinder == nullptr) return -1;
- return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+ return ProcessState::self()->getStrongRefCountForNode(bpBinder);
}
void ServiceManager::handleClientCallbacks() {
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 4aa2f60..639df7a 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -37,7 +37,6 @@
PLAYER_STATE_STARTED = 2,
PLAYER_STATE_PAUSED = 3,
PLAYER_STATE_STOPPED = 4,
- PLAYER_UPDATE_DEVICE_ID = 5,
} player_state_t;
// must be kept in sync with definitions in AudioManager.java
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 7d1f38f..2f5ccb8 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -49,8 +49,7 @@
audio_content_type_t content, const sp<IBinder>& player) = 0;
/*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage,
audio_content_type_t content)= 0;
- /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t deviceId) = 0;
+ /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0;
/*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0;
virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0;
/*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 91e465f..87eab52 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -206,7 +206,7 @@
if (proxy == nullptr) {
ALOGE("null proxy");
}
- const int32_t handle = proxy ? proxy->handle() : 0;
+ const int32_t handle = proxy ? proxy->getPrivateAccessorForHandle().handle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
@@ -2509,19 +2509,15 @@
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
const binder_size_t* objects, size_t objectsCount, release_func relFunc)
{
- binder_size_t minOffset = 0;
- freeDataNoInit();
- mError = NO_ERROR;
+ freeData();
+
mData = const_cast<uint8_t*>(data);
mDataSize = mDataCapacity = dataSize;
- //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)", this, mDataSize, getpid());
- mDataPos = 0;
- ALOGV("setDataReference Setting data pos of %p to %zu", this, mDataPos);
mObjects = const_cast<binder_size_t*>(objects);
mObjectsSize = mObjectsCapacity = objectsCount;
- mNextObjectHint = 0;
- mObjectsSorted = false;
mOwner = relFunc;
+
+ binder_size_t minOffset = 0;
for (size_t i = 0; i < mObjectsSize; i++) {
binder_size_t offset = mObjects[i];
if (offset < minOffset) {
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 9aedf28..6f26450 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -204,11 +204,11 @@
// that the handle points to. Can only be used by the servicemanager.
//
// Returns -1 in case of failure, otherwise the strong reference count.
-ssize_t ProcessState::getStrongRefCountForNodeByHandle(int32_t handle) {
+ssize_t ProcessState::getStrongRefCountForNode(const sp<BpBinder>& binder) {
binder_node_info_for_ref info;
memset(&info, 0, sizeof(binder_node_info_for_ref));
- info.handle = handle;
+ info.handle = binder->getPrivateAccessorForHandle().handle();
status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info);
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 2735315..22300ac 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
namespace internal {
class Stability;
}
+class ProcessState;
using binder_proxy_limit_callback = void(*)(int);
@@ -37,8 +38,6 @@
public:
static BpBinder* create(int32_t handle);
- int32_t handle() const;
-
virtual const String16& getInterfaceDescriptor() const;
virtual bool isBinderAlive() const;
virtual status_t pingBinder();
@@ -109,7 +108,23 @@
KeyedVector<const void*, entry_t> mObjects;
};
+ class PrivateAccessorForHandle {
+ private:
+ friend BpBinder;
+ friend ::android::Parcel;
+ friend ::android::ProcessState;
+ explicit PrivateAccessorForHandle(const BpBinder* binder) : mBinder(binder) {}
+ int32_t handle() const { return mBinder->handle(); }
+ const BpBinder* mBinder;
+ };
+ const PrivateAccessorForHandle getPrivateAccessorForHandle() const {
+ return PrivateAccessorForHandle(this);
+ }
+
private:
+ friend PrivateAccessorForHandle;
+
+ int32_t handle() const;
BpBinder(int32_t handle,int32_t trackedUid);
virtual ~BpBinder();
virtual void onFirstRef();
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 9f5260a..b49951b 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -504,9 +504,6 @@
const binder_size_t* objects, size_t objectsCount,
release_func relFunc);
- Parcel(const Parcel& o);
- Parcel& operator=(const Parcel& o);
-
status_t finishWrite(size_t len);
void releaseObjects();
void acquireObjects();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 46457cd..bab6469 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -70,7 +70,7 @@
// 2. Temporary strong references held by the kernel during a
// transaction on the node.
// It does NOT include local strong references to the node
- ssize_t getStrongRefCountForNodeByHandle(int32_t handle);
+ ssize_t getStrongRefCountForNode(const sp<BpBinder>& binder);
enum class CallRestriction {
// all calls okay
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index c44a24b..53871f2 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -27,19 +27,12 @@
#pragma once
#include <android/binder_ibinder.h>
+#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <android/binder_status.h>
#include <assert.h>
-// defined differently by liblog
-#pragma push_macro("LOG_PRI")
-#ifdef LOG_PRI
-#undef LOG_PRI
-#endif
-#include <syslog.h>
-#pragma pop_macro("LOG_PRI")
-
#include <unistd.h>
#include <cstddef>
#include <string>
diff --git a/libs/binder/ndk/include_cpp/android/binder_internal_logging.h b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
new file mode 100644
index 0000000..88c6443
--- /dev/null
+++ b/libs/binder/ndk/include_cpp/android/binder_internal_logging.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_internal_logging.h
+ * @brief This provides the ability to use syslog from binder headers, since
+ * other logging functionality might be inaccessable.
+ */
+
+#pragma once
+
+// defined differently by liblog
+#pragma push_macro("LOG_PRI")
+#ifdef LOG_PRI
+#undef LOG_PRI
+#endif
+#include <syslog.h>
+#pragma pop_macro("LOG_PRI")
+
+/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 054aebe..83190aa 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,6 +27,7 @@
#pragma once
#include <android/binder_auto_utils.h>
+#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
#include <optional>
@@ -179,6 +180,7 @@
static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel,
const SpAIBinder& binder) {
if (binder.get() == nullptr) {
+ syslog(LOG_ERR, "Passing null binder object as non-@nullable AIDL IBinder");
return STATUS_UNEXPECTED_NULL;
}
return AParcel_writeStrongBinder(parcel, binder.get());
@@ -228,6 +230,7 @@
static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor(
AParcel* parcel, const ScopedFileDescriptor& fd) {
if (fd.get() < 0) {
+ syslog(LOG_ERR, "Passing -1 file descriptor as non-@nullable AIDL ParcelFileDescriptor");
return STATUS_UNEXPECTED_NULL;
}
return AParcel_writeParcelFileDescriptor(parcel, fd.get());
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
index c685b41..6ca0e2f 100644
--- a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -48,9 +48,7 @@
static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
const sp<IBinder::DeathRecipient>&)>>
gBPBinderOperations =
- {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
- const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
- [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ {[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>& s_recipient) -> void {
// Clean up possible leftover memory.
wp<IBinder::DeathRecipient> outRecipient(nullptr);
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e17f319..3415d9d 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -326,7 +326,9 @@
t->setCrop(mSurfaceControl, computeCrop(bufferItem));
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
- t->setDesiredPresentTime(bufferItem.mTimestamp);
+ if (!bufferItem.mIsAutoTimestamp) {
+ t->setDesiredPresentTime(bufferItem.mTimestamp);
+ }
t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (!mNextFrameTimelineVsyncIdQueue.empty()) {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 405658b..a8d6832 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -71,7 +71,7 @@
virtual status_t setTransactionState(
int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
- const InputWindowCommands& commands, int64_t desiredPresentTime,
+ const InputWindowCommands& commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
Parcel data, reply;
@@ -92,6 +92,7 @@
SAFE_PARCEL(data.writeStrongBinder, applyToken);
SAFE_PARCEL(commands.write, data);
SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+ SAFE_PARCEL(data.writeBool, isAutoTimestamp);
SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
@@ -1297,7 +1298,9 @@
SAFE_PARCEL(inputWindowCommands.read, data);
int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
SAFE_PARCEL(data.readInt64, &desiredPresentTime);
+ SAFE_PARCEL(data.readBool, &isAutoTimestamp);
client_cache_t uncachedBuffer;
sp<IBinder> tmpBinder;
@@ -1323,8 +1326,8 @@
return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags,
applyToken, inputWindowCommands, desiredPresentTime,
- uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
- transactionId);
+ isAutoTimestamp, uncachedBuffer, hasListenerCallbacks,
+ listenerCallbacks, transactionId);
}
case BOOT_FINISHED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2dacae1..1808571 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -99,15 +99,13 @@
status_t JankData::writeToParcel(Parcel* output) const {
SAFE_PARCEL(output->writeInt64, frameVsyncId);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(jankType));
+ SAFE_PARCEL(output->writeInt32, jankType);
return NO_ERROR;
}
status_t JankData::readFromParcel(const Parcel* input) {
SAFE_PARCEL(input->readInt64, &frameVsyncId);
- int32_t jankTypeInt;
- SAFE_PARCEL(input->readInt32, &jankTypeInt);
- jankType = static_cast<JankType>(jankTypeInt);
+ SAFE_PARCEL(input->readInt32, &jankType);
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0d370d3..5570d99 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -395,6 +395,7 @@
mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
mContainsBuffer(other.mContainsBuffer),
mDesiredPresentTime(other.mDesiredPresentTime),
+ mIsAutoTimestamp(other.mIsAutoTimestamp),
mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) {
mDisplayStates = other.mDisplayStates;
mComposerStates = other.mComposerStates;
@@ -424,6 +425,7 @@
const bool explicitEarlyWakeupEnd = parcel->readBool();
const bool containsBuffer = parcel->readBool();
const int64_t desiredPresentTime = parcel->readInt64();
+ const bool isAutoTimestamp = parcel->readBool();
const int64_t frameTimelineVsyncId = parcel->readInt64();
size_t count = static_cast<size_t>(parcel->readUint32());
@@ -497,6 +499,7 @@
mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
mContainsBuffer = containsBuffer;
mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = isAutoTimestamp;
mFrameTimelineVsyncId = frameTimelineVsyncId;
mDisplayStates = displayStates;
mListenerCallbacks = listenerCallbacks;
@@ -527,6 +530,7 @@
parcel->writeBool(mExplicitEarlyWakeupEnd);
parcel->writeBool(mContainsBuffer);
parcel->writeInt64(mDesiredPresentTime);
+ parcel->writeBool(mIsAutoTimestamp);
parcel->writeInt64(mFrameTimelineVsyncId);
parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
for (auto const& displayState : mDisplayStates) {
@@ -628,7 +632,8 @@
mEarlyWakeup = false;
mExplicitEarlyWakeupStart = false;
mExplicitEarlyWakeupEnd = false;
- mDesiredPresentTime = -1;
+ mDesiredPresentTime = 0;
+ mIsAutoTimestamp = true;
mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
}
@@ -640,8 +645,9 @@
uncacheBuffer.id = cacheId;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1,
- uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+ sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {},
+ systemTime(), true, uncacheBuffer, false, {},
+ 0 /* Undefined transactionId */);
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -759,7 +765,7 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
- mInputWindowCommands, mDesiredPresentTime,
+ mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
hasListenerCallbacks, listenerCallbacks, mId);
mId = generateId();
@@ -1201,6 +1207,9 @@
}
s->what |= layer_state_t::eBufferChanged;
s->buffer = buffer;
+ if (mIsAutoTimestamp) {
+ mDesiredPresentTime = systemTime();
+ }
registerSurfaceControlForCallback(sc);
@@ -1295,6 +1304,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
nsecs_t desiredPresentTime) {
mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = false;
return *this;
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 7d25d61..40316db 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -166,7 +166,7 @@
int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
/* signal that we're done booting.
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 26b3840..cb17cee 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -69,15 +69,14 @@
status_t readFromParcel(const Parcel* input) override;
JankData();
- JankData(int64_t frameVsyncId, JankType jankType)
- : frameVsyncId(frameVsyncId),
- jankType(jankType) {}
+ JankData(int64_t frameVsyncId, int32_t jankType)
+ : frameVsyncId(frameVsyncId), jankType(jankType) {}
// Identifier for the frame submitted with Transaction.setFrameTimelineVsyncId
int64_t frameVsyncId;
- // The type of jank occurred
- JankType jankType;
+ // Bitmask of janks that occurred
+ int32_t jankType;
};
class SurfaceStats : public Parcelable {
diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h
index 47daf95..fc91714 100644
--- a/libs/gui/include/gui/JankInfo.h
+++ b/libs/gui/include/gui/JankInfo.h
@@ -18,24 +18,31 @@
namespace android {
-// Jank information tracked by SurfaceFlinger for the purpose of funneling to telemetry.
+// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry.
+// TODO(b/175843808): Change JankType from enum to enum class
enum JankType {
// No Jank
None = 0x0,
- // Jank not related to SurfaceFlinger or the App
- Display = 0x1,
+ // Jank that occurs in the layers below SurfaceFlinger
+ DisplayHAL = 0x1,
// SF took too long on the CPU
- SurfaceFlingerDeadlineMissed = 0x2,
+ SurfaceFlingerCpuDeadlineMissed = 0x2,
// SF took too long on the GPU
SurfaceFlingerGpuDeadlineMissed = 0x4,
// Either App or GPU took too long on the frame
AppDeadlineMissed = 0x8,
- // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
- // jank
- // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
- PredictionExpired = 0x10,
- // Latching a buffer early might cause an early present of the frame
- SurfaceFlingerEarlyLatch = 0x20,
+ // Vsync predictions have drifted beyond the threshold from the actual HWVsync
+ PredictionError = 0x10,
+ // Janks caused due to the time SF was scheduled to work on the frame
+ // Example: SF woke up too early and latched a buffer resulting in an early present
+ SurfaceFlingerScheduling = 0x20,
+ // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
+ // presented later because the previous buffer was presented in its expected vsync. This
+ // usually happens if there is an unexpectedly long frame causing the rest of the buffers
+ // to enter a stuffed state.
+ BufferStuffing = 0x40,
+ // Jank due to unknown reasons.
+ Unknown = 0x80,
};
} // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 3ee4a39..0abe72c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -373,13 +373,17 @@
// to be presented. When it is not possible to present at exactly that time, it will be
// presented after the time has passed.
//
+ // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+ // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+ //
// 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;
+ int64_t mDesiredPresentTime = 0;
+ bool mIsAutoTimestamp = true;
// The vsync Id provided by Choreographer.getVsyncId
int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c75c46c..3965ea0 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -638,6 +638,9 @@
std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by
+ // the default obscured/untrusted touch filter introduced in S.
+ nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW;
nonTouchableSurface->showAt(100, 100);
injectTap(190, 199);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 7761db8..fa98cd4c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -700,7 +700,7 @@
const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
- int64_t /*desiredPresentTime*/,
+ int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
const client_cache_t& /*cachedBuffer*/,
bool /*hasListenerCallbacks*/,
const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index e2cf245..f8f2f4e 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -43,7 +43,7 @@
KEY_LAYOUT);
ASSERT_FALSE(path.empty());
base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
- ASSERT_TRUE(ret) << "Cannot load KeyLayout at " << path;
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << path;
mKeyMap.keyLayoutMap = std::move(*ret);
mKeyMap.keyLayoutFile = path;
}
@@ -58,7 +58,7 @@
ASSERT_FALSE(path.empty()) << "KeyCharacterMap for " << name << " not found";
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
- ASSERT_TRUE(ret) << "Cannot load KeyCharacterMap at " << path;
+ ASSERT_TRUE(ret.ok()) << "Cannot load KeyCharacterMap at " << path;
mKeyMap.keyCharacterMap = *ret;
mKeyMap.keyCharacterMapFile = path;
}
@@ -82,4 +82,4 @@
ASSERT_EQ(*map, *mKeyMap.keyCharacterMap);
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 279e648..c88e298 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -222,6 +222,29 @@
return err;
}
+std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
+ const RenderEngineCreationArgs& args) {
+ if (!GLExtensions::getInstance().hasContextPriority()) {
+ return std::nullopt;
+ }
+
+ switch (args.contextPriority) {
+ case RenderEngine::ContextPriority::REALTIME:
+ if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ return RenderEngine::ContextPriority::REALTIME;
+ } else {
+ ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+ return RenderEngine::ContextPriority::HIGH;
+ }
+ case RenderEngine::ContextPriority::HIGH:
+ case RenderEngine::ContextPriority::MEDIUM:
+ case RenderEngine::ContextPriority::LOW:
+ return args.contextPriority;
+ default:
+ return std::nullopt;
+ }
+}
+
std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
// initialize EGL for the default display
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -235,6 +258,7 @@
LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
}
+ // Use the Android impl to grab EGL_NV_context_priority_realtime
const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
if (!eglExtensions) {
checkGlError(__FUNCTION__, __LINE__);
@@ -251,17 +275,16 @@
config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
}
- bool useContextPriority =
- extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
+ const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
EGLContext protectedContext = EGL_NO_CONTEXT;
if (args.enableProtectedContext && extensions.hasProtectedContent()) {
- protectedContext = createEglContext(display, config, nullptr, useContextPriority,
- Protection::PROTECTED);
+ protectedContext =
+ createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
}
- EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
- Protection::UNPROTECTED);
+ EGLContext ctxt =
+ createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -311,7 +334,6 @@
ALOGI("extensions: %s", extensions.getExtensions());
ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
-
return engine;
}
@@ -802,6 +824,12 @@
ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
}
+int GLESRenderEngine::getContextPriority() {
+ int value;
+ eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+ return value;
+}
+
FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
// Translate win by the rounded corners rect coordinates, to have all values in
// layer coordinate space.
@@ -1617,7 +1645,8 @@
}
EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection) {
EGLint renderableType = 0;
if (config == EGL_NO_CONFIG) {
@@ -1640,9 +1669,23 @@
contextAttributes.reserve(7);
contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
contextAttributes.push_back(contextClientVersion);
- if (useContextPriority) {
+ if (contextPriority) {
contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ switch (*contextPriority) {
+ case ContextPriority::REALTIME:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+ break;
+ case ContextPriority::MEDIUM:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+ break;
+ case ContextPriority::LOW:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+ break;
+ case ContextPriority::HIGH:
+ default:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ break;
+ }
}
if (protection == Protection::PROTECTED) {
contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 92e1529..64d6c6b 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -71,6 +71,7 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
bool cleanupPostRender(CleanupMode mode) override;
+ int getContextPriority() override;
EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
// Creates an output image for rendering to
@@ -116,8 +117,11 @@
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static GlesVersion parseGlesVersion(const char* str);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection);
+ static std::optional<RenderEngine::ContextPriority> createContextPriority(
+ const RenderEngineCreationArgs& args);
static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
std::unique_ptr<Framebuffer> createFramebuffer();
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 2924b0e..3dd534e 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -120,6 +120,10 @@
if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
mHasSurfacelessContext = true;
}
+
+ if (extensionSet.hasExtension("EGL_NV_context_priority_realtime")) {
+ mHasRealtimePriority = true;
+ }
}
char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index ef00009..e415ff3 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -41,6 +41,7 @@
bool hasContextPriority() const { return mHasContextPriority; }
bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
bool hasProtectedTexture() const { return mHasProtectedTexture; }
+ bool hasRealtimePriority() const { return mHasRealtimePriority; }
void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
GLubyte const* extensions);
@@ -67,6 +68,7 @@
bool mHasContextPriority = false;
bool mHasSurfacelessContext = false;
bool mHasProtectedTexture = false;
+ bool mHasRealtimePriority = false;
String8 mVendor;
String8 mRenderer;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ef12fd2..9157066 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -75,6 +75,7 @@
LOW = 1,
MEDIUM = 2,
HIGH = 3,
+ REALTIME = 4,
};
enum class RenderEngineType {
@@ -181,6 +182,10 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
virtual void cleanFramebufferCache() = 0;
+ // Returns the priority this context was actually created with. Note: this may not be
+ // the same as specified at context creation time, due to implementation limits on the
+ // number of contexts that can be created at a specific priority level in the system.
+ virtual int getContextPriority() = 0;
protected:
friend class threaded::RenderEngineThreaded;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 95ee925..2c34da4 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -53,6 +53,7 @@
const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
base::unique_fd*));
MOCK_METHOD0(cleanFramebufferCache, void());
+ MOCK_METHOD0(getContextPriority, int());
};
} // namespace mock
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 1f98a46..3d6b7af 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -169,17 +169,16 @@
config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
}
- bool useContextPriority =
- extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
EGLContext protectedContext = EGL_NO_CONTEXT;
+ const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
if (args.enableProtectedContext && extensions.hasProtectedContent()) {
- protectedContext = createEglContext(display, config, nullptr, useContextPriority,
- Protection::PROTECTED);
+ protectedContext =
+ createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
}
- EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority,
- Protection::UNPROTECTED);
+ EGLContext ctxt =
+ createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
@@ -832,7 +831,8 @@
}
EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection) {
EGLint renderableType = 0;
if (config == EGL_NO_CONFIG_KHR) {
@@ -855,9 +855,23 @@
contextAttributes.reserve(7);
contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
contextAttributes.push_back(contextClientVersion);
- if (useContextPriority) {
+ if (contextPriority) {
contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ switch (*contextPriority) {
+ case ContextPriority::REALTIME:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
+ break;
+ case ContextPriority::MEDIUM:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
+ break;
+ case ContextPriority::LOW:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
+ break;
+ case ContextPriority::HIGH:
+ default:
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ break;
+ }
}
if (protection == Protection::PROTECTED) {
contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
@@ -882,6 +896,29 @@
return context;
}
+std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
+ const RenderEngineCreationArgs& args) {
+ if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+ return std::nullopt;
+ }
+
+ switch (args.contextPriority) {
+ case RenderEngine::ContextPriority::REALTIME:
+ if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ return RenderEngine::ContextPriority::REALTIME;
+ } else {
+ ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
+ return RenderEngine::ContextPriority::HIGH;
+ }
+ case RenderEngine::ContextPriority::HIGH:
+ case RenderEngine::ContextPriority::MEDIUM:
+ case RenderEngine::ContextPriority::LOW:
+ return args.contextPriority;
+ default:
+ return std::nullopt;
+ }
+}
+
EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display,
EGLConfig config, int hwcFormat,
Protection protection) {
@@ -906,6 +943,12 @@
void SkiaGLRenderEngine::cleanFramebufferCache() {}
+int SkiaGLRenderEngine::getContextPriority() {
+ int value;
+ eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
+ return value;
+}
+
} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index b53250e..3d8c693 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -56,6 +56,7 @@
const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
void cleanFramebufferCache() override;
+ int getContextPriority() override;
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
bool useProtectedContext(bool useProtectedContext) override;
@@ -68,8 +69,11 @@
private:
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext, bool useContextPriority,
+ EGLContext shareContext,
+ std::optional<ContextPriority> contextPriority,
Protection protection);
+ static std::optional<RenderEngine::ContextPriority> createContextPriority(
+ const RenderEngineCreationArgs& args);
static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
inline SkRect getSkRect(const FloatRect& layer);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 2352c7e..12b8586 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -56,6 +56,7 @@
return 0;
};
virtual bool cleanupPostRender(CleanupMode) override { return true; };
+ virtual int getContextPriority() override { return 0; }
};
} // namespace skia
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 5453302..08f2949 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -304,6 +304,21 @@
resultFuture.wait();
}
+int RenderEngineThreaded::getContextPriority() {
+ std::promise<int> resultPromise;
+ std::future<int> resultFuture = resultPromise.get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::getContextPriority");
+ int priority = instance.getContextPriority();
+ resultPromise.set_value(priority);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture.get();
+}
+
} // namespace threaded
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index cdfbd04..8b1e2de 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -63,6 +63,7 @@
base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
void cleanFramebufferCache() override;
+ int getContextPriority() override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 393ced7..48abdce 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -52,36 +52,17 @@
license: "include/KHR/NOTICE",
}
-llndk_library {
- name: "libEGL.llndk",
- symbol_file: "libs/libEGL.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv1_CM.llndk",
- symbol_file: "libs/libGLESv1_CM.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv2.llndk",
- symbol_file: "libs/libGLESv2.map.txt",
- export_include_dirs: ["include"],
-}
-
-llndk_library {
- name: "libGLESv3.llndk",
- symbol_file: "libs/libGLESv3.map.txt",
- export_include_dirs: ["include"],
-}
-
cc_library_headers {
name: "gl_headers",
vendor_available: true,
export_include_dirs: ["include"],
}
+llndk_headers {
+ name: "gl_llndk_headers",
+ export_include_dirs: ["include"],
+}
+
subdirs = [
"*",
]
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 77d887c..5d17561 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -225,3 +225,27 @@
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
+
+llndk_library {
+ name: "libEGL.llndk",
+ symbol_file: "libEGL.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv1_CM.llndk",
+ symbol_file: "libGLESv1_CM.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv2.llndk",
+ symbol_file: "libGLESv2.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
+
+llndk_library {
+ name: "libGLESv3.llndk",
+ symbol_file: "libGLESv3.map.txt",
+ export_llndk_headers: ["gl_llndk_headers"],
+}
diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp
index 0d17265..6235f06 100644
--- a/services/audiomanager/IAudioManager.cpp
+++ b/services/audiomanager/IAudioManager.cpp
@@ -84,13 +84,11 @@
return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY);
}
- virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event,
- audio_port_handle_t deviceId) {
+ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) {
Parcel data, reply;
data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor());
data.writeInt32((int32_t) piid);
data.writeInt32((int32_t) event);
- data.writeInt32((int32_t) deviceId);
return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY);
}
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index b35a611..9606daa 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -41,6 +41,7 @@
],
static_libs: [
"libgmock",
+ "libperfetto_client_experimental",
"perfetto_trace_protos",
],
require_root: true,
diff --git a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
index 7fa75e1..cd8e19c 100644
--- a/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuMemTracerTest.cpp
@@ -75,6 +75,22 @@
int getTracerThreadCount() { return mGpuMemTracer->tracerThreadCount; }
+ std::vector<perfetto::protos::TracePacket> readGpuMemTotalPacketsBlocking(
+ perfetto::TracingSession* tracingSession) {
+ std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+ perfetto::protos::Trace trace;
+ trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
+
+ std::vector<perfetto::protos::TracePacket> packets;
+ for (const auto& packet : trace.packet()) {
+ if (!packet.has_gpu_mem_total_event()) {
+ continue;
+ }
+ packets.emplace_back(packet);
+ }
+ return packets;
+ }
+
std::shared_ptr<GpuMem> mGpuMem;
TestableGpuMem mTestableGpuMem;
std::unique_ptr<GpuMemTracer> mGpuMemTracer;
@@ -125,7 +141,7 @@
// The test tracer thread should have finished its execution by now.
EXPECT_EQ(getTracerThreadCount(), 0);
- auto packets = mGpuMemTracer->readGpuMemTotalPacketsForTestBlocking(tracingSession.get());
+ auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
EXPECT_EQ(packets.size(), 3);
const auto& packet0 = packets[0];
@@ -176,7 +192,7 @@
// The test tracer thread should have finished its execution by now.
EXPECT_EQ(getTracerThreadCount(), 0);
- auto packets = mGpuMemTracer->readGpuMemTotalPacketsForTestBlocking(tracingSession.get());
+ auto packets = readGpuMemTotalPacketsBlocking(tracingSession.get());
EXPECT_EQ(packets.size(), 0);
}
} // namespace android
diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp
index 9f951d3..919fed3 100644
--- a/services/gpuservice/tracing/Android.bp
+++ b/services/gpuservice/tracing/Android.bp
@@ -21,13 +21,10 @@
"libgpumem",
"libbase",
"liblog",
- "libprotobuf-cpp-lite",
- "libprotoutil",
"libutils",
],
static_libs: [
"libperfetto_client_experimental",
- "perfetto_trace_protos",
],
export_include_dirs: ["include"],
export_static_lib_headers: [
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 584498e..6975151 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -22,7 +22,6 @@
#include <gpumem/GpuMem.h>
#include <perfetto/trace/android/gpu_mem_event.pbzero.h>
-#include <perfetto/trace/trace.pb.h>
#include <unistd.h>
#include <thread>
@@ -63,22 +62,6 @@
tracerThreadCount++;
}
-std::vector<perfetto::protos::TracePacket> GpuMemTracer::readGpuMemTotalPacketsForTestBlocking(
- perfetto::TracingSession* tracingSession) {
- std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
- perfetto::protos::Trace trace;
- trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()));
-
- std::vector<perfetto::protos::TracePacket> packets;
- for (const auto& packet : trace.packet()) {
- if (!packet.has_gpu_mem_total_event()) {
- continue;
- }
- packets.emplace_back(packet);
- }
- return packets;
-}
-
// Each tracing session can be used for a single block of Start -> Stop.
std::unique_ptr<perfetto::TracingSession> GpuMemTracer::getTracingSessionForTest() {
perfetto::TraceConfig cfg;
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 3d99589..a50e5c7 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -148,7 +148,7 @@
}
base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name);
- if (!channel) {
+ if (!channel.ok()) {
return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()),
channel.error().message().c_str());
}
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index fa75ffa..6561707 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -389,6 +389,20 @@
mBufferInfo.mFrameLatencyNeeded = true;
}
+bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const {
+ // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the
+ // vsync period. We can do this change as soon as ag/13100772 is merged.
+ constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms;
+
+ const auto presentTime = nextPredictedPresentTime();
+ if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) {
+ return false;
+ }
+
+ return presentTime >= expectedPresentTime &&
+ presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count();
+}
+
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
ATRACE_CALL();
@@ -421,6 +435,12 @@
return false;
}
+ if (frameIsEarly(expectedPresentTime)) {
+ ATRACE_NAME("frameIsEarly()");
+ mFlinger->signalLayerUpdate();
+ return false;
+ }
+
// If the head buffer's acquire fence hasn't signaled yet, return and
// try again later
if (!fenceHasSignaled()) {
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 63dfe5f..5cd9a7c 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -224,6 +224,18 @@
std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+
+ // Returns true if the next frame is considered too early to present
+ // at the given expectedPresentTime
+ bool frameIsEarly(nsecs_t expectedPresentTime) const;
+
+ // Returns the predicted present time of the next frame if available or
+ // 0 otherwise.
+ virtual nsecs_t nextPredictedPresentTime() const = 0;
+
+ // The amount of time SF can delay a frame if it is considered early based
+ // on the VsyncModulator::VsyncConfig::appWorkDuration
+ static constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 325ecfe..e8e31db 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -222,6 +222,21 @@
return mQueuedFrames > 0;
}
+nsecs_t BufferQueueLayer::nextPredictedPresentTime() const {
+ Mutex::Autolock lock(mQueueItemLock);
+ if (mQueueItems.empty()) {
+ return 0;
+ }
+
+ const auto& bufferData = mQueueItems[0];
+
+ if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) {
+ return 0;
+ }
+
+ return bufferData.surfaceFrame->getPredictions().presentTime;
+}
+
status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
nsecs_t expectedPresentTime) {
// This boolean is used to make sure that SurfaceFlinger's shadow copy
@@ -274,8 +289,8 @@
mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
if (mQueueItems[0].surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Dropped);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
}
mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
@@ -290,8 +305,8 @@
Mutex::Autolock lock(mQueueItemLock);
for (auto& [item, surfaceFrame] : mQueueItems) {
if (surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
- PresentState::Dropped);
+ surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame);
}
}
mQueueItems.clear();
@@ -321,8 +336,8 @@
mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
if (mQueueItems[0].surfaceFrame) {
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Dropped);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Dropped);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
}
mQueueItems.erase(mQueueItems.begin());
mQueuedFrames--;
@@ -336,8 +351,9 @@
if (mQueueItems[0].surfaceFrame) {
mQueueItems[0].surfaceFrame->setAcquireFenceTime(
mQueueItems[0].item.mFenceTime->getSignalTime());
- mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame),
- PresentState::Presented);
+ mQueueItems[0].surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mQueueItems[0].surfaceFrame);
+ mLastLatchTime = latchTime;
}
mQueueItems.erase(mQueueItems.begin());
}
@@ -436,8 +452,9 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
- mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+ mOwnerPid, mOwnerUid, mName,
+ mName);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems.push_back({item, surfaceFrame});
@@ -475,8 +492,9 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
- mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mFrameTimelineVsyncId,
+ mOwnerPid, mOwnerUid, mName,
+ mName);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems[mQueueItems.size() - 1].item = item;
mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 5a6b9bc..2347d8c 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -116,6 +116,8 @@
// Temporary - Used only for LEGACY camera mode.
uint32_t getProducerStickyTransform() const;
+ nsecs_t nextPredictedPresentTime() const override;
+
sp<BufferLayerConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
@@ -146,6 +148,11 @@
// a buffer to correlate the buffer with the vsync id. Can only be accessed
// with the SF state lock held.
std::optional<int64_t> mFrameTimelineVsyncId;
+
+ // Keeps track of the time SF latched the last buffer from this layer.
+ // Used in buffer stuffing analysis in FrameTimeline.
+ // TODO(b/176106798): Find a way to do this for BLASTBufferQueue as well.
+ nsecs_t mLastLatchTime = 0;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 0b9caba..7ec7f36 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -341,7 +341,7 @@
}
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
- nsecs_t postTime, nsecs_t desiredPresentTime,
+ nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& clientCacheId, uint64_t frameNumber) {
ATRACE_CALL();
@@ -365,13 +365,13 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
mOwnerUid, postTime);
- desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
+ mCurrentState.isAutoTimestamp = isAutoTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+ mFlinger->mScheduler->recordLayerHistory(this, isAutoTimestamp ? 0 : desiredPresentTime,
LayerHistory::LayerUpdateType::Buffer);
- addFrameEvent(acquireFence, postTime, desiredPresentTime);
+ addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
return true;
}
@@ -533,7 +533,7 @@
return true;
}
- return mCurrentState.desiredPresentTime <= expectedPresentTime;
+ return mCurrentState.isAutoTimestamp || mCurrentState.desiredPresentTime <= expectedPresentTime;
}
bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -601,6 +601,14 @@
return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
}
+nsecs_t BufferStateLayer::nextPredictedPresentTime() const {
+ if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) {
+ return 0;
+ }
+
+ return mSurfaceFrame->getPredictions().presentTime;
+}
+
status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
nsecs_t /*expectedPresentTime*/) {
const State& s(getDrawingState());
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index ad00c65..6414e6b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -70,8 +70,8 @@
bool setCrop(const Rect& crop) override;
bool setFrame(const Rect& frame) override;
bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
- nsecs_t desiredPresentTime, const client_cache_t& clientCacheId,
- uint64_t frameNumber) override;
+ nsecs_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& clientCacheId, uint64_t frameNumber) override;
bool setAcquireFence(const sp<Fence>& fence) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -152,6 +152,8 @@
bool bufferNeedsFiltering() const override;
+ nsecs_t nextPredictedPresentTime() const override;
+
static const std::array<float, 16> IDENTITY_MATRIX;
std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index b09f07a..a8fef04 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -26,7 +26,7 @@
#include <cinttypes>
#include <numeric>
-namespace android::frametimeline::impl {
+namespace android::frametimeline {
using base::StringAppendF;
using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
@@ -60,7 +60,8 @@
StringAppendF(&result, "\t%10.2f\t|",
std::chrono::duration<double, std::milli>(startTime).count());
}
- if (actuals.endTime == 0) {
+ if (actuals.endTime <= 0) {
+ // Animation leashes can send the endTime as -1
StringAppendF(&result, "\t\tN/A\t|");
} else {
std::chrono::nanoseconds endTime(actuals.endTime - baseTime);
@@ -89,124 +90,160 @@
case PredictionState::Expired:
return "Expired";
case PredictionState::None:
- default:
return "None";
}
}
-std::string toString(JankType jankType) {
- switch (jankType) {
- case JankType::None:
- return "None";
- case JankType::Display:
- return "Composer/Display - outside SF and App";
- case JankType::SurfaceFlingerDeadlineMissed:
- return "SurfaceFlinger Deadline Missed";
- case JankType::AppDeadlineMissed:
- return "App Deadline Missed";
- case JankType::PredictionExpired:
- return "Prediction Expired";
- case JankType::SurfaceFlingerEarlyLatch:
- return "SurfaceFlinger Early Latch";
- default:
- return "Unclassified";
- }
-}
-
-std::string jankMetadataBitmaskToString(int32_t jankMetadata) {
- std::vector<std::string> jankInfo;
-
- if (jankMetadata & EarlyStart) {
- jankInfo.emplace_back("Early Start");
- } else if (jankMetadata & LateStart) {
- jankInfo.emplace_back("Late Start");
- }
-
- if (jankMetadata & EarlyFinish) {
- jankInfo.emplace_back("Early Finish");
- } else if (jankMetadata & LateFinish) {
- jankInfo.emplace_back("Late Finish");
- }
-
- if (jankMetadata & EarlyPresent) {
- jankInfo.emplace_back("Early Present");
- } else if (jankMetadata & LatePresent) {
- jankInfo.emplace_back("Late Present");
- }
- // TODO(b/169876734): add GPU composition metadata here
-
- if (jankInfo.empty()) {
+std::string jankTypeBitmaskToString(int32_t jankType) {
+ // TODO(b/175843808): Make this a switch case if jankType becomes an enum class
+ std::vector<std::string> janks;
+ if (jankType == JankType::None) {
return "None";
}
- return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(),
+ if (jankType & JankType::DisplayHAL) {
+ janks.emplace_back("Display HAL");
+ }
+ if (jankType & JankType::SurfaceFlingerCpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger CPU Deadline Missed");
+ }
+ if (jankType & JankType::SurfaceFlingerGpuDeadlineMissed) {
+ janks.emplace_back("SurfaceFlinger GPU Deadline Missed");
+ }
+ if (jankType & JankType::AppDeadlineMissed) {
+ janks.emplace_back("App Deadline Missed");
+ }
+ if (jankType & JankType::PredictionError) {
+ janks.emplace_back("Prediction Error");
+ }
+ if (jankType & JankType::SurfaceFlingerScheduling) {
+ janks.emplace_back("SurfaceFlinger Scheduling");
+ }
+ if (jankType & JankType::BufferStuffing) {
+ janks.emplace_back("Buffer Stuffing");
+ }
+ if (jankType & JankType::Unknown) {
+ janks.emplace_back("Unknown jank");
+ }
+ return std::accumulate(janks.begin(), janks.end(), std::string(),
[](const std::string& l, const std::string& r) {
return l.empty() ? r : l + ", " + r;
});
}
-FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
- if (jankMetadata & EarlyPresent) {
- return FrameTimelineEvent::PRESENT_EARLY;
+std::string toString(FramePresentMetadata presentMetadata) {
+ switch (presentMetadata) {
+ case FramePresentMetadata::OnTimePresent:
+ return "On Time Present";
+ case FramePresentMetadata::LatePresent:
+ return "Late Present";
+ case FramePresentMetadata::EarlyPresent:
+ return "Early Present";
+ case FramePresentMetadata::UnknownPresent:
+ return "Unknown Present";
}
- if (jankMetadata & LatePresent) {
- return FrameTimelineEvent::PRESENT_LATE;
- }
- return FrameTimelineEvent::PRESENT_ON_TIME;
}
-FrameTimelineEvent::JankType JankTypeToProto(JankType jankType) {
+std::string toString(FrameReadyMetadata finishMetadata) {
+ switch (finishMetadata) {
+ case FrameReadyMetadata::OnTimeFinish:
+ return "On Time Finish";
+ case FrameReadyMetadata::LateFinish:
+ return "Late Finish";
+ case FrameReadyMetadata::UnknownFinish:
+ return "Unknown Finish";
+ }
+}
+
+std::string toString(FrameStartMetadata startMetadata) {
+ switch (startMetadata) {
+ case FrameStartMetadata::OnTimeStart:
+ return "On Time Start";
+ case FrameStartMetadata::LateStart:
+ return "Late Start";
+ case FrameStartMetadata::EarlyStart:
+ return "Early Start";
+ case FrameStartMetadata::UnknownStart:
+ return "Unknown Start";
+ }
+}
+
+std::string toString(SurfaceFrame::PresentState presentState) {
+ using PresentState = SurfaceFrame::PresentState;
+ switch (presentState) {
+ case PresentState::Presented:
+ return "Presented";
+ case PresentState::Dropped:
+ return "Dropped";
+ case PresentState::Unknown:
+ return "Unknown";
+ }
+}
+
+FrameTimelineEvent::PresentType toProto(FramePresentMetadata presentMetadata) {
+ switch (presentMetadata) {
+ case FramePresentMetadata::EarlyPresent:
+ return FrameTimelineEvent::PRESENT_EARLY;
+ case FramePresentMetadata::LatePresent:
+ return FrameTimelineEvent::PRESENT_LATE;
+ case FramePresentMetadata::OnTimePresent:
+ return FrameTimelineEvent::PRESENT_ON_TIME;
+ case FramePresentMetadata::UnknownPresent:
+ return FrameTimelineEvent::PRESENT_UNSPECIFIED;
+ }
+}
+
+FrameTimelineEvent::JankType jankTypeBitmaskToProto(int32_t jankType) {
+ // TODO(b/175843808): Either make the proto a bitmask or jankType an enum class
switch (jankType) {
case JankType::None:
return FrameTimelineEvent::JANK_NONE;
- case JankType::Display:
+ case JankType::DisplayHAL:
return FrameTimelineEvent::JANK_DISPLAY_HAL;
- case JankType::SurfaceFlingerDeadlineMissed:
+ case JankType::SurfaceFlingerCpuDeadlineMissed:
+ case JankType::SurfaceFlingerGpuDeadlineMissed:
return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
case JankType::AppDeadlineMissed:
- case JankType::PredictionExpired:
+ case JankType::PredictionError:
return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+ case JankType::SurfaceFlingerScheduling:
+ return FrameTimelineEvent::JANK_SF_SCHEDULING;
+ case JankType::BufferStuffing:
+ return FrameTimelineEvent::JANK_BUFFER_STUFFING;
default:
+ // TODO(b/175843808): Remove default if JankType becomes an enum class
return FrameTimelineEvent::JANK_UNKNOWN;
}
}
-int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
- ATRACE_CALL();
- std::lock_guard<std::mutex> lock(mMutex);
- const int64_t assignedToken = mCurrentToken++;
- mPredictions[assignedToken] = predictions;
- mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
- flushTokens(systemTime());
- return assignedToken;
-}
-
-std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
- std::lock_guard<std::mutex> lock(mMutex);
- flushTokens(systemTime());
- auto predictionsIterator = mPredictions.find(token);
- if (predictionsIterator != mPredictions.end()) {
- return predictionsIterator->second;
+// Returns the smallest timestamp from the set of predictions and actuals.
+nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions,
+ TimelineItem actuals) {
+ nsecs_t minTime = std::numeric_limits<nsecs_t>::max();
+ if (predictionState == PredictionState::Valid) {
+ // Checking start time for predictions is enough because start time is always lesser than
+ // endTime and presentTime.
+ minTime = std::min(minTime, predictions.startTime);
}
- return {};
-}
-void TokenManager::flushTokens(nsecs_t flushTime) {
- for (size_t i = 0; i < mTokens.size(); i++) {
- if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
- mPredictions.erase(mTokens[i].first);
- mTokens.erase(mTokens.begin() + static_cast<int>(i));
- --i;
- } else {
- // Tokens are ordered by time. If i'th token is within the retention time, then the
- // i+1'th token will also be within retention time.
- break;
- }
+ // Need to check startTime, endTime and presentTime for actuals because some frames might not
+ // have them set.
+ if (actuals.startTime != 0) {
+ minTime = std::min(minTime, actuals.startTime);
}
+ if (actuals.endTime != 0) {
+ minTime = std::min(minTime, actuals.endTime);
+ }
+ if (actuals.presentTime != 0) {
+ minTime = std::min(minTime, actuals.endTime);
+ }
+ return minTime;
}
SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
std::string debugName, PredictionState predictionState,
- frametimeline::TimelineItem&& predictions)
+ frametimeline::TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds)
: mToken(token),
mOwnerPid(ownerPid),
mOwnerUid(ownerUid),
@@ -216,29 +253,8 @@
mPredictionState(predictionState),
mPredictions(predictions),
mActuals({0, 0, 0}),
- mActualQueueTime(0),
- mJankType(JankType::None),
- mJankMetadata(0) {}
-
-void SurfaceFrame::setPresentState(PresentState state) {
- std::lock_guard<std::mutex> lock(mMutex);
- mPresentState = state;
-}
-
-SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mPresentState;
-}
-
-TimelineItem SurfaceFrame::getActuals() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mActuals;
-}
-
-nsecs_t SurfaceFrame::getActualQueueTime() const {
- std::lock_guard<std::mutex> lock(mMutex);
- return mActualQueueTime;
-}
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -254,18 +270,13 @@
mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime);
}
-void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) {
+void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) {
std::lock_guard<std::mutex> lock(mMutex);
- mActuals.presentTime = presentTime;
+ mPresentState = presentState;
+ mLastLatchTime = lastLatchTime;
}
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
- std::lock_guard<std::mutex> lock(mMutex);
- mJankType = jankType;
- mJankMetadata = jankMetadata;
-}
-
-std::optional<JankType> SurfaceFrame::getJankType() const {
+std::optional<int32_t> SurfaceFrame::getJankType() const {
std::lock_guard<std::mutex> lock(mMutex);
if (mActuals.presentTime == 0) {
return std::nullopt;
@@ -275,31 +286,30 @@
nsecs_t SurfaceFrame::getBaseTime() const {
std::lock_guard<std::mutex> lock(mMutex);
- nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
- if (mPredictionState == PredictionState::Valid) {
- baseTime = std::min(baseTime, mPredictions.startTime);
- }
- if (mActuals.startTime != 0) {
- baseTime = std::min(baseTime, mActuals.startTime);
- }
- baseTime = std::min(baseTime, mActuals.endTime);
- return baseTime;
+ return getMinTime(mPredictionState, mPredictions, mActuals);
}
-std::string presentStateToString(SurfaceFrame::PresentState presentState) {
- using PresentState = SurfaceFrame::PresentState;
- switch (presentState) {
- case PresentState::Presented:
- return "Presented";
- case PresentState::Dropped:
- return "Dropped";
- case PresentState::Unknown:
- default:
- return "Unknown";
- }
+TimelineItem SurfaceFrame::getActuals() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActuals;
}
-void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPresentState;
+}
+
+FramePresentMetadata SurfaceFrame::getFramePresentMetadata() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFramePresentMetadata;
+}
+
+FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mFrameReadyMetadata;
+}
+
+void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Layer - %s", mDebugName.c_str());
@@ -309,21 +319,130 @@
}
StringAppendF(&result, "\n");
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Jank Type : %s\n", toString(mJankType).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Jank Metadata: %s\n",
- jankMetadataBitmaskToString(mJankMetadata).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ std::chrono::nanoseconds latchTime(
+ std::max(static_cast<int64_t>(0), mLastLatchTime - baseTime));
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Last latch time: %10f\n",
+ std::chrono::duration<double, std::milli>(latchTime).count());
+ if (mPredictionState == PredictionState::Valid) {
+ nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+ std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+ StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Present delta: %10f\n",
+ std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+ }
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
-void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
- using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
+void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType,
+ nsecs_t vsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mPresentState != PresentState::Presented) {
+ // No need to update dropped buffers
+ return;
+ }
+
+ mActuals.presentTime = presentTime;
+ // Jank Analysis for SurfaceFrame
+ if (mPredictionState == PredictionState::None) {
+ // Cannot do jank classification on frames that don't have a token.
+ return;
+ }
+ if (mPredictionState == PredictionState::Expired) {
+ // We do not know what happened here to classify this correctly. This could
+ // potentially be AppDeadlineMissed but that's assuming no app will request frames
+ // 120ms apart.
+ mJankType = JankType::Unknown;
+ mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+ mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+ mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+ return;
+ }
+
+ const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
+ const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
+ const nsecs_t deltaToVsync = std::abs(presentDelta) % vsyncPeriod;
+
+ if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ }
+
+ if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+ : FramePresentMetadata::EarlyPresent;
+ } else {
+ mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+ }
+
+ if (mFramePresentMetadata == FramePresentMetadata::OnTimePresent) {
+ // Frames presented on time are not janky.
+ mJankType = JankType::None;
+ } else if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present early
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+ // Delta factor of vsync
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta not a factor of vsync
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present early
+ mJankType = JankType::Unknown;
+ }
+ } else {
+ if (mLastLatchTime != 0 && mPredictions.endTime <= mLastLatchTime) {
+ // Buffer Stuffing.
+ mJankType |= JankType::BufferStuffing;
+ }
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present late
+ if (displayFrameJankType != JankType::None) {
+ // Propagate displayFrame's jank if it exists
+ mJankType |= displayFrameJankType;
+ } else {
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) {
+ // Delta factor of vsync
+ mJankType |= JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta not a factor of vsync
+ mJankType |= JankType::PredictionError;
+ }
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present late
+ if (displayFrameJankType == JankType::None) {
+ // Display frame is not janky, so purely app's fault
+ mJankType |= JankType::AppDeadlineMissed;
+ } else {
+ // Propagate DisplayFrame's jankType if it is janky
+ mJankType |= displayFrameJankType;
+ }
+ }
+ }
+ mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType);
+}
+
+void SurfaceFrame::trace(int64_t displayFrameToken) {
+ using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
std::lock_guard<std::mutex> lock(mMutex);
if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
@@ -349,11 +468,12 @@
} else if (mPresentState == PresentState::Unknown) {
surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
} else {
- surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
+ surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata));
}
- surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
- surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
- surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));
+ surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ surfaceFrameEvent->set_gpu_composition(mGpuComposition);
+ surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
@@ -366,10 +486,45 @@
});
}
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
- : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
+namespace impl {
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ const int64_t assignedToken = mCurrentToken++;
+ mPredictions[assignedToken] = {systemTime(), predictions};
+ flushTokens(systemTime());
+ return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ auto predictionsIterator = mPredictions.find(token);
+ if (predictionsIterator != mPredictions.end()) {
+ return predictionsIterator->second.predictions;
+ }
+ return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+ for (auto it = mPredictions.begin(); it != mPredictions.end();) {
+ if (flushTime - it->second.timestamp >= kMaxRetentionTime) {
+ it = mPredictions.erase(it);
+ } else {
+ // Tokens are ordered by time. If i'th token is within the retention time, then the
+ // i+1'th token will also be within retention time.
+ break;
+ }
+ }
+}
+
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds)
+ : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)),
mMaxDisplayFrames(kDefaultMaxDisplayFrames),
- mTimeStats(std::move(timeStats)) {}
+ mTimeStats(std::move(timeStats)),
+ mSurfaceFlingerPid(surfaceFlingerPid),
+ mJankClassificationThresholds(thresholds) {}
void FrameTimeline::onBootFinished() {
perfetto::TracingInitArgs args;
@@ -384,67 +539,225 @@
FrameTimelineDataSource::Register(dsd);
}
-FrameTimeline::DisplayFrame::DisplayFrame()
- : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
- this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
-}
-
-std::shared_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) {
+std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ std::optional<int64_t> token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName) {
ATRACE_CALL();
if (!token) {
- return std::make_shared<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID,ownerPid,
- ownerUid, std::move(layerName),
- std::move(debugName), PredictionState::None,
- TimelineItem());
+ return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+ ownerUid, std::move(layerName), std::move(debugName),
+ PredictionState::None, TimelineItem(), mTimeStats,
+ mJankClassificationThresholds);
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
- std::move(layerName), std::move(debugName),
- PredictionState::Valid,
- std::move(*predictions));
+ return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Valid,
+ std::move(*predictions), mTimeStats,
+ mJankClassificationThresholds);
}
- return std::make_shared<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
- std::move(debugName), PredictionState::Expired,
- TimelineItem());
+ return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Expired,
+ TimelineItem(), mTimeStats,
+ mJankClassificationThresholds);
}
-void FrameTimeline::addSurfaceFrame(
- std::shared_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
- SurfaceFrame::PresentState state) {
- ATRACE_CALL();
- surfaceFrame->setPresentState(state);
- std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->surfaceFrames.push_back(
- std::static_pointer_cast<impl::SurfaceFrame>(surfaceFrame));
+FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
+ JankClassificationThresholds thresholds)
+ : mSurfaceFlingerPredictions(TimelineItem()),
+ mSurfaceFlingerActuals(TimelineItem()),
+ mTimeStats(timeStats),
+ mJankClassificationThresholds(thresholds) {
+ mSurfaceFrames.reserve(kNumSurfaceFramesInitial);
}
-void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
ATRACE_CALL();
- const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->token = token;
- if (!prediction) {
- mCurrentDisplayFrame->predictionState = PredictionState::Expired;
- } else {
- mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
- mCurrentDisplayFrame->predictionState = PredictionState::Valid;
- }
- mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+ mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame);
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, nsecs_t vsyncPeriod) {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->onSfWakeUp(token, vsyncPeriod,
+ mTokenManager.getPredictionsForToken(token), wakeUpTime);
}
void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
- mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+ mCurrentDisplayFrame->setActualEndTime(sfPresentTime);
mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
flushPendingPresentFences();
finalizeCurrentDisplayFrame();
}
+void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
+ mSurfaceFrames.push_back(surfaceFrame);
+}
+
+void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, nsecs_t vsyncPeriod,
+ std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime) {
+ mToken = token;
+ mVsyncPeriod = vsyncPeriod;
+ if (!predictions) {
+ mPredictionState = PredictionState::Expired;
+ } else {
+ mPredictionState = PredictionState::Valid;
+ mSurfaceFlingerPredictions = *predictions;
+ }
+ mSurfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::DisplayFrame::setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod) {
+ mToken = token;
+ mVsyncPeriod = vsyncPeriod;
+}
+
+void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState,
+ TimelineItem predictions) {
+ mPredictionState = predictionState;
+ mSurfaceFlingerPredictions = predictions;
+}
+
+void FrameTimeline::DisplayFrame::setActualStartTime(nsecs_t actualStartTime) {
+ mSurfaceFlingerActuals.startTime = actualStartTime;
+}
+
+void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) {
+ mSurfaceFlingerActuals.endTime = actualEndTime;
+}
+
+void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) {
+ mSurfaceFlingerActuals.presentTime = signalTime;
+ int32_t totalJankReasons = JankType::None;
+
+ // Delta between the expected present and the actual present
+ const nsecs_t presentDelta =
+ mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+ // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
+ // was a prediction error or not.
+ nsecs_t deltaToVsync = std::abs(presentDelta) % mVsyncPeriod;
+ if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
+ mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
+ : FramePresentMetadata::EarlyPresent;
+ } else {
+ mFramePresentMetadata = FramePresentMetadata::OnTimePresent;
+ }
+
+ if (mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime >
+ mJankClassificationThresholds.deadlineThreshold) {
+ mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
+ } else {
+ mFrameReadyMetadata = FrameReadyMetadata::OnTimeFinish;
+ }
+
+ if (std::abs(mSurfaceFlingerActuals.startTime - mSurfaceFlingerPredictions.startTime) >
+ mJankClassificationThresholds.startThreshold) {
+ mFrameStartMetadata =
+ mSurfaceFlingerActuals.startTime > mSurfaceFlingerPredictions.startTime
+ ? FrameStartMetadata::LateStart
+ : FrameStartMetadata::EarlyStart;
+ }
+
+ if (mFramePresentMetadata != FramePresentMetadata::OnTimePresent) {
+ // Do jank classification only if present is not on time
+ if (mFramePresentMetadata == FramePresentMetadata::EarlyPresent) {
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present early
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >=
+ (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+ // Delta is a factor of vsync if its within the presentTheshold on either side
+ // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+ // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Delta is not a factor of vsync,
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present early
+ mJankType = JankType::SurfaceFlingerScheduling;
+ } else {
+ // Finish time unknown
+ mJankType = JankType::Unknown;
+ }
+ } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) {
+ if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) {
+ // Finish on time, Present late
+ if (deltaToVsync < mJankClassificationThresholds.presentThreshold ||
+ deltaToVsync >=
+ (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) {
+ // Delta is a factor of vsync if its within the presentTheshold on either side
+ // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold
+ // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms.
+ mJankType = JankType::DisplayHAL;
+ } else {
+ // Delta is not a factor of vsync
+ mJankType = JankType::PredictionError;
+ }
+ } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
+ // Finish late, Present late
+ mJankType = JankType::SurfaceFlingerCpuDeadlineMissed;
+ } else {
+ // Finish time unknown
+ mJankType = JankType::Unknown;
+ }
+ } else {
+ // Present unknown
+ mJankType = JankType::Unknown;
+ }
+ }
+ totalJankReasons |= mJankType;
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->onPresent(signalTime, mJankType, mVsyncPeriod);
+ auto surfaceFrameJankType = surfaceFrame->getJankType();
+ if (surfaceFrameJankType != std::nullopt) {
+ totalJankReasons |= *surfaceFrameJankType;
+ }
+ }
+ mTimeStats->incrementJankyFrames(totalJankReasons);
+}
+
+void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const {
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace DisplayFrame with invalid token");
+ return;
+ }
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* displayFrameEvent = event->set_display_frame();
+
+ displayFrameEvent->set_token(mToken);
+ displayFrameEvent->set_present_type(toProto(mFramePresentMetadata));
+ displayFrameEvent->set_on_time_finish(mFrameReadyMetadata ==
+ FrameReadyMetadata::OnTimeFinish);
+ displayFrameEvent->set_gpu_composition(mGpuComposition);
+ displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType));
+
+ displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime);
+ displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime);
+
+ displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime);
+ displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime);
+
+ displayFrameEvent->set_pid(surfaceFlingerPid);
+ });
+
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->trace(mToken);
+ }
+}
+
void FrameTimeline::flushPendingPresentFences() {
for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
const auto& pendingPresentFence = mPendingPresentFences[i];
@@ -456,93 +769,9 @@
}
}
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
- int32_t totalJankReasons = JankType::None;
auto& displayFrame = pendingPresentFence.second;
- displayFrame->surfaceFlingerActuals.presentTime = signalTime;
-
- // Jank Analysis for DisplayFrame
- const auto& sfActuals = displayFrame->surfaceFlingerActuals;
- const auto& sfPredictions = displayFrame->surfaceFlingerPredictions;
- if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) {
- displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime
- ? LatePresent
- : EarlyPresent;
- }
- if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) {
- if (sfActuals.endTime > sfPredictions.endTime) {
- displayFrame->jankMetadata |= LateFinish;
- } else {
- displayFrame->jankMetadata |= EarlyFinish;
- }
-
- if ((displayFrame->jankMetadata & EarlyFinish) &&
- (displayFrame->jankMetadata & EarlyPresent)) {
- displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if ((displayFrame->jankMetadata & LateFinish) &&
- (displayFrame->jankMetadata & LatePresent)) {
- displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
- } else if (displayFrame->jankMetadata & EarlyPresent ||
- displayFrame->jankMetadata & LatePresent) {
- // Cases where SF finished early but frame was presented late and vice versa
- displayFrame->jankType = JankType::Display;
- }
- }
-
- if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
- displayFrame->jankMetadata |=
- sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
- }
-
- totalJankReasons |= displayFrame->jankType;
- traceDisplayFrame(*displayFrame);
-
- for (auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
- // Only presented SurfaceFrames need to be updated
- surfaceFrame->setActualPresentTime(signalTime);
-
- // Jank Analysis for SurfaceFrame
- const auto& predictionState = surfaceFrame->getPredictionState();
- if (predictionState == PredictionState::Expired) {
- // Jank analysis cannot be done on apps that don't use predictions
- surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
- } else if (predictionState == PredictionState::Valid) {
- const auto& actuals = surfaceFrame->getActuals();
- const auto& predictions = surfaceFrame->getPredictions();
- int32_t jankMetadata = 0;
- JankType jankType = JankType::None;
- if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
- jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
- : EarlyFinish;
- }
- if (std::abs(actuals.presentTime - predictions.presentTime) >
- kPresentThreshold) {
- jankMetadata |= actuals.presentTime > predictions.presentTime
- ? LatePresent
- : EarlyPresent;
- }
- if (jankMetadata & EarlyPresent) {
- jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if (jankMetadata & LatePresent) {
- if (jankMetadata & EarlyFinish) {
- // TODO(b/169890654): Classify this properly
- jankType = JankType::Display;
- } else {
- jankType = JankType::AppDeadlineMissed;
- }
- }
-
- totalJankReasons |= jankType;
- mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
- surfaceFrame->getName(),
- jankType | displayFrame->jankType);
- surfaceFrame->setJankInfo(jankType, jankMetadata);
- }
- }
- surfaceFrame->traceSurfaceFrame(displayFrame->token);
- }
-
- mTimeStats->incrementJankyFrames(totalJankReasons);
+ displayFrame->onPresent(signalTime);
+ displayFrame->trace(mSurfaceFlingerPid);
}
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -557,16 +786,14 @@
}
mDisplayFrames.push_back(mCurrentDisplayFrame);
mCurrentDisplayFrame.reset();
- mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+ mCurrentDisplayFrame =
+ std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds);
}
-nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) {
- nsecs_t baseTime = std::numeric_limits<nsecs_t>::max();
- if (displayFrame->predictionState == PredictionState::Valid) {
- baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime);
- }
- baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime);
- for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const {
+ nsecs_t baseTime =
+ getMinTime(mPredictionState, mSurfaceFlingerPredictions, mSurfaceFlingerActuals);
+ for (const auto& surfaceFrame : mSurfaceFrames) {
nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime();
if (surfaceFrameBaseTime != 0) {
baseTime = std::min(baseTime, surfaceFrameBaseTime);
@@ -575,60 +802,79 @@
return baseTime;
}
-void FrameTimeline::dumpDisplayFrame(std::string& result,
- const std::shared_ptr<DisplayFrame>& displayFrame,
- nsecs_t baseTime) {
- if (displayFrame->jankType != JankType::None) {
+void FrameTimeline::DisplayFrame::dumpJank(std::string& result, nsecs_t baseTime,
+ int displayFrameCount) const {
+ if (mJankType == JankType::None) {
+ // Check if any Surface Frame has been janky
+ bool isJanky = false;
+ for (const auto& surfaceFrame : mSurfaceFrames) {
+ if (surfaceFrame->getJankType() != JankType::None) {
+ isJanky = true;
+ break;
+ }
+ }
+ if (!isJanky) {
+ return;
+ }
+ }
+ StringAppendF(&result, "Display Frame %d", displayFrameCount);
+ dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dumpAll(std::string& result, nsecs_t baseTime) const {
+ dump(result, baseTime);
+}
+
+void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) const {
+ if (mJankType != JankType::None) {
// Easily identify a janky Display Frame in the dump
StringAppendF(&result, " [*] ");
}
StringAppendF(&result, "\n");
- StringAppendF(&result, "Prediction State : %s\n",
- toString(displayFrame->predictionState).c_str());
- StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str());
- StringAppendF(&result, "Jank Metadata: %s\n",
- jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str());
- dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals,
- "", displayFrame->predictionState, baseTime);
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str());
+ std::chrono::nanoseconds vsyncPeriod(mVsyncPeriod);
+ StringAppendF(&result, "Vsync Period: %10f\n",
+ std::chrono::duration<double, std::milli>(vsyncPeriod).count());
+ nsecs_t presentDelta =
+ mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime;
+ std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta));
+ StringAppendF(&result, "Present delta: %10f\n",
+ std::chrono::duration<double, std::milli>(presentDeltaNs).count());
+ std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mVsyncPeriod);
+ StringAppendF(&result, "Present delta %% refreshrate: %10f\n",
+ std::chrono::duration<double, std::milli>(deltaToVsync).count());
+ dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState,
+ baseTime);
StringAppendF(&result, "\n");
std::string indent = " "; // 4 spaces
- for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
+ for (const auto& surfaceFrame : mSurfaceFrames) {
surfaceFrame->dump(result, indent, baseTime);
}
StringAppendF(&result, "\n");
}
+
void FrameTimeline::dumpAll(std::string& result) {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
- dumpDisplayFrame(result, mDisplayFrames[i], baseTime);
+ mDisplayFrames[i]->dumpAll(result, baseTime);
}
}
void FrameTimeline::dumpJank(std::string& result) {
std::lock_guard<std::mutex> lock(mMutex);
- nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
+ nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : mDisplayFrames[0]->getBaseTime();
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
- const auto& displayFrame = mDisplayFrames[i];
- if (displayFrame->jankType == JankType::None) {
- // Check if any Surface Frame has been janky
- bool isJanky = false;
- for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getJankType() != JankType::None) {
- isJanky = true;
- break;
- }
- }
- if (!isJanky) {
- continue;
- }
- }
- StringAppendF(&result, "Display Frame %d", static_cast<int>(i));
- dumpDisplayFrame(result, displayFrame, baseTime);
+ mDisplayFrames[i]->dumpJank(result, baseTime, static_cast<int>(i));
}
}
+
void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) {
ATRACE_CALL();
std::unordered_map<std::string, bool> argsMap;
@@ -656,31 +902,5 @@
setMaxDisplayFrames(kDefaultMaxDisplayFrames);
}
-void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
- ALOGD("Cannot trace DisplayFrame with invalid token");
- return;
- }
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
- packet->set_timestamp(static_cast<uint64_t>(systemTime()));
-
- auto* event = packet->set_frame_timeline_event();
- auto* displayFrameEvent = event->set_display_frame();
-
- displayFrameEvent->set_token(displayFrame.token);
- displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
- displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
- displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
- displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));
-
- displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
- displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);
-
- displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
- displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
- });
-}
-
-} // namespace android::frametimeline::impl
+} // namespace impl
+} // namespace android::frametimeline
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 084935b..f5239aa 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -32,24 +32,44 @@
namespace android::frametimeline {
-enum JankMetadata {
- // Frame was presented earlier than expected
- EarlyPresent = 0x1,
- // Frame was presented later than expected
- LatePresent = 0x2,
- // App/SF started earlier than expected
- EarlyStart = 0x4,
- // App/SF started later than expected
- LateStart = 0x8,
- // App/SF finished work earlier than the deadline
- EarlyFinish = 0x10,
- // App/SF finished work later than the deadline
- LateFinish = 0x20,
- // SF was in GPU composition
- GpuComposition = 0x40,
+class FrameTimelineTest;
+
+using namespace std::chrono_literals;
+
+// Metadata indicating how the frame was presented w.r.t expected present time.
+enum class FramePresentMetadata : int8_t {
+ // Frame was presented on time
+ OnTimePresent,
+ // Frame was presented late
+ LatePresent,
+ // Frame was presented early
+ EarlyPresent,
+ // Unknown/initial state
+ UnknownPresent,
};
-class FrameTimelineTest;
+// Metadata comparing the frame's actual finish time to the expected deadline.
+enum class FrameReadyMetadata : int8_t {
+ // App/SF finished on time. Early finish is treated as on time since the goal of any component
+ // is to finish before the deadline.
+ OnTimeFinish,
+ // App/SF finished work later than expected
+ LateFinish,
+ // Unknown/initial state
+ UnknownFinish,
+};
+
+// Metadata comparing the frame's actual start time to the expected start time.
+enum class FrameStartMetadata : int8_t {
+ // App/SF started on time
+ OnTimeStart,
+ // App/SF started later than expected
+ LateStart,
+ // App/SF started earlier than expected
+ EarlyStart,
+ // Unknown/initial state
+ UnknownStart,
+};
/*
* Collection of timestamps that can be used for both predictions and actual times.
@@ -71,6 +91,19 @@
bool operator!=(const TimelineItem& other) const { return !(*this == other); }
};
+struct TokenManagerPrediction {
+ nsecs_t timestamp = 0;
+ TimelineItem predictions;
+};
+
+struct JankClassificationThresholds {
+ // The various thresholds for App and SF. If the actual timestamp falls within the threshold
+ // compared to prediction, we treat it as on time.
+ nsecs_t presentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t deadlineThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+ nsecs_t startThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
+};
+
/*
* TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It
* saves these predictions for a short period of time and returns the predictions for a given token,
@@ -83,6 +116,9 @@
// Generates a token for the given set of predictions. Stores the predictions for 120ms and
// destroys it later.
virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0;
+
+ // Returns the stored predictions for a given token, if the predictions haven't expired.
+ virtual std::optional<TimelineItem> getPredictionsForToken(int64_t token) const = 0;
};
enum class PredictionState {
@@ -91,10 +127,6 @@
None, // Predictions are either not present or didn't come from TokenManager
};
-/*
- * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame
- * from the app
- */
class SurfaceFrame {
public:
enum class PresentState {
@@ -103,29 +135,76 @@
Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet
};
- virtual ~SurfaceFrame() = default;
+ // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
+ // TokenManager), Thresholds and TimeStats pointer.
+ SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName, PredictionState predictionState, TimelineItem&& predictions,
+ std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+ ~SurfaceFrame() = default;
- virtual TimelineItem getPredictions() const = 0;
- virtual TimelineItem getActuals() const = 0;
- virtual nsecs_t getActualQueueTime() const = 0;
- virtual PresentState getPresentState() const = 0;
- virtual PredictionState getPredictionState() const = 0;
- virtual pid_t getOwnerPid() const = 0;
+ // Returns std::nullopt if the frame hasn't been classified yet.
+ // Used by both SF and FrameTimeline.
+ std::optional<int32_t> getJankType() const;
- virtual void setPresentState(PresentState state) = 0;
-
+ // Functions called by SF
+ int64_t getToken() const { return mToken; };
+ TimelineItem getPredictions() const { return mPredictions; };
// Actual timestamps of the app are set individually at different functions.
// Start time (if the app provides) and Queue time are accessible after queueing the frame,
// whereas Acquire Fence time is available only during latch.
- virtual void setActualStartTime(nsecs_t actualStartTime) = 0;
- virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0;
- virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0;
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualQueueTime(nsecs_t actualQueueTime);
+ void setAcquireFenceTime(nsecs_t acquireFenceTime);
+ void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
- // Retrieves jank classification, if it's already been classified.
- virtual std::optional<JankType> getJankType() const = 0;
+ // Functions called by FrameTimeline
+ // BaseTime is the smallest timestamp in this SurfaceFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t getBaseTime() const;
+ // Sets the actual present time, appropriate metadata and classifies the jank.
+ void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, nsecs_t vsyncPeriod);
+ // All the timestamps are dumped relative to the baseTime
+ void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+ // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
+ // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
+ // DisplayFrame at the trace processor side.
+ void trace(int64_t displayFrameToken);
- // Token identifying the frame.
- virtual int64_t getToken() const = 0;
+ // Getter functions used only by FrameTimelineTests
+ TimelineItem getActuals() const;
+ pid_t getOwnerPid() const { return mOwnerPid; };
+ PredictionState getPredictionState() const { return mPredictionState; };
+ PresentState getPresentState() const;
+ FrameReadyMetadata getFrameReadyMetadata() const;
+ FramePresentMetadata getFramePresentMetadata() const;
+
+private:
+ const int64_t mToken;
+ const pid_t mOwnerPid;
+ const uid_t mOwnerUid;
+ const std::string mLayerName;
+ const std::string mDebugName;
+ PresentState mPresentState GUARDED_BY(mMutex);
+ const PredictionState mPredictionState;
+ const TimelineItem mPredictions;
+ TimelineItem mActuals GUARDED_BY(mMutex);
+ std::shared_ptr<TimeStats> mTimeStats;
+ const JankClassificationThresholds mJankClassificationThresholds;
+ nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0;
+ mutable std::mutex mMutex;
+ // Bitmask for the type of jank
+ int32_t mJankType GUARDED_BY(mMutex) = JankType::None;
+ // Indicates if this frame was composited by the GPU or not
+ bool mGpuComposition GUARDED_BY(mMutex) = false;
+ // Enum for the type of present
+ FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) =
+ FramePresentMetadata::UnknownPresent;
+ // Enum for the type of finish
+ FrameReadyMetadata mFrameReadyMetadata GUARDED_BY(mMutex) = FrameReadyMetadata::UnknownFinish;
+ // Time when the previous buffer from the same layer was latched by SF. This is used in checking
+ // for BufferStuffing where the current buffer is expected to be ready but the previous buffer
+ // was latched instead.
+ nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0;
};
/*
@@ -142,20 +221,19 @@
virtual void onBootFinished() = 0;
// Create a new surface frame, set the predictions based on a token and return it to the caller.
- // Sets the PredictionState of SurfaceFrame.
// Debug name is the human-readable debugging string for dumpsys.
- virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) = 0;
+ virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+ pid_t ownerPid, uid_t ownerUid,
+ std::string layerName,
+ std::string debugName) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
- virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame,
- SurfaceFrame::PresentState state) = 0;
+ virtual void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) = 0;
// The first function called by SF for the current DisplayFrame. Fetches SF predictions based on
// the token and sets the actualSfWakeTime for the current DisplayFrame.
- virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0;
+ virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) = 0;
// Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence
// until it's signaled, and updates the present timestamps of all presented SurfaceFrames in
@@ -178,15 +256,13 @@
namespace impl {
-using namespace std::chrono_literals;
-
class TokenManager : public android::frametimeline::TokenManager {
public:
TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {}
~TokenManager() = default;
int64_t generateTokenForPredictions(TimelineItem&& predictions) override;
- std::optional<TimelineItem> getPredictionsForToken(int64_t token);
+ std::optional<TimelineItem> getPredictionsForToken(int64_t token) const override;
private:
// Friend class for testing
@@ -194,64 +270,13 @@
void flushTokens(nsecs_t flushTime) REQUIRES(mMutex);
- std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex);
- std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex);
+ std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex);
int64_t mCurrentToken GUARDED_BY(mMutex);
- std::mutex mMutex;
+ mutable std::mutex mMutex;
static constexpr nsecs_t kMaxRetentionTime =
std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count();
};
-class SurfaceFrame : public android::frametimeline::SurfaceFrame {
-public:
- SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
- std::string debugName, PredictionState predictionState,
- TimelineItem&& predictions);
- ~SurfaceFrame() = default;
-
- TimelineItem getPredictions() const override { return mPredictions; };
- TimelineItem getActuals() const override;
- nsecs_t getActualQueueTime() const override;
- PresentState getPresentState() const override;
- PredictionState getPredictionState() const override { return mPredictionState; };
- pid_t getOwnerPid() const override { return mOwnerPid; };
- std::optional<JankType> getJankType() const override;
- int64_t getToken() const override { return mToken; };
- nsecs_t getBaseTime() const;
- uid_t getOwnerUid() const { return mOwnerUid; };
- const std::string& getName() const { return mLayerName; };
-
- void setActualStartTime(nsecs_t actualStartTime) override;
- void setActualQueueTime(nsecs_t actualQueueTime) override;
- void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
- void setPresentState(PresentState state) override;
- void setActualPresentTime(nsecs_t presentTime);
- void setJankInfo(JankType jankType, int32_t jankMetadata);
-
- // All the timestamps are dumped relative to the baseTime
- void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
-
- // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
- // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
- // DisplayFrame at the trace processor side.
- void traceSurfaceFrame(int64_t displayFrameToken);
-
-private:
- const int64_t mToken;
- const pid_t mOwnerPid;
- const uid_t mOwnerUid;
- const std::string mLayerName;
- const std::string mDebugName;
- PresentState mPresentState GUARDED_BY(mMutex);
- const PredictionState mPredictionState;
- const TimelineItem mPredictions;
- TimelineItem mActuals GUARDED_BY(mMutex);
- nsecs_t mActualQueueTime GUARDED_BY(mMutex);
- mutable std::mutex mMutex;
- JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
- int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
-};
-
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
@@ -260,16 +285,94 @@
void OnStop(const StopArgs&) override{};
};
- FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+ /*
+ * DisplayFrame should be used only internally within FrameTimeline. All members and methods are
+ * guarded by FrameTimeline's mMutex.
+ */
+ class DisplayFrame {
+ public:
+ DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds);
+ virtual ~DisplayFrame() = default;
+ // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one
+ // SurfaceFrame is janky.
+ void dumpJank(std::string& result, nsecs_t baseTime, int displayFrameCount) const;
+ // Dumpsys interface - dumps all data irrespective of jank
+ void dumpAll(std::string& result, nsecs_t baseTime) const;
+ // Emits a packet for perfetto tracing. The function body will be executed only if tracing
+ // is enabled.
+ void trace(pid_t surfaceFlingerPid) const;
+ // Sets the token, vsyncPeriod, predictions and SF start time.
+ void onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, std::optional<TimelineItem> predictions,
+ nsecs_t wakeUpTime);
+ // Sets the appropriate metadata, classifies the jank and returns the classified jankType.
+ void onPresent(nsecs_t signalTime);
+ // Adds the provided SurfaceFrame to the current display frame.
+ void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
+
+ void setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod);
+ void setPredictions(PredictionState predictionState, TimelineItem predictions);
+ void setActualStartTime(nsecs_t actualStartTime);
+ void setActualEndTime(nsecs_t actualEndTime);
+
+ // BaseTime is the smallest timestamp in a DisplayFrame.
+ // Used for dumping all timestamps relative to the oldest, making it easy to read.
+ nsecs_t getBaseTime() const;
+
+ // Functions to be used only in testing.
+ TimelineItem getActuals() const { return mSurfaceFlingerActuals; };
+ TimelineItem getPredictions() const { return mSurfaceFlingerPredictions; };
+ FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; };
+ FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; };
+ int32_t getJankType() const { return mJankType; }
+ const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const {
+ return mSurfaceFrames;
+ }
+
+ private:
+ void dump(std::string& result, nsecs_t baseTime) const;
+
+ int64_t mToken = ISurfaceComposer::INVALID_VSYNC_ID;
+
+ /* Usage of TimelineItem w.r.t SurfaceFlinger
+ * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates
+ * endTime Time when SurfaceFlinger sends a composited frame to Display
+ * presentTime Time when the composited frame was presented on screen
+ */
+ TimelineItem mSurfaceFlingerPredictions;
+ TimelineItem mSurfaceFlingerActuals;
+ std::shared_ptr<TimeStats> mTimeStats;
+ const JankClassificationThresholds mJankClassificationThresholds;
+
+ // Collection of predictions and actual values sent over by Layers
+ std::vector<std::shared_ptr<SurfaceFrame>> mSurfaceFrames;
+
+ PredictionState mPredictionState = PredictionState::None;
+ // Bitmask for the type of jank
+ int32_t mJankType = JankType::None;
+ // Indicates if this frame was composited by the GPU or not
+ bool mGpuComposition = false;
+ // Enum for the type of present
+ FramePresentMetadata mFramePresentMetadata = FramePresentMetadata::UnknownPresent;
+ // Enum for the type of finish
+ FrameReadyMetadata mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish;
+ // Enum for the type of start
+ FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart;
+ // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's
+ // timeline
+ nsecs_t mVsyncPeriod = 0;
+ };
+
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
+ JankClassificationThresholds thresholds = {});
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
- std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
- pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
- std::optional<int64_t> token) override;
- void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame,
- SurfaceFrame::PresentState state) override;
- void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
+ std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(std::optional<int64_t> token,
+ pid_t ownerPid, uid_t ownerUid,
+ std::string layerName,
+ std::string debugName) override;
+ void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
+ void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) override;
void setSfPresent(nsecs_t sfPresentTime,
const std::shared_ptr<FenceTime>& presentFence) override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
@@ -288,67 +391,28 @@
// Friend class for testing
friend class android::frametimeline::FrameTimelineTest;
- /*
- * DisplayFrame should be used only internally within FrameTimeline.
- */
- struct DisplayFrame {
- DisplayFrame();
-
- int64_t token = ISurfaceComposer::INVALID_VSYNC_ID;
-
- /* Usage of TimelineItem w.r.t SurfaceFlinger
- * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates
- * endTime Time when SurfaceFlinger sends a composited frame to Display
- * presentTime Time when the composited frame was presented on screen
- */
- TimelineItem surfaceFlingerPredictions;
- TimelineItem surfaceFlingerActuals;
-
- // Collection of predictions and actual values sent over by Layers
- std::vector<std::shared_ptr<SurfaceFrame>> surfaceFrames;
-
- PredictionState predictionState = PredictionState::None;
- JankType jankType = JankType::None; // Enum for the type of jank
- int32_t jankMetadata = 0x0; // Additional details about the jank
- };
-
void flushPendingPresentFences() REQUIRES(mMutex);
void finalizeCurrentDisplayFrame() REQUIRES(mMutex);
- // BaseTime is the smallest timestamp in a DisplayFrame.
- // Used for dumping all timestamps relative to the oldest, making it easy to read.
- nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex);
- void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&,
- nsecs_t baseTime) REQUIRES(mMutex);
void dumpAll(std::string& result);
void dumpJank(std::string& result);
- // Emits a packet for perfetto tracing. The function body will be executed only if tracing is
- // enabled.
- void traceDisplayFrame(const DisplayFrame& displayFrame) REQUIRES(mMutex);
-
// Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array
std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex);
std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>>
mPendingPresentFences GUARDED_BY(mMutex);
std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex);
TokenManager mTokenManager;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
+ const pid_t mSurfaceFlingerPid;
+ const JankClassificationThresholds mJankClassificationThresholds;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although
// this number doesn't represent any bounds on the number of surface frames that can go in a
// display frame, this is a good starting size for the vector so that we can avoid the
// internal vector resizing that happens with push_back.
static constexpr uint32_t kNumSurfaceFramesInitial = 10;
- // The various thresholds for App and SF. If the actual timestamp falls within the threshold
- // compared to prediction, we don't treat it as a jank.
- static constexpr nsecs_t kPresentThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
- static constexpr nsecs_t kDeadlineThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
- static constexpr nsecs_t kSFStartThreshold =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count();
};
} // namespace impl
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f53c4cc..4731f50 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -911,9 +911,8 @@
: std::make_optional(stateToCommit->frameTimelineVsyncId);
mSurfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
- mName, mTransactionName,
- vsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(vsyncId, mOwnerPid, mOwnerUid,
+ mName, mTransactionName);
mSurfaceFrame->setActualQueueTime(stateToCommit->postTime);
// For transactions we set the acquire fence time to the post time as we
// don't have a buffer. For BufferStateLayer it is overridden in
@@ -1066,7 +1065,8 @@
void Layer::commitTransaction(const State& stateToCommit) {
mDrawingState = stateToCommit;
- mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame, PresentState::Presented);
+ mSurfaceFrame->setPresentState(PresentState::Presented);
+ mFlinger->mFrameTimeline->addSurfaceFrame(mSurfaceFrame);
}
uint32_t Layer::getTransactionFlags(uint32_t flags) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bb897d5..d6023b6 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -275,7 +275,8 @@
// recent callback handle.
std::deque<sp<CallbackHandle>> callbackHandles;
bool colorSpaceAgnostic;
- nsecs_t desiredPresentTime = -1;
+ nsecs_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
// Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
// be rendered around the layer.
@@ -444,7 +445,8 @@
virtual bool setFrame(const Rect& /*frame*/) { return false; };
virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
- const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) {
+ bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/,
+ uint64_t /* frameNumber */) {
return false;
};
virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
@@ -881,7 +883,7 @@
*/
bool hasInputInfo() const;
- uid_t getOwnerUid() { return mOwnerUid; }
+ virtual uid_t getOwnerUid() const { return mOwnerUid; }
pid_t getOwnerPid() { return mOwnerPid; }
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index a959b9a..c291b7f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -235,7 +235,7 @@
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
mCurrentFps = refreshRate.getFps().getIntValue();
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
@@ -247,7 +247,7 @@
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {},
+ mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 499daad..170933d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -86,9 +86,8 @@
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, Fps highRefreshRate, LayerVoteType type) {
- const nsecs_t highRefreshRatePeriod = highRefreshRate.getPeriodNsecs();
- auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type);
+void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
+ auto info = std::make_unique<LayerInfo>(layer->getName(), type);
std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info));
}
@@ -143,8 +142,8 @@
const float layerArea = transformed.getWidth() * transformed.getHeight();
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- summary.push_back(
- {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused});
+ summary.push_back({strong->getName(), strong->getOwnerUid(), vote.type, vote.fps,
+ vote.seamlessness, weight, layerFocused});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(layer, *info, vote.type, vote.fps.getIntValue());
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 4214bab..bae9b5a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,7 +46,7 @@
~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, Fps highRefreshRate, LayerVoteType type);
+ void registerLayer(Layer*, LayerVoteType type);
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 1c0065c..4b2862e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -33,10 +33,8 @@
const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr;
bool LayerInfo::sTraceEnabled = false;
-LayerInfo::LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote)
+LayerInfo::LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote)
: mName(name),
- mHighRefreshRatePeriod(highRefreshRatePeriod),
mDefaultVote(defaultVote),
mLayerVote({defaultVote, Fps(0.0f)}),
mRefreshRateHistory(name) {}
@@ -133,7 +131,7 @@
}
totalQueueTimeDeltas +=
- std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+ std::max(((it + 1)->queueTime - it->queueTime), kMinPeriodBetweenFrames);
numFrames++;
if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
@@ -147,7 +145,7 @@
}
totalPresentTimeDeltas +=
- std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+ std::max(((it + 1)->presetTime - it->presetTime), kMinPeriodBetweenFrames);
}
// Calculate the average frame time based on presentation timestamps. If those
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 9304e62..427cc9e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -70,8 +70,7 @@
sRefreshRateConfigs = &refreshRateConfigs;
}
- LayerInfo(const std::string& name, nsecs_t highRefreshRatePeriod,
- LayerHistory::LayerVoteType defaultVote);
+ LayerInfo(const std::string& name, LayerHistory::LayerVoteType defaultVote);
LayerInfo(const LayerInfo&) = delete;
LayerInfo& operator=(const LayerInfo&) = delete;
@@ -194,8 +193,9 @@
const std::string mName;
- // Used for sanitizing the heuristic data
- const nsecs_t mHighRefreshRatePeriod;
+ // Used for sanitizing the heuristic data. If two frames are less than
+ // this period apart from each other they'll be considered as duplicates.
+ static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs();
LayerHistory::LayerVoteType mDefaultVote;
LayerVote mLayerVote;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 47a4f42..7ff0ddf 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -69,17 +69,33 @@
// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
// and remove the EventThread from MessageQueue
-void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
- if (mEventTube.getFd() >= 0) {
- mLooper->removeFd(mEventTube.getFd());
+void MessageQueue::setInjector(sp<EventThreadConnection> connection) {
+ auto& tube = mInjector.tube;
+
+ if (const int fd = tube.getFd(); fd >= 0) {
+ mLooper->removeFd(fd);
}
- mEvents = connection;
- if (mEvents) {
- mEvents->stealReceiveChannel(&mEventTube);
- mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
- this);
+ if (connection) {
+ // The EventThreadConnection is retained when disabling injection, so avoid subsequently
+ // stealing invalid FDs. Note that the stolen FDs are kept open.
+ if (tube.getFd() < 0) {
+ connection->stealReceiveChannel(&tube);
+ } else {
+ ALOGW("Recycling channel for VSYNC injection.");
+ }
+
+ mLooper->addFd(
+ tube.getFd(), 0, Looper::EVENT_INPUT,
+ [](int, int, void* data) {
+ reinterpret_cast<MessageQueue*>(data)->injectorCallback();
+ return 1; // Keep registration.
+ },
+ this);
}
+
+ std::lock_guard lock(mInjector.mutex);
+ mInjector.connection = std::move(connection);
}
void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
@@ -149,29 +165,31 @@
void MessageQueue::invalidate() {
ATRACE_CALL();
- if (mEvents) {
- mEvents->requestNextVsync();
- } else {
- std::lock_guard lock(mVsync.mutex);
- mVsync.mScheduled = true;
- mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
- mVsync.lastCallbackTime.count()});
+
+ {
+ std::lock_guard lock(mInjector.mutex);
+ if (CC_UNLIKELY(mInjector.connection)) {
+ ALOGD("%s while injecting VSYNC", __FUNCTION__);
+ mInjector.connection->requestNextVsync();
+ return;
+ }
}
+
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.mScheduled = true;
+ mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ .readyDuration = 0,
+ .earliestVsync = mVsync.lastCallbackTime.count()});
}
void MessageQueue::refresh() {
mHandler->dispatchRefresh();
}
-int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
- MessageQueue* queue = reinterpret_cast<MessageQueue*>(data);
- return queue->eventReceiver(fd, events);
-}
-
-int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
+void MessageQueue::injectorCallback() {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
- while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
+ while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
@@ -180,8 +198,6 @@
}
}
}
- return 1;
}
} // namespace android::impl
-
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 99ce3a6..2934af0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -21,12 +21,11 @@
#include <type_traits>
#include <utility>
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/threads.h>
-
+#include <android-base/thread_annotations.h>
#include <gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
+#include <utils/Looper.h>
+#include <utils/Timers.h>
#include "EventThread.h"
#include "TracedOrdinal.h"
@@ -68,7 +67,7 @@
virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
- virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
+ virtual void setInjector(sp<EventThreadConnection>) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void invalidate() = 0;
@@ -99,7 +98,6 @@
sp<SurfaceFlinger> mFlinger;
sp<Looper> mLooper;
- sp<EventThreadConnection> mEvents;
struct Vsync {
frametimeline::TokenManager* tokenManager = nullptr;
@@ -113,14 +111,19 @@
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
- Vsync mVsync;
+ struct Injector {
+ gui::BitTube tube;
+ std::mutex mutex;
+ sp<EventThreadConnection> connection GUARDED_BY(mutex);
+ };
- gui::BitTube mEventTube;
+ Vsync mVsync;
+ Injector mInjector;
+
sp<Handler> mHandler;
- static int cb_eventReceiver(int fd, int events, void* data);
- int eventReceiver(int fd, int events);
void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+ void injectorCallback();
public:
~MessageQueue() override = default;
@@ -128,7 +131,7 @@
void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) override;
void setDuration(std::chrono::nanoseconds workDuration) override;
- void setEventConnection(const sp<EventThreadConnection>& connection) override;
+ void setInjector(sp<EventThreadConnection>) override;
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 4b7251b..5b78483 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -71,16 +71,84 @@
std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
nsecs_t displayPeriod) const {
- auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
- if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
- std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
- displayFramesQuot++;
- displayFramesRem = 0;
+ auto [quotient, remainder] = std::div(layerPeriod, displayPeriod);
+ if (remainder <= MARGIN_FOR_PERIOD_CALCULATION ||
+ std::abs(remainder - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+ quotient++;
+ remainder = 0;
}
- return {displayFramesQuot, displayFramesRem};
+ return {quotient, remainder};
}
+float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
+ const RefreshRate& refreshRate,
+ bool isSeamlessSwitch) const {
+ // Slightly prefer seamless switches.
+ constexpr float kSeamedSwitchPenalty = 0.95f;
+ const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+
+ // If the layer wants Max, give higher score to the higher refresh rate
+ if (layer.vote == LayerVoteType::Max) {
+ const auto ratio =
+ refreshRate.fps.getValue() / mAppRequestRefreshRates.back()->fps.getValue();
+ // use ratio^2 to get a lower score the more we get further from peak
+ return ratio * ratio;
+ }
+
+ const auto displayPeriod = refreshRate.getVsyncPeriod();
+ const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+ if (layer.vote == LayerVoteType::ExplicitDefault) {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal time to render a frame
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
+ }
+ return std::min(1.0f,
+ static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+ layer.vote == LayerVoteType::Heuristic) {
+ // Calculate how many display vsyncs we need to present a single frame for this
+ // layer
+ const auto [displayFramesQuotient, displayFramesRemainder] =
+ getDisplayFrames(layerPeriod, displayPeriod);
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRemainder == 0) {
+ // Layer desired refresh rate matches the display rate.
+ return 1.0f * seamlessness;
+ }
+
+ if (displayFramesQuotient == 0) {
+ // Layer desired refresh rate is higher than the display rate.
+ return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ }
+
+ // Layer desired refresh rate is lower than the display rate. Check how well it fits
+ // the cadence.
+ auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+ int iter = 2;
+ while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ return (1.0f / iter) * seamlessness;
+ }
+
+ return 0;
+}
+
+struct RefreshRateScore {
+ const RefreshRate* refreshRate;
+ float score;
+};
+
const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
GlobalSignals* outSignalsConsidered) const {
@@ -165,11 +233,11 @@
}
// Find the best refresh rate based on score
- std::vector<std::pair<const RefreshRate*, float>> scores;
+ std::vector<RefreshRateScore> scores;
scores.reserve(mAppRequestRefreshRates.size());
for (const auto refreshRate : mAppRequestRefreshRates) {
- scores.emplace_back(refreshRate, 0.0f);
+ scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig);
@@ -184,12 +252,13 @@
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
- const bool isSeamlessSwitch =
- scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup();
+ const bool isSeamlessSwitch = scores[i].refreshRate->getConfigGroup() ==
+ mCurrentRefreshRate->getConfigGroup();
if (layer.seamlessness == Seamlessness::OnlySeamless && !isSeamlessSwitch) {
ALOGV("%s ignores %s to avoid non-seamless switch. Current config = %s",
- formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
continue;
}
@@ -198,7 +267,8 @@
!layer.focused) {
ALOGV("%s ignores %s because it's not focused and the switch is going to be seamed."
" Current config = %s",
- formatLayerInfo(layer, weight).c_str(), scores[i].first->toString().c_str(),
+ formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
continue;
}
@@ -209,18 +279,20 @@
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
const bool isInPolicyForDefault = seamedLayers > 0
- ? scores[i].first->getConfigGroup() == mCurrentRefreshRate->getConfigGroup()
- : scores[i].first->getConfigGroup() == defaultConfig->getConfigGroup();
+ ? scores[i].refreshRate->getConfigGroup() ==
+ mCurrentRefreshRate->getConfigGroup()
+ : scores[i].refreshRate->getConfigGroup() == defaultConfig->getConfigGroup();
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
!layer.focused) {
ALOGV("%s ignores %s. Current config = %s", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->toString().c_str(), mCurrentRefreshRate->toString().c_str());
+ scores[i].refreshRate->toString().c_str(),
+ mCurrentRefreshRate->toString().c_str());
continue;
}
- bool inPrimaryRange =
- scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+ bool inPrimaryRange = scores[i].refreshRate->inPolicy(policy->primaryRange.min,
+ policy->primaryRange.max);
if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
!(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
// Only focused layers with ExplicitDefault frame rate settings are allowed to score
@@ -228,81 +300,11 @@
continue;
}
- // If the layer wants Max, give higher score to the higher refresh rate
- if (layer.vote == LayerVoteType::Max) {
- const auto ratio =
- scores[i].first->fps.getValue() / scores.back().first->fps.getValue();
- // use ratio^2 to get a lower score the more we get further from peak
- const auto layerScore = ratio * ratio;
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
- const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
- if (layer.vote == LayerVoteType::ExplicitDefault) {
- const auto layerScore = [&]() {
- // Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal time to render a frame
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
- return std::min(1.0f,
- static_cast<float>(layerPeriod) /
- static_cast<float>(actualLayerPeriod));
- }();
-
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore;
- continue;
- }
-
- if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
- layer.vote == LayerVoteType::Heuristic) {
- const auto layerScore = [&] {
- // Calculate how many display vsyncs we need to present a single frame for this
- // layer
- const auto [displayFramesQuot, displayFramesRem] =
- getDisplayFrames(layerPeriod, displayPeriod);
- static constexpr size_t MAX_FRAMES_TO_FIT =
- 10; // Stop calculating when score < 0.1
- if (displayFramesRem == 0) {
- // Layer desired refresh rate matches the display rate.
- return 1.0f;
- }
-
- if (displayFramesQuot == 0) {
- // Layer desired refresh rate is higher the display rate.
- return (static_cast<float>(layerPeriod) /
- static_cast<float>(displayPeriod)) *
- (1.0f / (MAX_FRAMES_TO_FIT + 1));
- }
-
- // Layer desired refresh rate is lower the display rate. Check how well it fits
- // the cadence
- auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
- int iter = 2;
- while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
- diff = diff - (displayPeriod - diff);
- iter++;
- }
-
- return 1.0f / iter;
- }();
- // Slightly prefer seamless switches.
- constexpr float kSeamedSwitchPenalty = 0.95f;
- const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
- scores[i].first->getName().c_str(), layerScore);
- scores[i].second += weight * layerScore * seamlessness;
- continue;
- }
+ const auto layerScore =
+ calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
+ ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+ scores[i].refreshRate->getName().c_str(), layerScore);
+ scores[i].score += weight * layerScore;
}
}
@@ -317,7 +319,7 @@
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
- [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+ [](RefreshRateScore score) { return score.score == 0; })) {
ALOGV("layers not scored - choose %s",
getMaxRefreshRateByPolicyLocked().getName().c_str());
return getMaxRefreshRateByPolicyLocked();
@@ -342,11 +344,110 @@
return *bestRefreshRate;
}
+std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>>
+groupLayersByUid(const std::vector<RefreshRateConfigs::LayerRequirement>& layers) {
+ std::unordered_map<uid_t, std::vector<const RefreshRateConfigs::LayerRequirement*>> layersByUid;
+ for (const auto& layer : layers) {
+ auto iter = layersByUid.emplace(layer.ownerUid,
+ std::vector<const RefreshRateConfigs::LayerRequirement*>());
+ auto& layersWithSameUid = iter.first->second;
+ layersWithSameUid.push_back(&layer);
+ }
+
+ // Remove uids that can't have a frame rate override
+ for (auto iter = layersByUid.begin(); iter != layersByUid.end();) {
+ const auto& layersWithSameUid = iter->second;
+ bool skipUid = false;
+ for (const auto& layer : layersWithSameUid) {
+ if (layer->vote == RefreshRateConfigs::LayerVoteType::Max ||
+ layer->vote == RefreshRateConfigs::LayerVoteType::Heuristic) {
+ skipUid = true;
+ break;
+ }
+ }
+ if (skipUid) {
+ iter = layersByUid.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ return layersByUid;
+}
+
+std::vector<RefreshRateScore> initializeScoresForAllRefreshRates(
+ const AllRefreshRatesMapType& refreshRates) {
+ std::vector<RefreshRateScore> scores;
+ scores.reserve(refreshRates.size());
+ for (const auto& [ignored, refreshRate] : refreshRates) {
+ scores.emplace_back(RefreshRateScore{refreshRate.get(), 0.0f});
+ }
+ std::sort(scores.begin(), scores.end(),
+ [](const auto& a, const auto& b) { return *a.refreshRate < *b.refreshRate; });
+ return scores;
+}
+
+RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
+ const std::vector<LayerRequirement>& layers, Fps displayFrameRate) const {
+ ATRACE_CALL();
+ ALOGV("getFrameRateOverrides %zu layers", layers.size());
+
+ std::lock_guard lock(mLock);
+ std::vector<RefreshRateScore> scores = initializeScoresForAllRefreshRates(mRefreshRates);
+ std::unordered_map<uid_t, std::vector<const LayerRequirement*>> layersByUid =
+ groupLayersByUid(layers);
+ UidToFrameRateOverride frameRateOverrides;
+ for (const auto& [uid, layersWithSameUid] : layersByUid) {
+ for (auto& score : scores) {
+ score.score = 0;
+ }
+
+ for (const auto& layer : layersWithSameUid) {
+ if (layer->vote == LayerVoteType::NoVote || layer->vote == LayerVoteType::Min) {
+ continue;
+ }
+
+ LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
+ layer->vote != LayerVoteType::ExplicitExactOrMultiple);
+ for (RefreshRateScore& score : scores) {
+ const auto layerScore = calculateLayerScoreLocked(*layer, *score.refreshRate,
+ /*isSeamlessSwitch*/ true);
+ score.score += layer->weight * layerScore;
+ }
+ }
+
+ // We just care about the refresh rates which are a divider of the
+ // display refresh rate
+ auto iter =
+ std::remove_if(scores.begin(), scores.end(), [&](const RefreshRateScore& score) {
+ return getFrameRateDivider(displayFrameRate, score.refreshRate->getFps()) == 0;
+ });
+ scores.erase(iter, scores.end());
+
+ // If we never scored any layers, we don't have a preferred frame rate
+ if (std::all_of(scores.begin(), scores.end(),
+ [](const RefreshRateScore& score) { return score.score == 0; })) {
+ continue;
+ }
+
+ // Now that we scored all the refresh rates we need to pick the one that got the highest
+ // score.
+ const RefreshRate* bestRefreshRate = getBestRefreshRate(scores.begin(), scores.end());
+
+ // If the nest refresh rate is the current one, we don't have an override
+ if (!bestRefreshRate->getFps().equalsWithMargin(displayFrameRate)) {
+ frameRateOverrides.emplace(uid, bestRefreshRate->getFps());
+ }
+ }
+
+ return frameRateOverrides;
+}
+
template <typename Iter>
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
constexpr auto EPSILON = 0.001f;
- const RefreshRate* bestRefreshRate = begin->first;
- float max = begin->second;
+ const RefreshRate* bestRefreshRate = begin->refreshRate;
+ float max = begin->score;
for (auto i = begin; i != end; ++i) {
const auto [refreshRate, score] = *i;
ALOGV("%s scores %.2f", refreshRate->getName().c_str(), score);
@@ -362,10 +463,6 @@
return bestRefreshRate;
}
-const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
- return mRefreshRates;
-}
-
const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
return getMinRefreshRateByPolicyLocked();
@@ -649,50 +746,22 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
-void RefreshRateConfigs::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
- if (frameRateOverride.frameRateHz > 0 && frameRateOverride.frameRateHz < 1) {
- return;
- }
-
- std::lock_guard lock(mLock);
- if (frameRateOverride.frameRateHz != 0) {
- mPreferredRefreshRateForUid[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
- } else {
- mPreferredRefreshRateForUid.erase(frameRateOverride.uid);
- }
-}
-
-int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
- std::lock_guard lock(mLock);
-
- const auto iter = mPreferredRefreshRateForUid.find(uid);
- if (iter == mPreferredRefreshRateForUid.end()) {
- return 1;
- }
-
+int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
// This calculation needs to be in sync with the java code
// in DisplayManagerService.getDisplayInfoForFrameRateOverride
constexpr float kThreshold = 0.1f;
- const auto refreshRateHz = iter->second;
- const auto numPeriods = mCurrentRefreshRate->getFps().getValue() / refreshRateHz.getValue();
+ const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
const auto numPeriodsRounded = std::round(numPeriods);
if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
- return 1;
+ return 0;
}
return static_cast<int>(numPeriodsRounded);
}
-std::vector<FrameRateOverride> RefreshRateConfigs::getFrameRateOverrides() {
+int RefreshRateConfigs::getRefreshRateDivider(Fps frameRate) const {
std::lock_guard lock(mLock);
- std::vector<FrameRateOverride> overrides;
- overrides.reserve(mPreferredRefreshRateForUid.size());
-
- for (const auto [uid, frameRate] : mPreferredRefreshRateForUid) {
- overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
- }
-
- return overrides;
+ return getFrameRateDivider(mCurrentRefreshRate->getFps(), frameRate);
}
void RefreshRateConfigs::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index ec7ffe5..6c83f8b 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -221,6 +221,8 @@
struct LayerRequirement {
// Layer's name. Used for debugging purposes.
std::string name;
+ // Layer's owner uid
+ uid_t ownerUid = static_cast<uid_t>(-1);
// Layer vote type.
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
@@ -261,9 +263,6 @@
GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
- // Returns all the refresh rates supported by the device. This won't change at runtime.
- const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
-
// Returns the lowest refresh rate supported by the device. This won't change at runtime.
const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
@@ -319,18 +318,16 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
- // Stores the preferred refresh rate that an app should run at.
- // FrameRateOverride.refreshRateHz == 0 means no preference.
- void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mLock);
-
// Returns a divider for the current refresh rate
- int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+ int getRefreshRateDivider(Fps frameRate) const EXCLUDES(mLock);
+
+ // Returns the frame rate override for each uid
+ using UidToFrameRateOverride = std::map<uid_t, Fps>;
+ UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
+ Fps displayFrameRate) const EXCLUDES(mLock);
void dump(std::string& result) const EXCLUDES(mLock);
- // Returns the current frame rate overrides
- std::vector<FrameRateOverride> getFrameRateOverrides() EXCLUDES(mLock);
-
private:
friend class RefreshRateConfigsTest;
@@ -367,6 +364,16 @@
const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
bool isPolicyValid(const Policy& policy);
+ // Return the display refresh rate divider to match the layer
+ // frame rate, or 0 if the display refresh rate is not a multiple of the
+ // layer refresh rate.
+ static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+
+ // calculates a score for a layer. Used to determine the display refresh rate
+ // and the frame rate override for certains applications.
+ float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
+ bool isSeamlessSwitch) const REQUIRES(mLock);
+
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates;
@@ -388,10 +395,6 @@
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
- // A mapping between a UID and a preferred refresh rate that this app would
- // run at.
- std::unordered_map<uid_t, Fps> mPreferredRefreshRateForUid GUARDED_BY(mLock);
-
// The min and max refresh rates supported by the device.
// This will not change at runtime.
const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 07411b0..ab8bff1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,8 +206,32 @@
readyDuration, traceVsync, name);
}
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+ std::lock_guard lock(mFeatureStateLock);
+ {
+ const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+ if (iter != mFrameRateOverridesFromBackdoor.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ {
+ const auto iter = mFeatures.frameRateOverrides.find(uid);
+ if (iter != mFeatures.frameRateOverrides.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ return std::nullopt;
+}
+
bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
- const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return true;
+ }
+
+ const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
if (divider <= 1) {
return true;
}
@@ -290,8 +314,19 @@
thread->onScreenReleased();
}
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- std::vector<FrameRateOverride> overrides) {
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+ std::vector<FrameRateOverride> overrides;
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+ if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ }
+ }
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -303,9 +338,11 @@
void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- // Cache the last reported config for primary display.
- mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ // Cache the last reported config for primary display.
+ mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+ }
onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
}
@@ -399,6 +436,10 @@
impl::EventThread::InterceptVSyncsCallback(),
impl::EventThread::ThrottleVsyncCallback());
+ // EventThread does not dispatch VSYNC unless the display is connected and powered on.
+ eventThread->onHotplugReceived(PhysicalDisplayId::fromPort(0), true);
+ eventThread->onScreenAcquired();
+
mInjectorConnectionHandle = createConnection(std::move(eventThread));
}
@@ -510,23 +551,19 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
-
if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) {
- mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::NoVote);
+ mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::NoVote);
} else if (!mOptions.useContentDetection) {
// If the content detection feature is off, all layers are registered at Max. We still keep
// the layer history, since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory->registerLayer(layer, maxFps, scheduler::LayerHistory::LayerVoteType::Max);
+ mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Max);
} else {
if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
- mLayerHistory->registerLayer(layer, maxFps,
- scheduler::LayerHistory::LayerVoteType::Min);
+ mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Min);
} else {
- mLayerHistory->registerLayer(layer, maxFps,
- scheduler::LayerHistory::LayerVoteType::Heuristic);
+ mLayerHistory->registerLayer(layer, scheduler::LayerHistory::LayerVoteType::Heuristic);
}
}
}
@@ -550,7 +587,10 @@
ATRACE_CALL();
scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
HwcConfigIndexType newConfigId;
+ bool frameRateChanged;
+ bool frameRateOverridesChanged;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (mFeatures.contentRequirements == summary) {
@@ -558,22 +598,32 @@
}
mFeatures.contentRequirements = summary;
- scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+ auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
if (mFeatures.configId == newConfigId) {
// We don't need to change the config, but we might need to send an event
// about a config change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
dispatchCachedReportedConfig();
}
- return;
+ frameRateChanged = false;
+ } else {
+ mFeatures.configId = newConfigId;
+ frameRateChanged = true;
}
- mFeatures.configId = newConfigId;
+ }
+ if (frameRateChanged) {
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate,
consideredSignals.idle ? ConfigEvent::None
: ConfigEvent::Changed);
}
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ }
}
void Scheduler::resetIdleTimer() {
@@ -666,6 +716,21 @@
StringAppendF(&result, "+ Content detection: %s %s\n\n",
toContentDetectionString(mOptions.useContentDetection),
mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+ {
+ std::lock_guard lock(mFeatureStateLock);
+ StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+
+ StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+ for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+ }
}
void Scheduler::dumpVsync(std::string& s) const {
@@ -677,9 +742,35 @@
mVsyncSchedule.dispatch->dump(s);
}
+bool Scheduler::updateFrameRateOverrides(
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ if (consideredSignals.touch) {
+ const bool changed = !mFeatures.frameRateOverrides.empty();
+ mFeatures.frameRateOverrides.clear();
+ return changed;
+ }
+
+ if (!consideredSignals.idle) {
+ const auto frameRateOverrides =
+ mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+ displayRefreshRate);
+ if (!std::equal(mFeatures.frameRateOverrides.begin(), mFeatures.frameRateOverrides.end(),
+ frameRateOverrides.begin(), frameRateOverrides.end(),
+ [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+ return a.first == b.first && a.second.equalsWithMargin(b.second);
+ })) {
+ mFeatures.frameRateOverrides = frameRateOverrides;
+ return true;
+ }
+ }
+ return false;
+}
+
template <class T>
bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
+ bool refreshRateChanged = false;
+ bool frameRateOverridesChanged;
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -688,20 +779,32 @@
}
*currentState = newState;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+ const RefreshRate& newRefreshRate =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
if (mFeatures.configId == newConfigId) {
// We don't need to change the config, but we might need to send an event
// about a config change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
dispatchCachedReportedConfig();
}
- return consideredSignals.touch;
+ } else {
+ mFeatures.configId = newConfigId;
+ refreshRateChanged = true;
}
- mFeatures.configId = newConfigId;
}
- const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ConfigEvent::None
- : ConfigEvent::Changed);
+ if (refreshRateChanged) {
+ const RefreshRate& newRefreshRate =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
+ }
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ }
return consideredSignals.touch;
}
@@ -775,4 +878,17 @@
}
}
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+ return;
+ }
+
+ std::lock_guard lock(mFeatureStateLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+ } else {
+ mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f16e1f9..052c425 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -60,6 +60,7 @@
scheduler::RefreshRateConfigEvent) = 0;
virtual void repaintEverythingForHWC() = 0;
virtual void kernelTimerChanged(bool expired) = 0;
+ virtual void triggerOnFrameRateOverridesChanged() = 0;
protected:
~ISchedulerCallback() = default;
@@ -93,8 +94,7 @@
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
- void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId,
- std::vector<FrameRateOverride>);
+ void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId);
// Modifies work duration in the event thread.
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
@@ -169,6 +169,10 @@
std::chrono::nanoseconds readyDuration,
bool traceVsync = true);
+ // Stores the preferred refresh rate that an app should run at.
+ // FrameRateOverride.refreshRateHz == 0 means no preference.
+ void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFeatureStateLock);
+
private:
friend class TestableScheduler;
@@ -226,6 +230,10 @@
REQUIRES(mFeatureStateLock);
void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+ bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
+ Fps displayRefreshRate) REQUIRES(mFeatureStateLock);
+
+ std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFeatureStateLock);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -264,7 +272,7 @@
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
- std::mutex mFeatureStateLock;
+ mutable std::mutex mFeatureStateLock;
struct {
TimerState idleTimer = TimerState::Reset;
@@ -285,6 +293,7 @@
};
std::optional<ConfigChangedParams> cachedConfigChangedParams;
+ scheduler::RefreshRateConfigs::UidToFrameRateOverride frameRateOverrides;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
@@ -295,6 +304,11 @@
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+
+ // mappings between a UID and a preferred refresh rate that this app would
+ // run at.
+ scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+ GUARDED_BY(mFeatureStateLock);
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 8431323..cb57aea 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -16,14 +16,19 @@
#include "VsyncConfiguration.h"
-#include <cutils/properties.h>
-
+#include <chrono>
+#include <cinttypes>
#include <optional>
+#include <cutils/properties.h>
+#include <log/log.h>
+
#include "SurfaceFlingerProperties.h"
namespace {
+using namespace std::chrono_literals;
+
std::optional<nsecs_t> getProperty(const char* name) {
char value[PROPERTY_VALUE_MAX];
property_get(name, value, "-1");
@@ -31,19 +36,6 @@
return std::nullopt;
}
-std::vector<android::Fps> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<android::Fps> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->getFps());
- }
-
- return refreshRates;
-}
-
} // namespace
namespace android::scheduler::impl {
@@ -51,25 +43,19 @@
VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
- const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
- [&fps](const std::pair<Fps, VsyncConfigSet>& candidateFps) {
- return fps.equalsWithMargin(candidateFps.first);
- });
+ std::lock_guard lock(mLock);
+ return getConfigsForRefreshRateLocked(fps);
+}
- if (iter != mOffsets.end()) {
+PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+ const auto iter = mOffsetsCache.find(fps);
+ if (iter != mOffsetsCache.end()) {
return iter->second;
}
- // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
- // In this case just construct the offset.
- ALOGW("Can't find offset for %s", to_string(fps).c_str());
- return constructOffsets(fps.getPeriodNsecs());
-}
-
-void VsyncConfiguration::initializeOffsets(const std::vector<Fps>& refreshRates) {
- for (const auto fps : refreshRates) {
- mOffsets.emplace(fps, constructOffsets(fps.getPeriodNsecs()));
- }
+ const auto offset = constructOffsets(fps.getPeriodNsecs());
+ mOffsetsCache[fps] = offset;
+ return offset;
}
void VsyncConfiguration::dump(std::string& result) const {
@@ -98,10 +84,8 @@
earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count());
}
-PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- sysprop::vsync_event_phase_offset_ns(1000000),
+PhaseOffsets::PhaseOffsets(Fps currentRefreshRate)
+ : PhaseOffsets(currentRefreshRate, sysprop::vsync_event_phase_offset_ns(1000000),
sysprop::vsync_sf_event_phase_offset_ns(1000000),
getProperty("debug.sf.early_phase_offset_ns"),
getProperty("debug.sf.early_gl_phase_offset_ns"),
@@ -121,15 +105,17 @@
getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
.value_or(std::numeric_limits<nsecs_t>::max())) {}
-PhaseOffsets::PhaseOffsets(
- const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
- nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
- std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
- nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
- std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
- std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
- std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync)
+PhaseOffsets::PhaseOffsets(Fps currentFps, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs,
+ std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
+ std::optional<nsecs_t> earlyGpuAppOffsetNs,
+ nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> highFpsEarlySfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
+ std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
+ nsecs_t thresholdForNextVsync)
: VsyncConfiguration(currentFps),
mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
@@ -143,9 +129,7 @@
mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs),
mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs),
mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs),
- mThresholdForNextVsync(thresholdForNextVsync) {
- initializeOffsets(refreshRates);
-}
+ mThresholdForNextVsync(thresholdForNextVsync) {}
PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
@@ -361,10 +345,8 @@
};
}
-WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs),
- refreshRateConfigs.getCurrentRefreshRate().getFps(),
- getProperty("debug.sf.late.sf.duration").value_or(-1),
+WorkDuration::WorkDuration(Fps currentRefreshRate)
+ : WorkDuration(currentRefreshRate, getProperty("debug.sf.late.sf.duration").value_or(-1),
getProperty("debug.sf.late.app.duration").value_or(-1),
getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
@@ -373,17 +355,15 @@
validateSysprops();
}
-WorkDuration::WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration)
- : VsyncConfiguration(currentFps),
+ : VsyncConfiguration(currentRefreshRate),
mSfDuration(sfDuration),
mAppDuration(appDuration),
mSfEarlyDuration(sfEarlyDuration),
mAppEarlyDuration(appEarlyDuration),
mSfEarlyGpuDuration(sfEarlyGpuDuration),
- mAppEarlyGpuDuration(appEarlyGpuDuration) {
- initializeOffsets(refreshRates);
-}
+ mAppEarlyGpuDuration(appEarlyGpuDuration) {}
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index a120e97..d9d206d 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -16,12 +16,14 @@
#pragma once
+#include <mutex>
+#include <type_traits>
#include <unordered_map>
+#include <vector>
#include <utils/Timers.h>
#include "Fps.h"
-#include "RefreshRateConfigs.h"
#include "VsyncModulator.h"
namespace android::scheduler {
@@ -39,9 +41,9 @@
virtual ~VsyncConfiguration() = default;
virtual VsyncConfigSet getCurrentConfigs() const = 0;
virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
+ virtual void reset() = 0;
virtual void setRefreshRateFps(Fps fps) = 0;
-
virtual void dump(std::string& result) const = 0;
};
@@ -57,26 +59,39 @@
explicit VsyncConfiguration(Fps currentFps);
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override;
+ VsyncConfigSet getConfigsForRefreshRate(Fps fps) const override EXCLUDES(mLock);
// Returns early, early GL, and late offsets for Apps and SF.
- VsyncConfigSet getCurrentConfigs() const override {
- return getConfigsForRefreshRate(mRefreshRateFps);
+ VsyncConfigSet getCurrentConfigs() const override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ return getConfigsForRefreshRateLocked(mRefreshRateFps);
+ }
+
+ // Cleans the internal cache.
+ void reset() override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ mOffsetsCache.clear();
}
// This function should be called when the device is switching between different
// refresh rates, to properly update the offsets.
- void setRefreshRateFps(Fps fps) override { mRefreshRateFps = fps; }
+ void setRefreshRateFps(Fps fps) override EXCLUDES(mLock) {
+ std::lock_guard lock(mLock);
+ mRefreshRateFps = fps;
+ }
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
protected:
- void initializeOffsets(const std::vector<Fps>& refreshRates);
virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
- std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets> mOffsets;
- std::atomic<Fps> mRefreshRateFps;
+ VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
+
+ mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
+ mOffsetsCache GUARDED_BY(mLock);
+ std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+ mutable std::mutex mLock;
};
/*
@@ -85,13 +100,13 @@
*/
class PhaseOffsets : public VsyncConfiguration {
public:
- explicit PhaseOffsets(const scheduler::RefreshRateConfigs&);
+ explicit PhaseOffsets(Fps currentRefreshRate);
protected:
// Used for unit tests
- PhaseOffsets(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t vsyncPhaseOffsetNs,
- nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs,
- std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs,
+ PhaseOffsets(Fps currentRefreshRate, nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+ std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs,
+ std::optional<nsecs_t> earlyAppOffsetNs,
std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs,
nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs,
std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs,
@@ -128,13 +143,12 @@
*/
class WorkDuration : public VsyncConfiguration {
public:
- explicit WorkDuration(const scheduler::RefreshRateConfigs&);
+ explicit WorkDuration(Fps currentRefrshRate);
protected:
// Used for unit tests
- WorkDuration(const std::vector<Fps>& refreshRates, Fps currentFps, nsecs_t sfDuration,
- nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
- nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
+ WorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration);
private:
VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d9c8457..5d08328 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -339,7 +339,7 @@
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
- mFrameTimeline(mFactory.createFrameTimeline(mTimeStats)),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -710,16 +710,17 @@
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
- .setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
- .setEnableProtectedContext(enable_protected_contents(false))
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
- .setContextPriority(useContextPriority
- ? renderengine::RenderEngine::ContextPriority::HIGH
- : renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build()));
+ .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+ .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+ .setUseColorManagerment(useColorManagement)
+ .setEnableProtectedContext(enable_protected_contents(false))
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mSupportsBlur)
+ .setContextPriority(
+ useContextPriority
+ ? renderengine::RenderEngine::ContextPriority::REALTIME
+ : renderengine::RenderEngine::ContextPriority::MEDIUM)
+ .build()));
mCompositionEngine->setTimeStats(mTimeStats);
mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
@@ -1430,8 +1431,7 @@
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle)
- : nullptr);
+ mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
}
}).wait();
@@ -1871,7 +1871,7 @@
const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
- mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
+ mFrameTimeline->setSfWakeUp(vsyncId, frameStart, stats.vsyncPeriod);
refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
@@ -2620,8 +2620,14 @@
setPowerModeInternal(display, hal::PowerMode::ON);
// TODO(b/175678251) Call a listener instead.
- if (mRefreshRateOverlay) {
- mRefreshRateOverlay->reset();
+ if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ const auto displayId = currentState.physical->id;
+ const auto configs = getHwComposer().getConfigs(displayId);
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate());
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->reset();
+ }
}
}
return;
@@ -2884,12 +2890,21 @@
Scheduler::ConfigEvent event) {
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
- // Sheduler::chooseRefreshRateForContent
+ // Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
changeRefreshRateLocked(refreshRate, event);
}
+void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
+ PhysicalDisplayId displayId = [&]() {
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+ return getDefaultDisplayDeviceLocked()->getPhysicalId();
+ }();
+
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
+}
+
void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) {
if (mScheduler) {
// In practice it's not allowed to hotplug in/out the primary display once it's been
@@ -2908,7 +2923,7 @@
std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, currRefreshRate.getFps(),
hal::PowerMode::OFF);
- mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs);
+ mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate.getFps());
mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs());
// start the EventThread
@@ -3247,8 +3262,9 @@
applyTransactionState(transaction.frameTimelineVsyncId, transaction.states,
transaction.displays, transaction.flags,
mPendingInputWindowCommands, transaction.desiredPresentTime,
- transaction.buffer, transaction.postTime,
- transaction.privileged, transaction.hasListenerCallbacks,
+ transaction.isAutoTimestamp, transaction.buffer,
+ transaction.postTime, transaction.privileged,
+ transaction.hasListenerCallbacks,
transaction.listenerCallbacks, transaction.originPid,
transaction.originUid, transaction.id, /*isMainThread*/ true);
transactionQueue.pop();
@@ -3279,7 +3295,7 @@
bool ready = true;
// 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 &&
+ if (desiredPresentTime > 0 && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
ready = false;
}
@@ -3292,14 +3308,18 @@
if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
ready = false;
}
+ sp<Layer> layer = nullptr;
+ if (s.surface) {
+ layer = fromHandleLocked(s.surface).promote();
+ } else {
+ ALOGW("Transaction with buffer, but no Layer?");
+ continue;
+ }
+ if (layer && !mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ ready = false;
+ }
if (updateTransactionCounters) {
- sp<Layer> layer = nullptr;
- if (s.surface) {
- layer = fromHandleLocked(s.surface).promote();
- } else {
- ALOGW("Transaction with buffer, but no Layer?");
- continue;
- }
// See BufferStateLayer::mPendingBufferTransactions
if (layer) layer->incrementPendingBufferCount();
@@ -3312,7 +3332,7 @@
int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
ATRACE_CALL();
@@ -3342,24 +3362,30 @@
const bool pendingTransactions = itr != mTransactionQueues.end();
// Expected present time is computed and cached on invalidate, so it may be stale.
if (!pendingTransactions) {
- mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
+ // The transaction might arrive just before the next vsync but after
+ // invalidate was called. In that case we need to get the next vsync
+ // afterwards.
+ const auto referenceTime = std::max(mExpectedPresentTime.load(), systemTime());
+ mExpectedPresentTime = calculateExpectedPresentTime(referenceTime);
}
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
- if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states, true)) {
+ if (pendingTransactions ||
+ !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) {
mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags,
- desiredPresentTime, uncacheBuffer, postTime,
- privileged, hasListenerCallbacks, listenerCallbacks,
- originPid, originUid, transactionId);
+ desiredPresentTime, isAutoTimestamp, uncacheBuffer,
+ postTime, privileged, hasListenerCallbacks,
+ listenerCallbacks, originPid, originUid,
+ transactionId);
setTransactionFlags(eTransactionFlushNeeded);
return NO_ERROR;
}
applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands,
- desiredPresentTime, uncacheBuffer, postTime, privileged,
+ desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged,
hasListenerCallbacks, listenerCallbacks, originPid, originUid,
transactionId, /*isMainThread*/ false);
return NO_ERROR;
@@ -3369,9 +3395,10 @@
int64_t frameTimelineVsyncId, const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
- const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
- bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId, bool isMainThread) {
+ bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime,
+ bool privileged, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+ uint64_t transactionId, bool isMainThread) {
uint32_t transactionFlags = 0;
if (flags & eAnimation) {
@@ -3405,12 +3432,13 @@
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |=
- setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime,
- privileged, listenerCallbacksWithSurfaces);
+ clientStateFlags |= setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime,
+ isAutoTimestamp, postTime, privileged,
+ listenerCallbacksWithSurfaces);
if ((flags & eAnimation) && state.state.surface) {
if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
- mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+ mScheduler->recordLayerHistory(layer.get(),
+ isAutoTimestamp ? 0 : desiredPresentTime,
LayerHistory::LayerUpdateType::AnimationTX);
}
}
@@ -3585,7 +3613,7 @@
uint32_t SurfaceFlinger::setClientStateLocked(
int64_t frameTimelineVsyncId, const ComposerState& composerState,
- int64_t desiredPresentTime, int64_t postTime, bool privileged,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
const layer_state_t& s = composerState.state;
@@ -3896,8 +3924,8 @@
? s.frameNumber
: layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
- if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer,
- frameNumber)) {
+ if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
+ s.cachedBuffer, frameNumber)) {
flags |= eTraversalNeeded;
}
}
@@ -4175,7 +4203,7 @@
d.height = 0;
displays.add(d);
setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr,
- mPendingInputWindowCommands, -1, {}, false, {},
+ mPendingInputWindowCommands, systemTime(), true, {}, false, {},
0 /* Undefined transactionId */);
setPowerModeInternal(display, hal::PowerMode::ON);
@@ -5281,12 +5309,17 @@
return NO_ERROR;
}
case 1035: {
- n = data.readInt32();
+ const int newConfigId = data.readInt32();
mDebugDisplayConfigSetByBackdoor = false;
- const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size();
- if (n >= 0 && n < numConfigs) {
+ const auto displayId = getInternalDisplayId();
+ if (!displayId) {
+ ALOGE("No internal display found.");
+ return NO_ERROR;
+ }
+ const auto numConfigs = getHwComposer().getConfigs(*displayId).size();
+ if (newConfigId >= 0 && newConfigId < numConfigs) {
const auto displayToken = getInternalDisplayToken();
- status_t result = setActiveConfig(displayToken, n);
+ status_t result = setActiveConfig(displayToken, newConfigId);
if (result != NO_ERROR) {
return result;
}
@@ -5340,13 +5373,10 @@
auto inUid = static_cast<uid_t>(data.readInt32());
const auto refreshRate = data.readFloat();
- mRefreshRateConfigs->setPreferredRefreshRateForUid(
- FrameRateOverride{inUid, refreshRate});
- const auto mappings = mRefreshRateConfigs->getFrameRateOverrides();
- mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId,
- std::move(mappings));
- }
+ mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
+ mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId);
return NO_ERROR;
+ }
}
}
return err;
@@ -6355,10 +6385,7 @@
}
int SurfaceFlinger::getGPUContextPriority() {
- // TODO(b/168740533): This is a proof of concept. Once REAL time priority is available
- // in EGL, we can return it in RenderEngine and propagate it to SurfaceFlinger. Until
- // then return IntentFilter.SYSTEM_HIGH_PRIORITY.
- return 1000;
+ return getRenderEngine().getContextPriority();
}
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 542ba98..db75312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -341,7 +341,7 @@
virtual uint32_t setClientStateLocked(
int64_t frameTimelineVsyncId, const ComposerState& composerState,
- int64_t desiredPresentTime, int64_t postTime, bool privileged,
+ int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
REQUIRES(mStateLock);
virtual void commitTransactionLocked();
@@ -434,8 +434,9 @@
struct TransactionState {
TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates,
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- int64_t postTime, bool privileged, bool hasListenerCallbacks,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged,
+ bool hasListenerCallbacks,
std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
int originUid, uint64_t transactionId)
: frameTimelineVsyncId(frameTimelineVsyncId),
@@ -443,6 +444,7 @@
displays(displayStates),
flags(transactionFlags),
desiredPresentTime(desiredPresentTime),
+ isAutoTimestamp(isAutoTimestamp),
buffer(uncacheBuffer),
postTime(postTime),
privileged(privileged),
@@ -457,6 +459,7 @@
Vector<DisplayState> displays;
uint32_t flags;
const int64_t desiredPresentTime;
+ const bool isAutoTimestamp;
client_cache_t buffer;
const int64_t postTime;
bool privileged;
@@ -520,8 +523,8 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- bool hasListenerCallbacks,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) override;
void bootFinished() override;
@@ -641,6 +644,8 @@
void repaintEverythingForHWC() override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
+ // Called when the frame rate override list changed to trigger an event.
+ void triggerOnFrameRateOverridesChanged() override;
// Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
void toggleKernelIdleTimer();
// Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
@@ -721,7 +726,7 @@
void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime,
+ const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
bool privileged, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bc487ac..4a75180 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -56,11 +56,11 @@
}
std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ Fps currentRefreshRate) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
- return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::WorkDuration>(currentRefreshRate);
} else {
- return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+ return std::make_unique<scheduler::impl::PhaseOffsets>(currentRefreshRate);
}
}
@@ -136,8 +136,8 @@
}
std::unique_ptr<frametimeline::FrameTimeline> DefaultFactory::createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) {
- return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats);
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) {
+ return std::make_unique<frametimeline::impl::FrameTimeline>(timeStats, surfaceFlingerPid);
}
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index fc45fa9..24148dd 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,7 @@
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
- const scheduler::RefreshRateConfigs&) override;
+ Fps currentRefreshRate) override;
std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) override;
sp<SurfaceInterceptor> createSurfaceInterceptor() override;
@@ -56,7 +56,7 @@
sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
std::unique_ptr<FrameTracer> createFrameTracer() override;
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) override;
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index deb1b52..885297f 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,6 +16,8 @@
#pragma once
+#include "Fps.h"
+
#include <cutils/compiler.h>
#include <utils/StrongPointer.h>
@@ -76,7 +78,7 @@
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
- const scheduler::RefreshRateConfigs&) = 0;
+ Fps currentRefreshRate) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&,
ISchedulerCallback&) = 0;
virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
@@ -108,7 +110,7 @@
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) = 0;
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
protected:
~Factory() = default;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 2405884..f4a0319 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -680,19 +680,18 @@
static void updateJankPayload(T& t, int32_t reasons) {
t.jankPayload.totalFrames++;
- static const constexpr int32_t kValidJankyReason =
- JankType::SurfaceFlingerDeadlineMissed |
- JankType::SurfaceFlingerGpuDeadlineMissed |
- JankType::AppDeadlineMissed | JankType::Display;
+ static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::DisplayHAL;
if (reasons & kValidJankyReason) {
t.jankPayload.totalJankyFrames++;
- if ((reasons & JankType::SurfaceFlingerDeadlineMissed) != 0) {
+ if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
t.jankPayload.totalSFLongCpu++;
}
if ((reasons & JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
t.jankPayload.totalSFLongGpu++;
}
- if ((reasons & JankType::Display) != 0) {
+ if ((reasons & JankType::DisplayHAL) != 0) {
t.jankPayload.totalSFUnattributed++;
}
if ((reasons & JankType::AppDeadlineMissed) != 0) {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 5cbf2ef..b38032d 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -907,6 +907,7 @@
}
void FakeComposerClient::onSurfaceFlingerStop() {
+ mSurfaceComposer->enableVSyncInjections(false);
mSurfaceComposer->dispose();
mSurfaceComposer.clear();
}
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 538b10d..a24aeba 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -27,6 +27,7 @@
#include "FakeComposerUtils.h"
#include "MockComposerHal.h"
+#include <binder/Parcel.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
@@ -870,6 +871,25 @@
using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
+// Tests that VSYNC injection can be safely toggled while invalidating.
+TEST_F(DisplayTest_2_1, VsyncInjection) {
+ const auto flinger = ComposerService::getComposerService();
+ bool enable = true;
+
+ for (int i = 0; i < 100; i++) {
+ flinger->enableVSyncInjections(enable);
+ enable = !enable;
+
+ constexpr uint32_t kForceInvalidate = 1004;
+ android::Parcel data, reply;
+ data.writeInterfaceToken(String16("android.ui.ISurfaceComposer"));
+ EXPECT_EQ(NO_ERROR,
+ android::IInterface::asBinder(flinger)->transact(kForceInvalidate, data, &reply));
+
+ std::this_thread::sleep_for(5ms);
+ }
+}
+
TEST_F(DisplayTest_2_1, HotplugOneConfig) {
Test_HotplugOneConfig();
}
diff --git a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
index 36e24d2..e890a62 100644
--- a/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
+++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h
@@ -37,6 +37,7 @@
FAKE_DURATION_OFFSET_NS}};
}
+ void reset() override {}
void setRefreshRateFps(Fps) override {}
void dump(std::string&) const override {}
};
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 43b5afe..4b897fa 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -26,6 +26,7 @@
#include <cinttypes>
using namespace std::chrono_literals;
+using testing::AtLeast;
using testing::Contains;
using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent;
using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame;
@@ -62,7 +63,8 @@
void SetUp() override {
mTimeStats = std::make_shared<mock::TimeStats>();
- mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid,
+ kTestThresholds);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
@@ -110,7 +112,8 @@
SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) {
std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex);
- return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]);
+ return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]
+ ->getSurfaceFrames()[surfaceFrameIdx]);
}
std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) {
@@ -123,7 +126,7 @@
a.presentTime == b.presentTime;
}
- const std::unordered_map<int64_t, TimelineItem>& getPredictions() {
+ const std::map<int64_t, TokenManagerPrediction>& getPredictions() {
return mTokenManager->mPredictions;
}
@@ -138,6 +141,16 @@
FenceToFenceTimeMap fenceFactory;
uint32_t* maxDisplayFrames;
nsecs_t maxTokenRetentionTime;
+ pid_t mSurfaceFlingerPid = 666;
+ static constexpr nsecs_t kPresentThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr nsecs_t kDeadlineThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr nsecs_t kStartThreshold =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count();
+ static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold,
+ kDeadlineThreshold,
+ kStartThreshold};
};
static const std::string sLayerNameOne = "layer1";
@@ -162,55 +175,58 @@
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidTwo, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
flushTokens(systemTime() + maxTokenRetentionTime);
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1, SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flush by calling setSfPresent for the next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
auto& droppedSurfaceFrame = getSurfaceFrame(0, 0);
@@ -219,48 +235,50 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
auto surfaceFrame2 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
- sLayerNameTwo, surfaceFrameToken2);
- mFrameTimeline->setSfWakeUp(sfToken1, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
- mFrameTimeline->addSurfaceFrame(surfaceFrame2,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameTwo, sLayerNameTwo);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
mFrameTimeline->setSfPresent(26, presentFence1);
auto displayFrame = getDisplayFrame(0);
- SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
- SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 1);
presentFence1->signalForTest(42);
// Fences haven't been flushed yet, so it should be 0
- EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
- EXPECT_EQ(surfaceFrame1->getToken(), surfaceFrameToken1);
- EXPECT_EQ(surfaceFrame2->getToken(), surfaceFrameToken2);
-
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto surfaceFrame3 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken2);
- mFrameTimeline->setSfWakeUp(sfToken2, 52);
- mFrameTimeline->addSurfaceFrame(surfaceFrame3, SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame3);
mFrameTimeline->setSfPresent(56, presentFence2);
displayFrame = getDisplayFrame(0);
// Fences have flushed, so the present timestamps should be updated
- EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42);
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42);
EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
@@ -270,6 +288,12 @@
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
// Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
int frameTimeFactor = 0;
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames));
for (size_t i = 0; i < *maxDisplayFrames; i++) {
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions(
@@ -277,11 +301,11 @@
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
frameTimeFactor += 30;
@@ -289,8 +313,7 @@
auto displayFrame0 = getDisplayFrame(0);
// The 0th Display Frame should have actuals 22, 27, 32
- EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)),
- true);
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(22, 27, 32)), true);
// Add one more display frame
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -299,51 +322,54 @@
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
- mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
- mFrameTimeline->addSurfaceFrame(surfaceFrame, SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
presentFence->signalForTest(32 + frameTimeFactor);
displayFrame0 = getDisplayFrame(0);
// The window should have slided by 1 now and the previous 0th display frame
// should have been removed from the deque
- EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)),
- true);
+ EXPECT_EQ(compareTimelineItems(displayFrame0->getActuals(), TimelineItem(52, 57, 62)), true);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
- "acquireFenceAfterQueue", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, 0,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue");
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
presentFence->signalForTest(2);
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -351,15 +377,18 @@
// Increase the size to 256
mFrameTimeline->setMaxDisplayFrames(256);
EXPECT_EQ(*maxDisplayFrames, 256);
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
@@ -367,26 +396,30 @@
// Shrink the size to 128
mFrameTimeline->setMaxDisplayFrames(128);
EXPECT_EQ(*maxDisplayFrames, 128);
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_))
+ .Times(static_cast<int32_t>(*maxDisplayFrames + 10));
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
- mFrameTimeline->setSfWakeUp(sfToken, 22);
- mFrameTimeline->addSurfaceFrame(surfaceFrame,
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(sfToken, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
mFrameTimeline->setSfPresent(27, presentFence);
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
}
+// Tests related to TimeStats
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(sUidOne, sLayerNameOne,
- HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+ HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(HasBit(JankType::SurfaceFlingerDeadlineMissed)));
+ incrementJankyFrames(HasBit(JankType::SurfaceFlingerCpuDeadlineMissed)));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
{std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -397,12 +430,13 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count());
@@ -412,8 +446,9 @@
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::Display)));
- EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::Display)));
+ incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::DisplayHAL)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::DisplayHAL)));
+
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
{std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
@@ -424,18 +459,18 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
presentFence1);
- EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
- EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::Display) != 0);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL);
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
@@ -449,26 +484,26 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
- {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
- std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
surfaceFrame1->setAcquireFenceTime(
std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
mFrameTimeline->setSfWakeUp(sfToken1,
- std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ 11);
- mFrameTimeline->addSurfaceFrame(surfaceFrame1,
- SurfaceFrame::PresentState::Presented);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
presentFence1->signalForTest(
std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
- mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(),
presentFence1);
- EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt);
- EXPECT_TRUE((surfaceFrame1->getJankType().value() & JankType::AppDeadlineMissed) != 0);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed);
}
/*
@@ -481,23 +516,26 @@
*/
TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
@@ -506,25 +544,29 @@
TEST_F(FrameTimelineTest, tracing_sanityTest) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(token1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token2, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(token2, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -543,6 +585,8 @@
TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -550,13 +594,13 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
// Set up the display frame
- mFrameTimeline->setSfWakeUp(-1, 20);
+ mFrameTimeline->setSfWakeUp(-1, 20, 11);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token1, 50);
+ mFrameTimeline->setSfWakeUp(token1, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
@@ -572,24 +616,27 @@
TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, std::nullopt);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(std::nullopt, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(token1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->setSfWakeUp(token1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(25, presentFence1);
presentFence1->signalForTest(30);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(token2, 50);
+ mFrameTimeline->setSfWakeUp(token2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(60);
@@ -662,6 +709,8 @@
TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -670,7 +719,7 @@
int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
mFrameTimeline->setSfPresent(26, presentFence1);
presentFence1->signalForTest(31);
@@ -687,7 +736,7 @@
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+ mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -713,6 +762,10 @@
TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) {
auto tracingSession = getTracingSessionForTest();
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -722,8 +775,8 @@
int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
- mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
- sLayerNameOne, surfaceFrameToken);
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
surfaceFrame1->setActualStartTime(0);
surfaceFrame1->setActualQueueTime(15);
surfaceFrame1->setAcquireFenceTime(20);
@@ -743,15 +796,15 @@
protoSurfaceFrame.set_pid(sPidOne);
// Set up the display frame
- mFrameTimeline->setSfWakeUp(displayFrameToken1, 20);
- mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
- SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
mFrameTimeline->setSfPresent(26, presentFence1);
- presentFence1->signalForTest(31);
+ presentFence1->signalForTest(40);
// Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the
// next frame
- mFrameTimeline->setSfWakeUp(displayFrameToken2, 50);
+ mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11);
mFrameTimeline->setSfPresent(55, presentFence2);
presentFence2->signalForTest(55);
@@ -775,4 +828,508 @@
validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame);
}
+// Tests for Jank classification
+TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ auto& presentedSurfaceFrame = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(29);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 29);
+ EXPECT_EQ(presentedSurfaceFrame.getActuals().presentTime, 29);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::None);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ displayFrame = getDisplayFrame(0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ auto displayFrame2 = getDisplayFrame(1);
+ presentFence2->signalForTest(75);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame2 = getDisplayFrame(1);
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 75);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+
+ mFrameTimeline->setSfPresent(22, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(28);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 28);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ mFrameTimeline->setSfWakeUp(sfToken1, 12, 11);
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame = getDisplayFrame(0);
+ presentFence1->signalForTest(52);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+ displayFrame = getDisplayFrame(0);
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 52);
+ EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(30);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 30);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 30);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::SurfaceFlingerScheduling);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(65);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 65);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 65);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 40});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(36);
+ mFrameTimeline->setSfWakeUp(sfToken2, 52, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::DisplayHAL);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(86);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 86);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 86);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::PredictionError);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_));
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken1, 42, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(46, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(50);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 50);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 50);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::EarlyPresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::Unknown);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
+ // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+ // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
+ // jank to the SurfaceFrame.
+
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(26);
+ mFrameTimeline->setSfWakeUp(sfToken1, 32, 11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(36, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(40);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(40);
+ mFrameTimeline->setSfWakeUp(sfToken2, 43, 11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(56, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 40);
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 40);
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ presentFence2->signalForTest(60);
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 60);
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 60);
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+}
+
+TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
+ // Global increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2);
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
+
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({52, 56, 60});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken1, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame1->setAcquireFenceTime(50);
+ mFrameTimeline->setSfWakeUp(sfToken1, 52, 30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(56, presentFence1);
+ auto displayFrame1 = getDisplayFrame(0);
+ auto& presentedSurfaceFrame1 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(60);
+
+ // Fences for the first frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 0);
+ auto actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.presentTime, 0);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(surfaceFrameToken2, sPidOne, sUidOne,
+ sLayerNameOne, sLayerNameOne);
+ surfaceFrame2->setAcquireFenceTime(84);
+ mFrameTimeline->setSfWakeUp(sfToken2, 112, 30);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(116, presentFence2);
+ auto displayFrame2 = getDisplayFrame(1);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(1, 0);
+ presentFence2->signalForTest(120);
+
+ // Fences for the first frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame1->getActuals().presentTime, 60);
+ actuals1 = presentedSurfaceFrame1.getActuals();
+ EXPECT_EQ(actuals1.endTime, 50);
+ EXPECT_EQ(actuals1.presentTime, 60);
+
+ EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame1->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed);
+
+ // Fences for the second frame haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 0);
+ auto actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences for the second frame have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame2->getActuals().presentTime, 120);
+ actuals2 = presentedSurfaceFrame2.getActuals();
+ EXPECT_EQ(actuals2.presentTime, 120);
+
+ EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent);
+ EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish);
+ EXPECT_EQ(displayFrame2->getJankType(), JankType::None);
+
+ EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
+ EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::AppDeadlineMissed | JankType::BufferStuffing);
+}
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 53dfe3f..8208b3f 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -69,6 +69,7 @@
~MockTokenManager() override = default;
MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
+ MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
};
class MessageQueueTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 83ad737..29be5ab 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1545,38 +1545,112 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
-TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
- auto refreshRateConfigs =
- std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
- /*currentConfigId=*/HWC_CONFIG_ID_30);
- EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
-}
-
TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_30);
- const uid_t uid = 1234;
- refreshRateConfigs->setPreferredRefreshRateForUid({uid, 30});
- EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ const auto frameRate = Fps(30.f);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDivider(frameRate));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
- EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDivider(frameRate));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
- EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ EXPECT_EQ(0, refreshRateConfigs->getRefreshRateDivider(frameRate));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDivider(frameRate));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(frameRate));
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.5f});
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
- refreshRateConfigs->setPreferredRefreshRateForUid({uid, 22.6f});
- EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.5f)));
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDivider(Fps(22.6f)));
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_noLayers) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120);
+
+ auto layers = std::vector<LayerRequirement>{};
+ ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f)).empty());
+}
+
+TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].name = "Test layer";
+ layers[0].ownerUid = 1234;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[0].vote = LayerVoteType::NoVote;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Min;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Max;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_TRUE(frameRateOverrides.empty());
+
+ layers[0].vote = LayerVoteType::Heuristic;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_TRUE(frameRateOverrides.empty());
+}
+
+TEST_F(RefreshRateConfigsTest, populatePreferredFrameRate_twoUids) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
+ HWC_CONFIG_ID_120);
+
+ auto layers = std::vector<LayerRequirement>{
+ LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
+ LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
+ };
+
+ layers[0].name = "Test layer 1234";
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+
+ layers[1].name = "Test layer 5678";
+ layers[1].desiredRefreshRate = Fps(30.0f);
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+
+ ASSERT_EQ(2, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(1, frameRateOverrides.count(5678));
+ ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+
+ layers[1].vote = LayerVoteType::Heuristic;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_EQ(1, frameRateOverrides.size());
+ ASSERT_EQ(1, frameRateOverrides.count(1234));
+ ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+
+ layers[1].ownerUid = 1234;
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f));
+ ASSERT_TRUE(frameRateOverrides.empty());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index c47b141..1bbe8e2 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -432,7 +432,7 @@
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
}
-TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+TEST_P(SetFrameRateTest, SetAndGetReparentChildren) {
EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 7f05a75..25aaa14 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -78,7 +78,7 @@
}
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
- const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
+ Fps /*currentRefreshRate*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -156,8 +156,8 @@
}
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
- std::shared_ptr<TimeStats> timeStats) override {
- return std::make_unique<mock::FrameTimeline>(timeStats);
+ std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid = 0) override {
+ return std::make_unique<mock::FrameTimeline>(timeStats, surfaceFlingerPid);
}
using CreateBufferQueueFunction =
@@ -231,8 +231,7 @@
mFlinger->mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
/*powerMode=*/hal::PowerMode::OFF);
- mFlinger->mVsyncConfiguration =
- mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs);
+ mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(currFps);
mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
@@ -372,14 +371,14 @@
const Vector<DisplayState>& displays, uint32_t flags,
const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
- int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
- bool hasListenerCallbacks,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) {
return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags,
applyToken, inputWindowCommands, desiredPresentTime,
- uncacheBuffer, hasListenerCallbacks, listenerCallbacks,
- transactionId);
+ isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+ listenerCallbacks, transactionId);
}
auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
@@ -692,6 +691,7 @@
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {}
void repaintEverythingForHWC() override {}
void kernelTimerChanged(bool) override {}
+ void triggerOnFrameRateOverridesChanged() {}
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index bbcc0c9..ace370f 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -356,9 +356,9 @@
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
- mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(JankType::Display);
+ mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::None);
@@ -383,10 +383,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -848,10 +848,10 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
@@ -987,9 +987,9 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
- mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(JankType::Display);
+ mTimeStats->incrementJankyFrames(JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(JankType::None);
@@ -1062,10 +1062,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
- JankType::SurfaceFlingerDeadlineMissed);
+ JankType::SurfaceFlingerCpuDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerGpuDeadlineMissed);
- mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
JankType::AppDeadlineMissed);
mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index c36d994..fa6ff30 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -97,7 +97,8 @@
uint32_t flags = 0;
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
InputWindowCommands inputWindowCommands;
- int64_t desiredPresentTime = -1;
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID;
client_cache_t uncacheBuffer;
int64_t id = -1;
@@ -114,11 +115,13 @@
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
- int64_t desiredPresentTime, int64_t frameTimelineVsyncId) {
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ int64_t frameTimelineVsyncId) {
mTransactionNumber++;
transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
transaction.desiredPresentTime = desiredPresentTime;
+ transaction.isAutoTimestamp = isAutoTimestamp;
transaction.frameTimelineVsyncId = frameTimelineVsyncId;
}
@@ -129,13 +132,15 @@
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationTime = systemTime();
mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, transaction.id);
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
// This transaction should not have been placed on the transaction queue.
// If transaction is synchronous or syncs input windows, SF
@@ -164,13 +169,15 @@
.WillOnce(Return(time + nsecs_t(5 * 1e8)));
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
- /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+ /*desiredPresentTime*/ time + s2ns(1), false,
+ ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationSentTime = systemTime();
mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states,
transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
- transaction.desiredPresentTime, transaction.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, transaction.id);
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
@@ -189,20 +196,23 @@
// transaction that should go on the pending thread
TransactionInfo transactionA;
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+ /*desiredPresentTime*/ time + s2ns(1), false,
+ ISurfaceComposer::INVALID_VSYNC_ID);
// transaction that would not have gone on the pending thread if not
// blocked
TransactionInfo transactionB;
setupSingle(transactionB, flags, syncInputWindows,
- /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID);
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ ISurfaceComposer::INVALID_VSYNC_ID);
nsecs_t applicationSentTime = systemTime();
mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
transactionA.displays, transactionA.flags,
transactionA.applyToken, transactionA.inputWindowCommands,
- transactionA.desiredPresentTime, transactionA.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, transactionA.id);
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -213,8 +223,9 @@
mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states,
transactionB.displays, transactionB.flags,
transactionB.applyToken, transactionB.inputWindowCommands,
- transactionB.desiredPresentTime, transactionB.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, transactionB.id);
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -256,12 +267,12 @@
.WillOnce(Return(s2ns(2)));
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
- /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID);
+ /*desiredPresentTime*/ s2ns(1), false, ISurfaceComposer::INVALID_VSYNC_ID);
mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states,
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
- transactionA.id);
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+ mHasListenerCallbacks, mCallbacks, transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_EQ(1, transactionQueue.size());
@@ -279,8 +290,8 @@
empty.applyToken = sp<IBinder>();
mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays,
empty.flags, empty.applyToken, empty.inputWindowCommands,
- empty.desiredPresentTime, empty.uncacheBuffer,
- mHasListenerCallbacks, mCallbacks, empty.id);
+ empty.desiredPresentTime, empty.isAutoTimestamp,
+ empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
// flush transaction queue should flush as desiredPresentTime has
// passed
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 2a35f69..bb7578d 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -19,6 +19,7 @@
#include <gmock/gmock.h>
#include <log/log.h>
+#include <chrono>
#include <thread>
#include "Scheduler/VsyncConfiguration.h"
@@ -27,14 +28,15 @@
namespace android::scheduler {
+using namespace std::chrono_literals;
+
class TestableWorkDuration : public impl::WorkDuration {
public:
TestableWorkDuration(Fps currentFps, nsecs_t sfDuration, nsecs_t appDuration,
nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
- : impl::WorkDuration({Fps(60.0f), Fps(90.0f)}, currentFps, sfDuration, appDuration,
- sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
- appEarlyGlDuration) {}
+ : impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration,
+ appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {}
};
class WorkDurationTest : public testing::Test {
@@ -171,9 +173,9 @@
std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
nsecs_t thresholdForNextVsync)
- : impl::PhaseOffsets({Fps(60.0f), Fps(90.0f)}, Fps(60.0f), vsyncPhaseOffsetNs,
- sfVSyncPhaseOffsetNs, earlySfOffsetNs, earlyGpuSfOffsetNs,
- earlyAppOffsetNs, earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
+ : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
+ earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
+ earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
index f784df3..ff005a0 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.cpp
@@ -19,8 +19,8 @@
namespace android::mock {
// Explicit default instantiation is recommended.
-FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
- : android::frametimeline::impl::FrameTimeline(timeStats) {}
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid)
+ : android::frametimeline::impl::FrameTimeline(timeStats, surfaceFlingerPid) {}
FrameTimeline::~FrameTimeline() = default;
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 6b12536..0a6a9f4 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -26,14 +26,12 @@
// No need to create mocks for SurfaceFrame and TokenManager yet. They are very small components
// and do not have external dependencies like perfetto.
public:
- FrameTimeline(std::shared_ptr<TimeStats> timeStats);
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid);
~FrameTimeline();
MOCK_METHOD0(onBootFinished, void());
- MOCK_METHOD2(addSurfaceFrame,
- void(std::shared_ptr<frametimeline::SurfaceFrame>,
- frametimeline::SurfaceFrame::PresentState));
- MOCK_METHOD2(setSfWakeUp, void(int64_t, nsecs_t));
+ MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
+ MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, nsecs_t));
MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 078d8e07..ba2e4db 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -33,6 +33,7 @@
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+ MOCK_CONST_METHOD0(getOwnerUid, uid_t());
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index efaa9fa..453c93a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,7 @@
~MessageQueue() override;
MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
- MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
+ MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
MOCK_METHOD0(waitMessage, void());
MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
MOCK_METHOD0(invalidate, void());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 72bc89c..ab19886 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -29,6 +29,7 @@
scheduler::RefreshRateConfigEvent));
MOCK_METHOD0(repaintEverythingForHWC, void());
MOCK_METHOD1(kernelTimerChanged, void(bool));
+ MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -37,6 +38,7 @@
scheduler::RefreshRateConfigEvent) override {}
void repaintEverythingForHWC() override {}
void kernelTimerChanged(bool) override {}
+ void triggerOnFrameRateOverridesChanged() {}
};
} // namespace android::mock