diff options
25 files changed, 539 insertions, 76 deletions
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index ba8b02d597..9d2c79139f 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -60,6 +60,27 @@ __BEGIN_DECLS struct APerformanceHintManager; struct APerformanceHintSession; +struct AWorkDuration; + +/** + * {@link AWorkDuration} is an opaque type that represents the breakdown of the + * actual workload duration in each component internally. + * + * A new {@link AWorkDuration} can be obtained using + * {@link AWorkDuration_create()}, when the client finishes using + * {@link AWorkDuration}, {@link AWorkDuration_release()} must be + * called to destroy and free up the resources associated with + * {@link AWorkDuration}. + * + * This file provides a set of functions to allow clients to set the measured + * work duration of each component on {@link AWorkDuration}. + * + * - AWorkDuration_setWorkPeriodStartTimestampNanos() + * - AWorkDuration_setActualTotalDurationNanos() + * - AWorkDuration_setActualCpuDurationNanos() + * - AWorkDuration_setActualGpuDurationNanos() + */ +typedef struct AWorkDuration AWorkDuration; /** * An opaque type representing a handle to a performance hint manager. @@ -102,7 +123,7 @@ typedef struct APerformanceHintSession APerformanceHintSession; * * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); +APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); /** * Creates a session for the given set of threads and sets their initial target work @@ -116,9 +137,9 @@ APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID * This must be positive if using the work duration API, or 0 otherwise. * @return APerformanceHintManager instance on success, nullptr on failure. */ -APerformanceHintSession* APerformanceHint_createSession( - APerformanceHintManager* manager, - const int32_t* threadIds, size_t size, +APerformanceHintSession* _Nullable APerformanceHint_createSession( + APerformanceHintManager* _Nonnull manager, + const int32_t* _Nonnull threadIds, size_t size, int64_t initialTargetWorkDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -128,7 +149,7 @@ APerformanceHintSession* APerformanceHint_createSession( * @return the preferred update rate supported by device software. */ int64_t APerformanceHint_getPreferredUpdateRateNanos( - APerformanceHintManager* manager) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintManager* _Nonnull manager) __INTRODUCED_IN(__ANDROID_API_T__); /** * Updates this session's target duration for each cycle of work. @@ -140,7 +161,7 @@ int64_t APerformanceHint_getPreferredUpdateRateNanos( * EPIPE if communication with the system service has failed. */ int APerformanceHint_updateTargetWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t targetDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -157,7 +178,7 @@ int APerformanceHint_updateTargetWorkDuration( * EPIPE if communication with the system service has failed. */ int APerformanceHint_reportActualWorkDuration( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, int64_t actualDurationNanos) __INTRODUCED_IN(__ANDROID_API_T__); /** @@ -167,7 +188,7 @@ int APerformanceHint_reportActualWorkDuration( * @param session The performance hint session instance to release. */ void APerformanceHint_closeSession( - APerformanceHintSession* session) __INTRODUCED_IN(__ANDROID_API_T__); + APerformanceHintSession* _Nonnull session) __INTRODUCED_IN(__ANDROID_API_T__); /** * Set a list of threads to the performance hint session. This operation will replace @@ -184,8 +205,8 @@ void APerformanceHint_closeSession( * EPERM if any thread id doesn't belong to the application. */ int APerformanceHint_setThreads( - APerformanceHintSession* session, - const pid_t* threadIds, + APerformanceHintSession* _Nonnull session, + const pid_t* _Nonnull threadIds, size_t size) __INTRODUCED_IN(__ANDROID_API_U__); /** @@ -198,11 +219,92 @@ int APerformanceHint_setThreads( * EPIPE if communication with the system service has failed. */ int APerformanceHint_setPreferPowerEfficiency( - APerformanceHintSession* session, + APerformanceHintSession* _Nonnull session, bool enabled) __INTRODUCED_IN(__ANDROID_API_V__); +/** + * Reports the durations for the last cycle of work. + * + * The system will attempt to adjust the scheduling and performance of the + * threads within the thread group to bring the actual duration close to the target duration. + * + * @param session The {@link APerformanceHintSession} instance to update. + * @param workDuration The {@link AWorkDuration} structure of times the thread group took to + * complete its last task in nanoseconds breaking down into different components. + * + * The work period start timestamp, actual total duration and actual CPU duration must be + * positive. + * + * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means + * the actual GPU duration is not measured. + * + * @return 0 on success. + * EINVAL if session is nullptr or any duration is an invalid number. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_reportActualWorkDuration2( + APerformanceHintSession* _Nonnull session, + AWorkDuration* _Nonnull workDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Creates a new AWorkDuration. When the client finishes using {@link AWorkDuration}, it should + * call {@link AWorkDuration_release()} to destroy {@link AWorkDuration} and release all resources + * associated with it. + * + * @return AWorkDuration on success and nullptr otherwise. + */ +AWorkDuration* _Nonnull AWorkDuration_create() __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Destroys {@link AWorkDuration} and free all resources associated to it. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + */ +void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the work period start timestamp in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param workPeriodStartTimestampNanos The work period start timestamp in nanoseconds based on + * CLOCK_MONOTONIC about when the work starts, the timestamp must be positive. + */ +void AWorkDuration_setWorkPeriodStartTimestampNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t workPeriodStartTimestampNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual total work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualTotalDurationNanos The actual total work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualTotalDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual CPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} + * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds, the number must be + * positive. + */ +void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + +/** + * Sets the actual GPU work duration in nanoseconds. + * + * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}. + * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be + * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is + * measured. + */ +void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, + int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H -/** @} */
\ No newline at end of file +/** @} */ diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index eccd5dbc3a..d73c3a4e8c 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -90,7 +90,6 @@ cc_defaults { "Stability.cpp", "Status.cpp", "TextOutput.cpp", - "Trace.cpp", "Utils.cpp", "file.cpp", ], @@ -251,7 +250,6 @@ cc_library_shared { srcs: [ // Trusty-specific files - "OS_android.cpp", "trusty/OS.cpp", "trusty/RpcServerTrusty.cpp", "trusty/RpcTransportTipcTrusty.cpp", diff --git a/libs/binder/OS.h b/libs/binder/OS.h index c5f0730d6b..0035aeb205 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -24,6 +24,9 @@ namespace android::binder::os { +void trace_begin(uint64_t tag, const char* name); +void trace_end(uint64_t tag); + status_t setNonBlocking(borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp index ad458eb705..155588d7f5 100644 --- a/libs/binder/OS_android.cpp +++ b/libs/binder/OS_android.cpp @@ -17,6 +17,7 @@ #include "OS.h" #include <android-base/threads.h> +#include <cutils/trace.h> #include <utils/misc.h> namespace android::binder::os { @@ -34,4 +35,12 @@ bool report_sysprop_change() { return true; } +void trace_begin(uint64_t tag, const char* name) { + atrace_begin(tag, name); +} + +void trace_end(uint64_t tag) { + atrace_end(tag); +} + } // namespace android::binder::os diff --git a/libs/binder/Trace.cpp b/libs/binder/Trace.cpp deleted file mode 100644 index 1ebfa1a165..0000000000 --- a/libs/binder/Trace.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <binder/Trace.h> -#include <cutils/trace.h> - -namespace android { -namespace binder { - -void atrace_begin(uint64_t tag, const char* name) { - ::atrace_begin(tag, name); -} - -void atrace_end(uint64_t tag) { - ::atrace_end(tag); -} - -} // namespace binder -} // namespace android diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h index 99378428ad..95318b2bf6 100644 --- a/libs/binder/include/binder/Trace.h +++ b/libs/binder/include/binder/Trace.h @@ -16,22 +16,36 @@ #pragma once -#include <cutils/trace.h> #include <stdint.h> +#if __has_include(<cutils/trace.h>) +#include <cutils/trace.h> +#endif + +#ifdef ATRACE_TAG_AIDL +#if ATRACE_TAG_AIDL != (1 << 24) +#error "Mismatched ATRACE_TAG_AIDL definitions" +#endif +#else +#define ATRACE_TAG_AIDL (1 << 24) +#endif + namespace android { namespace binder { +// Forward declarations from internal OS.h +namespace os { // Trampoline functions allowing generated aidls to trace binder transactions without depending on // libcutils/libutils -void atrace_begin(uint64_t tag, const char* name); -void atrace_end(uint64_t tag); +void trace_begin(uint64_t tag, const char* name); +void trace_end(uint64_t tag); +} // namespace os class ScopedTrace { public: - inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); } + inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) { os::trace_begin(mTag, name); } - inline ~ScopedTrace() { atrace_end(mTag); } + inline ~ScopedTrace() { os::trace_end(mTag); } private: uint64_t mTag; diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index ca14286d74..99da1ebc6d 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -31,6 +31,18 @@ using android::binder::unique_fd; namespace android::binder::os { +void trace_begin(uint64_t, const char*) {} + +void trace_end(uint64_t) {} + +uint64_t GetThreadId() { + return 0; +} + +bool report_sysprop_change() { + return false; +} + status_t setNonBlocking(borrowed_fd /*fd*/) { // Trusty IPC syscalls are all non-blocking by default. return OK; diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk index d2b37aa8f6..69737fa102 100644 --- a/libs/binder/trusty/kernel/rules.mk +++ b/libs/binder/trusty/kernel/rules.mk @@ -24,13 +24,13 @@ LIBUTILS_DIR := system/core/libutils FMTLIB_DIR := external/fmtlib MODULE_SRCS := \ + $(LOCAL_DIR)/../OS.cpp \ $(LOCAL_DIR)/../TrustyStatus.cpp \ $(LIBBINDER_DIR)/Binder.cpp \ $(LIBBINDER_DIR)/BpBinder.cpp \ $(LIBBINDER_DIR)/FdTrigger.cpp \ $(LIBBINDER_DIR)/IInterface.cpp \ $(LIBBINDER_DIR)/IResultReceiver.cpp \ - $(LIBBINDER_DIR)/OS_android.cpp \ $(LIBBINDER_DIR)/Parcel.cpp \ $(LIBBINDER_DIR)/Stability.cpp \ $(LIBBINDER_DIR)/Status.cpp \ diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk index dbddbe16e2..96c66a8be4 100644 --- a/libs/binder/trusty/rules.mk +++ b/libs/binder/trusty/rules.mk @@ -34,7 +34,6 @@ MODULE_SRCS := \ $(LIBBINDER_DIR)/FdTrigger.cpp \ $(LIBBINDER_DIR)/IInterface.cpp \ $(LIBBINDER_DIR)/IResultReceiver.cpp \ - $(LIBBINDER_DIR)/OS_android.cpp \ $(LIBBINDER_DIR)/Parcel.cpp \ $(LIBBINDER_DIR)/ParcelFileDescriptor.cpp \ $(LIBBINDER_DIR)/RpcServer.cpp \ diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h index bf354e7bb4..1fc80c30d7 100644 --- a/libs/gui/include/gui/JankInfo.h +++ b/libs/gui/include/gui/JankInfo.h @@ -18,7 +18,7 @@ namespace android { -// Jank information tracked by SurfaceFlinger(SF) for perfetto tracing and telemetry. +// Jank type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. enum JankType { // No Jank None = 0x0, @@ -50,4 +50,16 @@ enum JankType { Dropped = 0x200, }; +// Jank severity type tracked by SurfaceFlinger(SF) for Perfetto tracing and telemetry. +enum class JankSeverityType { + // Unknown: not enough information to classify the severity of a jank + Unknown = 0, + // None: no jank + None = 1, + // Partial: jank caused by missing the deadline by less than the app's frame interval + Partial = 2, + // Full: jank caused by missing the deadline by more than the app's frame interval + Full = 3, +}; + } // namespace android diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index e41aa06541..a98ea86073 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1057,7 +1057,12 @@ enum { /** * This surface will vote for the minimum refresh rate. */ - ANATIVEWINDOW_FRAME_RATE_MIN + ANATIVEWINDOW_FRAME_RATE_MIN, + + /** + * The surface requests a frame rate that is greater than or equal to `frameRate`. + */ + ANATIVEWINDOW_FRAME_RATE_GTE }; /* diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index 37ebfc4617..2ec6d18fda 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -518,14 +518,16 @@ status_t Gralloc5Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32 } } { - auto value = - getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper, - bufferHandle); - if (static_cast<::aidl::android::hardware::graphics::common::PixelFormat>(format) != - value) { - ALOGW("Format didn't match, expected %d got %s", format, - value.has_value() ? toString(*value).c_str() : "<null>"); - return BAD_VALUE; + auto expected = static_cast<APixelFormat>(format); + if (expected != APixelFormat::IMPLEMENTATION_DEFINED) { + auto value = + getStandardMetadata<StandardMetadataType::PIXEL_FORMAT_REQUESTED>(mMapper, + bufferHandle); + if (expected != value) { + ALOGW("Format didn't match, expected %d got %s", format, + value.has_value() ? toString(*value).c_str() : "<null>"); + return BAD_VALUE; + } } } { diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 8b16890a45..1f72e8ba2c 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { "PowerHalWrapper.cpp", "PowerSaveState.cpp", "Temperature.cpp", + "WorkDuration.cpp", "WorkSource.cpp", ":libpowermanager_aidl", ], diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp new file mode 100644 index 0000000000..ef723c229c --- /dev/null +++ b/services/powermanager/WorkDuration.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "WorkDuration" + +#include <android/WorkDuration.h> +#include <android/performance_hint.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos, + int64_t cpuDurationNanos, int64_t gpuDurationNanos) + : workPeriodStartTimestampNanos(startTimestampNanos), + actualTotalDurationNanos(totalDurationNanos), + actualCpuDurationNanos(cpuDurationNanos), + actualGpuDurationNanos(gpuDurationNanos) {} + +status_t WorkDuration::writeToParcel(Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + parcel->writeInt64(workPeriodStartTimestampNanos); + parcel->writeInt64(actualTotalDurationNanos); + parcel->writeInt64(actualCpuDurationNanos); + parcel->writeInt64(actualGpuDurationNanos); + parcel->writeInt64(timestampNanos); + return OK; +} + +status_t WorkDuration::readFromParcel(const Parcel*) { + return INVALID_OPERATION; +} + +} // namespace android::os diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h new file mode 100644 index 0000000000..99b5b8b1b4 --- /dev/null +++ b/services/powermanager/include/android/WorkDuration.h @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Parcelable.h> +#include <math.h> + +struct AWorkDuration {}; + +namespace android::os { + +/** + * C++ Parcelable version of {@link PerformanceHintManager.WorkDuration} that can be used in + * binder calls. + * This file needs to be kept in sync with the WorkDuration in + * frameworks/base/core/java/android/os/WorkDuration.java + */ +struct WorkDuration : AWorkDuration, android::Parcelable { + WorkDuration() = default; + ~WorkDuration() = default; + + WorkDuration(int64_t workPeriodStartTimestampNanos, int64_t actualTotalDurationNanos, + int64_t actualCpuDurationNanos, int64_t actualGpuDurationNanos); + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + inline bool equalsWithoutTimestamp(const WorkDuration& other) const { + return workPeriodStartTimestampNanos == other.workPeriodStartTimestampNanos && + actualTotalDurationNanos == other.actualTotalDurationNanos && + actualCpuDurationNanos == other.actualCpuDurationNanos && + actualGpuDurationNanos == other.actualGpuDurationNanos; + } + + bool operator==(const WorkDuration& other) const { + return timestampNanos == other.timestampNanos && equalsWithoutTimestamp(other); + } + + bool operator!=(const WorkDuration& other) const { return !(*this == other); } + + friend std::ostream& operator<<(std::ostream& os, const WorkDuration& workDuration) { + os << "{" + << "workPeriodStartTimestampNanos: " << workDuration.workPeriodStartTimestampNanos + << ", actualTotalDurationNanos: " << workDuration.actualTotalDurationNanos + << ", actualCpuDurationNanos: " << workDuration.actualCpuDurationNanos + << ", actualGpuDurationNanos: " << workDuration.actualGpuDurationNanos + << ", timestampNanos: " << workDuration.timestampNanos << "}"; + return os; + } + + int64_t workPeriodStartTimestampNanos; + int64_t actualTotalDurationNanos; + int64_t actualCpuDurationNanos; + int64_t actualGpuDurationNanos; + int64_t timestampNanos; +}; + +} // namespace android::os diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index f00ef671ad..e005ad3e03 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -251,7 +251,12 @@ void PowerAdvisor::reportActualWorkDuration() { actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin); mActualDuration = actualDuration; WorkDuration duration; + duration.workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(); + // TODO(b/284324521): Correctly calculate total duration. duration.durationNanos = actualDuration->ns(); + duration.cpuDurationNanos = actualDuration->ns(); + // TODO(b/284324521): Calculate RenderEngine GPU time. + duration.gpuDurationNanos = 0; duration.timeStampNanos = TimePoint::now().ns(); mHintSessionQueue.push_back(duration); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 9dc3938322..803299cf6f 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -280,6 +280,19 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) { return protoJank; } +FrameTimelineEvent::JankSeverityType toProto(JankSeverityType jankSeverityType) { + switch (jankSeverityType) { + case JankSeverityType::Unknown: + return FrameTimelineEvent::SEVERITY_UNKNOWN; + case JankSeverityType::None: + return FrameTimelineEvent::SEVERITY_NONE; + case JankSeverityType::Partial: + return FrameTimelineEvent::SEVERITY_PARTIAL; + case JankSeverityType::Full: + return FrameTimelineEvent::SEVERITY_FULL; + } +} + // Returns the smallest timestamp from the set of predictions and actuals. nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, TimelineItem actuals) { @@ -389,6 +402,15 @@ std::optional<int32_t> SurfaceFrame::getJankType() const { return mJankType; } +std::optional<JankSeverityType> SurfaceFrame::getJankSeverityType() const { + std::scoped_lock lock(mMutex); + if (mActuals.presentTime == 0) { + // Frame hasn't been presented yet. + return std::nullopt; + } + return mJankSeverityType; +} + nsecs_t SurfaceFrame::getBaseTime() const { std::scoped_lock lock(mMutex); return getMinTime(mPredictionState, mPredictions, mActuals); @@ -505,10 +527,11 @@ std::string SurfaceFrame::miniDump() const { } void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, - nsecs_t& deadlineDelta) { + Fps displayFrameRenderRate, nsecs_t& deadlineDelta) { if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { // Cannot do any classification for invalid present time. mJankType = JankType::Unknown; + mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } @@ -519,6 +542,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r // reasonable app, so prediction expire would mean a huge scheduling delay. mJankType = mPresentState != PresentState::Presented ? JankType::Dropped : JankType::AppDeadlineMissed; + mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = -1; return; } @@ -543,6 +567,11 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; + // Jank that is missing by less than the render rate period is classified as partial jank, + // otherwise it is a full jank. + mJankSeverityType = std::abs(presentDelta) < displayFrameRenderRate.getPeriodNsecs() + ? JankSeverityType::Partial + : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } @@ -613,6 +642,7 @@ void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& r mJankType = JankType::Dropped; // Since frame was not presented, lets drop any present value mActuals.presentTime = 0; + mJankSeverityType = JankSeverityType::Unknown; } } @@ -625,7 +655,7 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mActuals.presentTime = presentTime; nsecs_t deadlineDelta = 0; - classifyJankLocked(displayFrameJankType, refreshRate, deadlineDelta); + classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, deadlineDelta); if (mPredictionState != PredictionState::None) { // Only update janky frames if the app used vsync predictions @@ -718,6 +748,7 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffse actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualSurfaceFrameStartEvent->set_prediction_type(toProto(mPredictionState)); actualSurfaceFrameStartEvent->set_is_buffer(mIsBuffer); + actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end @@ -910,6 +941,7 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& // Cannot do jank classification with expired predictions or invalid signal times. Set the // deltas to 0 as both negative and positive deltas are used as real values. mJankType = JankType::Unknown; + mJankSeverityType = JankSeverityType::Unknown; deadlineDelta = 0; deltaToVsync = 0; if (!presentTimeValid) { @@ -941,6 +973,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; + // Jank that is missing by less than the render rate period is classified as partial jank, + // otherwise it is a full jank. + mJankSeverityType = std::abs(presentDelta) < mRenderRate.getPeriodNsecs() + ? JankSeverityType::Partial + : JankSeverityType::Full; } else { mFramePresentMetadata = FramePresentMetadata::OnTimePresent; } @@ -1119,6 +1156,7 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs actualDisplayFrameStartEvent->set_prediction_type(toProto(PredictionState::Valid)); actualDisplayFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(JankType::Dropped)); + actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None)); }); // Actual timeline end @@ -1160,6 +1198,7 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, actualDisplayFrameStartEvent->set_gpu_composition(mGpuFence != FenceTime::NO_FENCE); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); actualDisplayFrameStartEvent->set_prediction_type(toProto(mPredictionState)); + actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); // Actual timeline end diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 6598e218fd..b5047a3467 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -168,6 +168,7 @@ public: // Returns std::nullopt if the frame hasn't been classified yet. // Used by both SF and FrameTimeline. std::optional<int32_t> getJankType() const; + std::optional<JankSeverityType> getJankSeverityType() const; // Functions called by SF int64_t getToken() const { return mToken; }; @@ -232,7 +233,7 @@ private: void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, - nsecs_t& deadlineDelta) REQUIRES(mMutex); + Fps displayFrameRenderRate, nsecs_t& deadlineDelta) REQUIRES(mMutex); const int64_t mToken; const int32_t mInputEventId; @@ -252,6 +253,8 @@ private: mutable std::mutex mMutex; // Bitmask for the type of jank int32_t mJankType GUARDED_BY(mMutex) = JankType::None; + // Enum for the severity of jank + JankSeverityType mJankSeverityType GUARDED_BY(mMutex) = JankSeverityType::None; // Indicates if this frame was composited by the GPU or not bool mGpuComposition GUARDED_BY(mMutex) = false; // Refresh rate for this frame. @@ -404,6 +407,7 @@ public: FramePresentMetadata getFramePresentMetadata() const { return mFramePresentMetadata; }; FrameReadyMetadata getFrameReadyMetadata() const { return mFrameReadyMetadata; }; int32_t getJankType() const { return mJankType; } + JankSeverityType getJankSeverityType() const { return mJankSeverityType; } const std::vector<std::shared_ptr<SurfaceFrame>>& getSurfaceFrames() const { return mSurfaceFrames; } @@ -435,6 +439,8 @@ public: PredictionState mPredictionState = PredictionState::None; // Bitmask for the type of jank int32_t mJankType = JankType::None; + // Enum for the severity of jank + JankSeverityType mJankSeverityType = JankSeverityType::None; // A valid gpu fence indicates that the DisplayFrame was composited by the GPU std::shared_ptr<FenceTime> mGpuFence = FenceTime::NO_FENCE; // Enum for the type of present diff --git a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h index 405c982494..d8c408f485 100644 --- a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h +++ b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h @@ -29,6 +29,8 @@ enum class FrameRateCompatibility { ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the // content properly. Any other value will result in a pull down. + Gte, // Layer needs greater than or equal to the frame rate. + NoVote, // Layer doesn't have any requirements for the refresh rate and // should not be considered when the display refresh rate is determined. diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index d309adccf8..9c003026ea 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -278,6 +278,8 @@ void LayerHistory::partitionLayers(nsecs_t now) { return LayerVoteType::NoVote; case Layer::FrameRateCompatibility::Exact: return LayerVoteType::ExplicitExact; + case Layer::FrameRateCompatibility::Gte: + return LayerVoteType::ExplicitGte; } }(); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 38416bf5ed..97fca395b4 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -496,6 +496,8 @@ FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compati return FrameRateCompatibility::Exact; case ANATIVEWINDOW_FRAME_RATE_MIN: return FrameRateCompatibility::Min; + case ANATIVEWINDOW_FRAME_RATE_GTE: + return FrameRateCompatibility::Gte; case ANATIVEWINDOW_FRAME_RATE_NO_VOTE: return FrameRateCompatibility::NoVote; default: diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 6a7063e40f..e06221a43d 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -332,6 +332,15 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer return calculateNonExactMatchingDefaultLayerScoreLocked(displayPeriod, layerPeriod); } + if (layer.vote == LayerVoteType::ExplicitGte) { + using fps_approx_ops::operator>=; + if (refreshRate >= layer.desiredRefreshRate) { + return 1.0f; + } else { + return calculateDistanceScoreLocked(layer.desiredRefreshRate, refreshRate); + } + } + if (layer.vote == LayerVoteType::ExplicitExactOrMultiple || layer.vote == LayerVoteType::Heuristic) { using fps_approx_ops::operator<; @@ -390,13 +399,20 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer return 0; } -float RefreshRateSelector::calculateDistanceScoreFromMax(Fps refreshRate) const { - const auto& maxFps = mAppRequestFrameRates.back().fps; - const float ratio = refreshRate.getValue() / maxFps.getValue(); - // Use ratio^2 to get a lower score the more we get further from peak +float RefreshRateSelector::calculateDistanceScoreLocked(Fps referenceRate, Fps refreshRate) const { + using fps_approx_ops::operator>=; + const float ratio = referenceRate >= refreshRate + ? refreshRate.getValue() / referenceRate.getValue() + : referenceRate.getValue() / refreshRate.getValue(); + // Use ratio^2 to get a lower score the more we get further from the reference rate. return ratio * ratio; } +float RefreshRateSelector::calculateDistanceScoreFromMaxLocked(Fps refreshRate) const { + const auto& maxFps = mAppRequestFrameRates.back().fps; + return calculateDistanceScoreLocked(maxFps, refreshRate); +} + float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, bool isSeamlessSwitch) const { // Slightly prefer seamless switches. @@ -421,7 +437,7 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay // If the layer wants Max, give higher score to the higher refresh rate if (layer.vote == LayerVoteType::Max) { - return calculateDistanceScoreFromMax(refreshRate); + return calculateDistanceScoreFromMaxLocked(refreshRate); } if (layer.vote == LayerVoteType::ExplicitExact) { @@ -489,6 +505,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitDefaultVoteLayers = 0; int explicitExactOrMultipleVoteLayers = 0; int explicitExact = 0; + int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -513,6 +530,9 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi case LayerVoteType::ExplicitExact: explicitExact++; break; + case LayerVoteType::ExplicitGte: + explicitGteLayers++; + break; case LayerVoteType::ExplicitCategory: explicitCategoryVoteLayers++; if (layer.frameRateCategory == FrameRateCategory::NoPreference) { @@ -535,7 +555,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi } const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 || - explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 || + explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 || explicitGteLayers > 0 || explicitCategoryVoteLayers > 0; const Policy* policy = getCurrentPolicyLocked(); @@ -688,6 +708,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi case LayerVoteType::Max: case LayerVoteType::ExplicitDefault: case LayerVoteType::ExplicitExact: + case LayerVoteType::ExplicitGte: case LayerVoteType::ExplicitCategory: return false; } @@ -1081,7 +1102,7 @@ auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt, return; } - float score = calculateDistanceScoreFromMax(frameRateMode.fps); + float score = calculateDistanceScoreFromMaxLocked(frameRateMode.fps); if (ascending) { score = 1.0f / score; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 40e9a8310f..9f6a29cbc9 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -149,6 +149,7 @@ public: // ExactOrMultiple compatibility ExplicitExact, // Specific refresh rate that was provided by the app with // Exact compatibility + ExplicitGte, // Greater than or equal to frame rate provided by the app ExplicitCategory, // Specific frame rate category was provided by the app ftl_last = ExplicitCategory @@ -460,7 +461,11 @@ private: bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); // Returns the refresh rate score as a ratio to max refresh rate, which has a score of 1. - float calculateDistanceScoreFromMax(Fps refreshRate) const REQUIRES(mLock); + float calculateDistanceScoreFromMaxLocked(Fps refreshRate) const REQUIRES(mLock); + + // Returns the refresh rate score based on its distance from the reference rate. + float calculateDistanceScoreLocked(Fps referenceRate, Fps refreshRate) const REQUIRES(mLock); + // 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&, Fps refreshRate, diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 9dd14317ef..ddc3967c40 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -41,6 +41,7 @@ using ProtoActualSurfaceFrameStart = perfetto::protos::FrameTimelineEvent_Actual using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd; using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType; using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType; +using ProtoJankSeverityType = perfetto::protos::FrameTimelineEvent_JankSeverityType; using ProtoPredictionType = perfetto::protos::FrameTimelineEvent_PredictionType; namespace android::frametimeline { @@ -335,7 +336,9 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42); EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42); EXPECT_NE(surfaceFrame1->getJankType(), std::nullopt); + EXPECT_NE(surfaceFrame1->getJankSeverityType(), std::nullopt); EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt); + EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt); } TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { @@ -493,8 +496,10 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_invalidSignalTime) { auto displayFrame0 = getDisplayFrame(0); EXPECT_EQ(displayFrame0->getActuals().presentTime, 59); EXPECT_EQ(displayFrame0->getJankType(), JankType::Unknown | JankType::DisplayHAL); + EXPECT_EQ(displayFrame0->getJankSeverityType(), JankSeverityType::Unknown); EXPECT_EQ(surfaceFrame1->getActuals().presentTime, -1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Unknown); } // Tests related to TimeStats @@ -604,6 +609,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { presentFence1->signalForTest(90); mFrameTimeline->setSfPresent(56, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { @@ -633,6 +639,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { mFrameTimeline->setSfPresent(86, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { @@ -662,6 +669,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { mFrameTimeline->setSfPresent(56, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { @@ -691,6 +699,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { mFrameTimeline->setSfPresent(56, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { @@ -721,6 +730,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { mFrameTimeline->setSfPresent(86, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::BufferStuffing); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { @@ -752,6 +762,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { mFrameTimeline->setSfPresent(86, presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPresentsSurfaceFrame) { @@ -788,12 +799,14 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres auto displayFrame = getDisplayFrame(0); EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown); EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::UnknownStart); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish); EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent); EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed); + EXPECT_EQ(surfaceFrame1->getJankSeverityType(), JankSeverityType::Full); } /* @@ -920,7 +933,8 @@ ProtoExpectedDisplayFrameStart createProtoExpectedDisplayFrameStart(int64_t cook ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart( int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish, - bool gpuComposition, ProtoJankType jankType, ProtoPredictionType predictionType) { + bool gpuComposition, ProtoJankType jankType, ProtoJankSeverityType jankSeverityType, + ProtoPredictionType predictionType) { ProtoActualDisplayFrameStart proto; proto.set_cookie(cookie); proto.set_token(token); @@ -929,6 +943,7 @@ ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart( proto.set_on_time_finish(onTimeFinish); proto.set_gpu_composition(gpuComposition); proto.set_jank_type(jankType); + proto.set_jank_severity_type(jankSeverityType); proto.set_prediction_type(predictionType); return proto; } @@ -949,7 +964,8 @@ ProtoExpectedSurfaceFrameStart createProtoExpectedSurfaceFrameStart(int64_t cook ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart( int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName, ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition, - ProtoJankType jankType, ProtoPredictionType predictionType, bool isBuffer) { + ProtoJankType jankType, ProtoJankSeverityType jankSeverityType, + ProtoPredictionType predictionType, bool isBuffer) { ProtoActualSurfaceFrameStart proto; proto.set_cookie(cookie); proto.set_token(token); @@ -960,6 +976,7 @@ ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart( proto.set_on_time_finish(onTimeFinish); proto.set_gpu_composition(gpuComposition); proto.set_jank_type(jankType); + proto.set_jank_severity_type(jankSeverityType); proto.set_prediction_type(predictionType); proto.set_is_buffer(isBuffer); return proto; @@ -1002,6 +1019,8 @@ void validateTraceEvent(const ProtoActualDisplayFrameStart& received, EXPECT_EQ(received.gpu_composition(), source.gpu_composition()); ASSERT_TRUE(received.has_jank_type()); EXPECT_EQ(received.jank_type(), source.jank_type()); + ASSERT_TRUE(received.has_jank_severity_type()); + EXPECT_EQ(received.jank_severity_type(), source.jank_severity_type()); ASSERT_TRUE(received.has_prediction_type()); EXPECT_EQ(received.prediction_type(), source.prediction_type()); } @@ -1049,6 +1068,8 @@ void validateTraceEvent(const ProtoActualSurfaceFrameStart& received, EXPECT_EQ(received.gpu_composition(), source.gpu_composition()); ASSERT_TRUE(received.has_jank_type()); EXPECT_EQ(received.jank_type(), source.jank_type()); + ASSERT_TRUE(received.has_jank_severity_type()); + EXPECT_EQ(received.jank_severity_type(), source.jank_severity_type()); ASSERT_TRUE(received.has_prediction_type()); EXPECT_EQ(received.prediction_type(), source.prediction_type()); ASSERT_TRUE(received.has_is_buffer()); @@ -1116,6 +1137,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) { createProtoActualDisplayFrameStart(traceCookie + 9, 0, kSurfaceFlingerPid, FrameTimelineEvent::PRESENT_DROPPED, true, false, FrameTimelineEvent::JANK_DROPPED, + FrameTimelineEvent::SEVERITY_NONE, FrameTimelineEvent::PREDICTION_VALID); auto protoSkippedActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 9); @@ -1176,6 +1198,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { kSurfaceFlingerPid, FrameTimelineEvent::PRESENT_ON_TIME, true, false, FrameTimelineEvent::JANK_NONE, + FrameTimelineEvent::SEVERITY_NONE, FrameTimelineEvent::PREDICTION_VALID); auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2); @@ -1255,6 +1278,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpecte kSurfaceFlingerPid, FrameTimelineEvent::PRESENT_UNSPECIFIED, false, false, FrameTimelineEvent::JANK_UNKNOWN, + FrameTimelineEvent::SEVERITY_UNKNOWN, FrameTimelineEvent::PREDICTION_EXPIRED); auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1); @@ -1330,6 +1354,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { displayFrameToken1, sPidOne, sLayerNameOne, FrameTimelineEvent::PRESENT_DROPPED, true, false, FrameTimelineEvent::JANK_DROPPED, + FrameTimelineEvent::SEVERITY_UNKNOWN, FrameTimelineEvent::PREDICTION_VALID, true); auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2); @@ -1342,6 +1367,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { displayFrameToken1, sPidOne, sLayerNameOne, FrameTimelineEvent::PRESENT_ON_TIME, true, false, FrameTimelineEvent::JANK_NONE, + FrameTimelineEvent::SEVERITY_NONE, FrameTimelineEvent::PREDICTION_VALID, true); auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4); @@ -1488,6 +1514,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredIsAppMissedDeadline displayFrameToken, sPidOne, sLayerNameOne, FrameTimelineEvent::PRESENT_UNSPECIFIED, false, false, FrameTimelineEvent::JANK_APP_DEADLINE_MISSED, + FrameTimelineEvent::SEVERITY_UNKNOWN, FrameTimelineEvent::PREDICTION_EXPIRED, true); auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1); @@ -1565,6 +1592,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced displayFrameToken, sPidOne, sLayerNameOne, FrameTimelineEvent::PRESENT_DROPPED, false, false, FrameTimelineEvent::JANK_DROPPED, + FrameTimelineEvent::SEVERITY_UNKNOWN, FrameTimelineEvent::PREDICTION_EXPIRED, true); auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1); @@ -1643,6 +1671,7 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::None); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None); } TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) { @@ -1669,6 +1698,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresen EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial); // Fences for the second frame haven't been flushed yet, so it should be 0 auto displayFrame2 = getDisplayFrame(1); @@ -1682,6 +1712,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresen EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) { @@ -1708,6 +1739,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::DisplayHAL); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial); // Fences for the second frame haven't been flushed yet, so it should be 0 auto displayFrame2 = getDisplayFrame(1); @@ -1722,6 +1754,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) { @@ -1744,6 +1777,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) { @@ -1789,6 +1823,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame0->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); + EXPECT_EQ(displayFrame0->getJankSeverityType(), JankSeverityType::Full); // case 3 - cpu time = 86 - 82 = 4, vsync period = 30 mFrameTimeline->setSfWakeUp(sfToken3, 106, RR_30, RR_30); @@ -1803,6 +1838,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Full); // case 4 - cpu time = 86 - 82 = 4, vsync period = 30 mFrameTimeline->setSfWakeUp(sfToken4, 120, RR_30, RR_30); @@ -1817,6 +1853,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full); addEmptyDisplayFrame(); @@ -1825,6 +1862,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); + EXPECT_EQ(displayFrame3->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) { @@ -1877,12 +1915,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerScheduling); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Partial); 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); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial); // Fences for the second frame haven't been flushed yet, so it should be 0 presentFence2->signalForTest(65); @@ -1905,12 +1945,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::EarlyPresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Partial); 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); + EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) { @@ -1963,12 +2005,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::DisplayHAL); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::Partial); 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); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial); // Fences for the second frame haven't been flushed yet, so it should be 0 presentFence2->signalForTest(86); @@ -1991,12 +2035,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::PredictionError); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full); 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); + EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) { @@ -2033,12 +2079,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::None); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::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); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) { @@ -2095,12 +2143,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::None); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::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); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Partial); // Fences for the second frame haven't been flushed yet, so it should be 0 presentFence2->signalForTest(60); @@ -2115,6 +2165,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Partial); actuals2 = presentedSurfaceFrame2.getActuals(); EXPECT_EQ(actuals2.presentTime, 60); @@ -2122,6 +2173,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed); + EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Partial); } TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) { @@ -2181,10 +2233,12 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::None); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None); EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Full); // Fences for the second frame haven't been flushed yet, so it should be 0 EXPECT_EQ(displayFrame2->getActuals().presentTime, 0); @@ -2201,11 +2255,13 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::None); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::None); EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::AppDeadlineMissed | JankType::BufferStuffing); + EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffing) { @@ -2266,10 +2322,12 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::None); + EXPECT_EQ(displayFrame1->getJankSeverityType(), JankSeverityType::None); EXPECT_EQ(presentedSurfaceFrame1.getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(presentedSurfaceFrame1.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(presentedSurfaceFrame1.getJankType(), JankType::AppDeadlineMissed); + EXPECT_EQ(presentedSurfaceFrame1.getJankSeverityType(), JankSeverityType::Full); // Fences for the second frame haven't been flushed yet, so it should be 0 EXPECT_EQ(displayFrame2->getActuals().presentTime, 0); @@ -2286,10 +2344,12 @@ TEST_F(FrameTimelineTest, jankClassification_appDeadlineAdjustedForBufferStuffin EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::None); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::None); EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::BufferStuffing); + EXPECT_EQ(presentedSurfaceFrame2.getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_GpuAndCpuMiss) { @@ -2317,6 +2377,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_G EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Full); // Case 2: No GPU fence so it will not use GPU composition. mFrameTimeline->setSfWakeUp(sfToken2, 52, RR_30, RR_30); @@ -2334,6 +2395,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent_G EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed); + EXPECT_EQ(displayFrame2->getJankSeverityType(), JankSeverityType::Full); } TEST_F(FrameTimelineTest, jankClassification_presentFenceError) { @@ -2364,6 +2426,7 @@ TEST_F(FrameTimelineTest, jankClassification_presentFenceError) { EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown); } { auto displayFrame = getDisplayFrame(1); @@ -2371,6 +2434,7 @@ TEST_F(FrameTimelineTest, jankClassification_presentFenceError) { EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::UnknownFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::Unknown | JankType::DisplayHAL); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::Unknown); } { auto displayFrame = getDisplayFrame(2); @@ -2378,6 +2442,7 @@ TEST_F(FrameTimelineTest, jankClassification_presentFenceError) { EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::OnTimePresent); EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::OnTimeFinish); EXPECT_EQ(displayFrame->getJankType(), JankType::None); + EXPECT_EQ(displayFrame->getJankSeverityType(), JankSeverityType::None); } } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index a9567b2881..0cacf810c0 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1150,6 +1150,75 @@ TEST_P(RefreshRateSelectorTest, scrollWhileWatching60fps_60_90) { EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr); } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitGte) { + auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId120); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 25_Hz; + lr1.name = "25Hz ExplicitGte"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + EXPECT_EQ(kMode30, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 91_Hz; + lr1.name = "91Hz ExplicitGte"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 60_Hz; + lr2.name = "60Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode60, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 90_Hz; + lr2.name = "90Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode90, selector.getBestFrameRateMode(layers).modePtr); + + lr1.vote = LayerVoteType::ExplicitGte; + lr1.desiredRefreshRate = 60_Hz; + lr1.name = "60Hz ExplicitGte"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 120_Hz; + lr2.name = "120Hz ExplicitExactOrMultiple"; + EXPECT_EQ(kMode120, selector.getBestFrameRateMode(layers).modePtr); +} + TEST_P(RefreshRateSelectorTest, getMaxRefreshRatesByPolicy) { // The kModes_30_60_90 contains two kMode72_G1, kMode120_G1 which are from the // different group. |