diff options
131 files changed, 2708 insertions, 935 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 17e5cc119d..83b336c62f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1035,8 +1035,6 @@ static void DoLogcat() { CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); DoRadioLogcat(); - RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); - /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */ RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}); @@ -1243,7 +1241,7 @@ static void DumpPacketStats() { static void DumpIpAddrAndRules() { /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ - RunCommand("NETWORK INTERFACES", {"ip", "link"}); + RunCommand("NETWORK INTERFACES", {"ip", "-s", "link"}); RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"}); RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"}); RunCommand("IP RULES", {"ip", "rule", "show"}); @@ -1526,7 +1524,7 @@ static void DumpExternalFragmentationInfo() { } static void DumpstateLimitedOnly() { - // Trimmed-down version of dumpstate to only include a whitelisted + // Trimmed-down version of dumpstate to only include a allowlisted // set of logs (system log, event log, and system server / system app // crashes, and networking logs). See b/136273873 and b/138459828 // for context. diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs index 6f5deb96b2..c30c00fcd2 100644 --- a/cmds/evemu-record/main.rs +++ b/cmds/evemu-record/main.rs @@ -26,7 +26,7 @@ use std::io; use std::io::{BufRead, Write}; use std::path::PathBuf; -use clap::Parser; +use clap::{Parser, ValueEnum}; use nix::sys::time::TimeVal; mod evdev; @@ -39,6 +39,19 @@ struct Args { device: Option<PathBuf>, /// The file to save the recording to. Defaults to standard output. output_file: Option<PathBuf>, + + /// The base time that timestamps should be relative to (Android-specific extension) + #[arg(long, value_enum, default_value_t = TimestampBase::FirstEvent)] + timestamp_base: TimestampBase, +} + +#[derive(Clone, Debug, ValueEnum)] +enum TimestampBase { + /// The first event received from the device. + FirstEvent, + + /// The time when the system booted. + Boot, } fn get_choice(max: u32) -> u32 { @@ -149,7 +162,11 @@ fn print_device_description( Ok(()) } -fn print_events(device: &evdev::Device, output: &mut impl Write) -> Result<(), Box<dyn Error>> { +fn print_events( + device: &evdev::Device, + output: &mut impl Write, + timestamp_base: TimestampBase, +) -> Result<(), Box<dyn Error>> { fn print_event(output: &mut impl Write, event: &evdev::InputEvent) -> Result<(), io::Error> { // TODO(b/302297266): Translate events into human-readable names and add those as comments. writeln!( @@ -164,12 +181,15 @@ fn print_events(device: &evdev::Device, output: &mut impl Write) -> Result<(), B Ok(()) } let event = device.read_event()?; - // Due to a bug in the C implementation of evemu-play [0] that has since become part of the API, - // the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, so offset - // it by 1µs. - // - // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d - let start_time = event.time - TimeVal::new(0, 1); + let start_time = match timestamp_base { + // Due to a bug in the C implementation of evemu-play [0] that has since become part of the + // API, the timestamp of the first event in a recording shouldn't be exactly 0.0 seconds, + // so offset it by 1µs. + // + // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d + TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1), + TimestampBase::Boot => TimeVal::new(0, 0), + }; print_event(output, &event.offset_time_by(start_time))?; loop { let event = device.read_event()?; @@ -188,6 +208,6 @@ fn main() -> Result<(), Box<dyn Error>> { None => Box::new(io::stdout().lock()), }; print_device_description(&device, &mut output)?; - print_events(&device, &mut output)?; + print_events(&device, &mut output, args.timestamp_base)?; Ok(()) } 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/IInterface.h b/libs/binder/include/binder/IInterface.h index dc572ac953..ac845bc003 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -119,8 +119,8 @@ public: "The preferred way to add interfaces is to define " \ "an .aidl file to auto-generate the interface. If " \ "an interface must be manually written, add its " \ - "name to the whitelist."); \ - DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + "name to the allowlist."); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) #else @@ -305,10 +305,10 @@ constexpr bool equals(const char* a, const char* b) { return equals(a + 1, b + 1); } -constexpr bool inList(const char* a, const char* const* whitelist) { - if (*whitelist == nullptr) return false; - if (equals(a, *whitelist)) return true; - return inList(a, whitelist + 1); +constexpr bool inList(const char* a, const char* const* allowlist) { + if (*allowlist == nullptr) return false; + if (equals(a, *allowlist)) return true; + return inList(a, allowlist + 1); } constexpr bool allowedManualInterface(const char* name) { 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/ndk/Android.bp b/libs/binder/ndk/Android.bp index 47b9f5868d..ccf3ce891f 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -139,6 +139,7 @@ cc_library { "performance*", "portability*", ], + afdo: true, } cc_library_headers { 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/Android.bp b/libs/gui/Android.bp index c84ee1f9da..44a9e52d5b 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -423,10 +423,10 @@ cc_defaults { "libhidlbase", "liblog", "libnativewindow", + "libselinux", "libsync", "libui", "libutils", - "libvndksupport", ], static_libs: [ diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index b6a47fb4e9..744201a5df 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -36,13 +36,45 @@ #include <gui/TraceUtils.h> #include <private/gui/BufferQueueThreadState.h> -#ifndef __ANDROID_VNDK__ +#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER) #include <binder/PermissionCache.h> -#include <vndksupport/linker.h> +#include <selinux/android.h> +#include <selinux/selinux.h> #endif #include <system/window.h> +namespace { +#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER) +int selinux_log_suppress_callback(int, const char*, ...) { // NOLINT + // DO NOTHING + return 0; +} + +bool hasAccessToPermissionService() { + char* ctx; + + if (getcon(&ctx) == -1) { + // Failed to get current selinux context + return false; + } + + union selinux_callback cb; + + cb.func_log = selinux_log_suppress_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + bool hasAccess = selinux_check_access(ctx, "u:object_r:permission_service:s0", + "service_manager", "find", NULL) == 0; + freecon(ctx); + cb.func_log = hasAccess ? selinux_log_callback : selinux_vendor_log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + return hasAccess; +} +#endif +} // namespace + namespace android { // Macros for include BufferQueueCore information in log messages @@ -814,7 +846,7 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul // the PermissionController. We need to do a runtime check as well, since // the system variant of libgui can be loaded in a vendor process. For eg: // if a HAL uses an llndk library that depends on libgui (libmediandk etc). - if (!android_is_in_vendor_process()) { + if (hasAccessToPermissionService()) { const pid_t pid = BufferQueueThreadState::getCallingPid(); if ((uid != shellUid) && !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index fd8fc8d123..38fab9cdaa 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -86,7 +86,7 @@ layer_state_t::layer_state_t() defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT), frameRateCategorySmoothSwitchOnly(false), - frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF), + frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE), fixedTransformHint(ui::Transform::ROT_INVALID), autoRefresh(false), isTrustedOverlay(false), @@ -930,7 +930,6 @@ status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const { SAFE_PARCEL(output->writeStrongBinder, displayToken); SAFE_PARCEL(output->writeUint32, width); SAFE_PARCEL(output->writeUint32, height); - SAFE_PARCEL(output->writeBool, useIdentityTransform); return NO_ERROR; } @@ -940,7 +939,6 @@ status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) { SAFE_PARCEL(input->readStrongBinder, &displayToken); SAFE_PARCEL(input->readUint32, &width); SAFE_PARCEL(input->readUint32, &height); - SAFE_PARCEL(input->readBool, &useIdentityTransform); return NO_ERROR; } diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h index 2676e0a338..e29ce41bd5 100644 --- a/libs/gui/include/gui/DisplayCaptureArgs.h +++ b/libs/gui/include/gui/DisplayCaptureArgs.h @@ -76,7 +76,6 @@ struct DisplayCaptureArgs : CaptureArgs { sp<IBinder> displayToken; uint32_t width{0}; uint32_t height{0}; - bool useIdentityTransform{false}; status_t writeToParcel(Parcel* output) const override; status_t readFromParcel(const Parcel* input) override; 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/input/input_flags.aconfig b/libs/input/input_flags.aconfig index a807d823f1..d13a2606c2 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -43,6 +43,13 @@ flag { } flag { + name: "report_palms_to_gestures_library" + namespace: "input" + description: "Report touches marked as palm by firmware to gestures library" + bug: "302505955" +} + +flag { name: "enable_touchpad_typing_palm_rejection" namespace: "input" description: "Enable additional palm rejection on touchpad while typing" diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index b068f4807c..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 }; /* @@ -1103,17 +1108,34 @@ enum { enum { /** * Default value. The layer uses its own frame rate specifications, assuming it has any - * specifications, instead of its parent's. + * specifications, instead of its parent's. If it does not have its own frame rate + * specifications, it will try to use its parent's. It will propagate its specifications to any + * descendants that do not have their own. + * + * However, FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN on an ancestor layer + * supersedes this behavior, meaning that this layer will inherit frame rate specifications + * regardless of whether it has its own. */ - ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 0, + ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE = 0, /** * The layer's frame rate specifications will propagate to and override those of its descendant * layers. - * The layer with this strategy has the ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF - * behavior for itself. + * + * The layer itself has the FRAME_RATE_SELECTION_STRATEGY_PROPAGATE behavior. + * Thus, ancestor layer that also has the strategy + * FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN will override this layer's + * frame rate specifications. */ ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN = 1, + + /** + * The layer's frame rate specifications will not propagate to its descendant + * layers, even if the descendant layer has no frame rate specifications. + * However, FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN on an ancestor + * layer supersedes this behavior. + */ + ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF = 2, }; static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate, diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 8821c0e97d..ba20d1f223 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -433,6 +433,10 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { // Looks like this would slow things down and we can't depend on it on all platforms interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; + if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) { + BAIL("Protected memory not supported"); + } + float queuePriorities[1] = {0.0f}; void* queueNextPtr = nullptr; 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/libs/ui/include/ui/DisplayMap.h b/libs/ui/include/ui/DisplayMap.h index 7eacb0a7f0..65d2b8fa31 100644 --- a/libs/ui/include/ui/DisplayMap.h +++ b/libs/ui/include/ui/DisplayMap.h @@ -23,13 +23,18 @@ namespace android::ui { // The static capacities were chosen to exceed a typical number of physical and/or virtual displays. +constexpr size_t kDisplayCapacity = 5; template <typename Key, typename Value> -using DisplayMap = ftl::SmallMap<Key, Value, 5>; +using DisplayMap = ftl::SmallMap<Key, Value, kDisplayCapacity>; +constexpr size_t kPhysicalDisplayCapacity = 3; template <typename Key, typename Value> -using PhysicalDisplayMap = ftl::SmallMap<Key, Value, 3>; +using PhysicalDisplayMap = ftl::SmallMap<Key, Value, kPhysicalDisplayCapacity>; template <typename T> -using PhysicalDisplayVector = ftl::SmallVector<T, 3>; +using DisplayVector = ftl::SmallVector<T, kDisplayCapacity>; + +template <typename T> +using PhysicalDisplayVector = ftl::SmallVector<T, kPhysicalDisplayCapacity>; } // namespace android::ui diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp index 9deba01dc8..eda5ea4578 100644 --- a/libs/ultrahdr/Android.bp +++ b/libs/ultrahdr/Android.bp @@ -21,7 +21,8 @@ package { } cc_library { - name: "libultrahdr", + name: "libultrahdr-deprecated", + enabled: false, host_supported: true, vendor_available: true, export_include_dirs: ["include"], @@ -46,7 +47,8 @@ cc_library { } cc_library { - name: "libjpegencoder", + name: "libjpegencoder-deprecated", + enabled: false, host_supported: true, vendor_available: true, @@ -64,7 +66,8 @@ cc_library { } cc_library { - name: "libjpegdecoder", + name: "libjpegdecoder-deprecated", + enabled: false, host_supported: true, vendor_available: true, diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp index e999a8bd28..2fa361f0b7 100644 --- a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp +++ b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp @@ -13,7 +13,7 @@ // limitations under the License. license { - name: "adobe_hdr_gain_map_license", + name: "adobe_hdr_gain_map_license-deprecated", license_kinds: ["legacy_by_exception_only"], license_text: ["NOTICE"], } diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp index 6c0a2f577c..8d9132fd4d 100644 --- a/libs/ultrahdr/fuzzer/Android.bp +++ b/libs/ultrahdr/fuzzer/Android.bp @@ -22,7 +22,8 @@ package { } cc_defaults { - name: "ultrahdr_fuzzer_defaults", + name: "ultrahdr_fuzzer_defaults-deprecated", + enabled: false, host_supported: true, shared_libs: [ "libimage_io", @@ -53,7 +54,8 @@ cc_defaults { } cc_fuzz { - name: "ultrahdr_enc_fuzzer", + name: "ultrahdr_enc_fuzzer-deprecated", + enabled: false, defaults: ["ultrahdr_fuzzer_defaults"], srcs: [ "ultrahdr_enc_fuzzer.cpp", @@ -61,7 +63,8 @@ cc_fuzz { } cc_fuzz { - name: "ultrahdr_dec_fuzzer", + name: "ultrahdr_dec_fuzzer-deprecated", + enabled: false, defaults: ["ultrahdr_fuzzer_defaults"], srcs: [ "ultrahdr_dec_fuzzer.cpp", diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp index bda804ae31..00cc797591 100644 --- a/libs/ultrahdr/tests/Android.bp +++ b/libs/ultrahdr/tests/Android.bp @@ -22,7 +22,8 @@ package { } cc_test { - name: "ultrahdr_unit_test", + name: "ultrahdr_unit_test-deprecated", + enabled: false, test_suites: ["device-tests"], srcs: [ "gainmapmath_test.cpp", diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp index f2a313cb8a..cb063af2f6 100644 --- a/libs/vibrator/fuzzer/Android.bp +++ b/libs/vibrator/fuzzer/Android.bp @@ -47,6 +47,17 @@ cc_fuzz { ], fuzz_config: { - componentid: 155276, + cc: [ + "android-haptics@google.com", + ], + componentid: 345036, + hotlists: [ + "4593311", + ], + description: "The fuzzer targets the APIs of libvibrator", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "multi_user", + fuzzed_code_usage: "shipped", }, } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 54da8e809e..6ad3de0015 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3980,16 +3980,6 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { - if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS || - options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) && - mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) { - LOG(INFO) << __func__ - << ": Canceling drag and drop because the pointers for the drag window are being " - "canceled."; - sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0); - mDragState.reset(); - } - if (connection->status == Connection::Status::BROKEN) { return; } @@ -4002,6 +3992,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( if (cancelationEvents.empty()) { return; } + if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " "with reality: %s, mode=%s.", @@ -4050,6 +4041,14 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( pointerIndex++) { pointerIds.set(motionEntry.pointerProperties[pointerIndex].id); } + if (mDragState && mDragState->dragWindow->getToken() == token && + pointerIds.test(mDragState->pointerId)) { + LOG(INFO) << __func__ + << ": Canceling drag and drop because the pointers for the drag " + "window are being canceled."; + sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0); + mDragState.reset(); + } addPointerWindowTargetLocked(window, InputTarget::Flags::DISPATCH_AS_IS, pointerIds, motionEntry.downTime, targets); } else { diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index 536775100a..cd0500c872 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -128,20 +128,14 @@ void TouchedWindow::removeTouchingPointers(DeviceId deviceId, std::set<DeviceId> TouchedWindow::getTouchingDeviceIds() const { std::set<DeviceId> deviceIds; - for (const auto& [deviceId, _] : mDeviceStates) { - deviceIds.insert(deviceId); + for (const auto& [deviceId, deviceState] : mDeviceStates) { + if (deviceState.touchingPointerIds.any()) { + deviceIds.insert(deviceId); + } } return deviceIds; } -std::set<DeviceId> TouchedWindow::getActiveDeviceIds() const { - std::set<DeviceId> out; - for (const auto& [deviceId, _] : mDeviceStates) { - out.emplace(deviceId); - } - return out; -} - bool TouchedWindow::hasPilferingPointers(DeviceId deviceId) const { const auto stateIt = mDeviceStates.find(deviceId); if (stateIt == mDeviceStates.end()) { diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index 6d2283e0af..9a31678955 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -48,15 +48,7 @@ struct TouchedWindow { void addTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); void removeTouchingPointer(DeviceId deviceId, int32_t pointerId); void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers); - /** - * Get the currently active touching device id. If there isn't exactly 1 touching device, return - * nullopt. - */ std::set<DeviceId> getTouchingDeviceIds() const; - /** - * The ids of devices that are currently touching or hovering. - */ - std::set<DeviceId> getActiveDeviceIds() const; // Pilfering pointers bool hasPilferingPointers(DeviceId deviceId) const; diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp index 6780dcedec..b89b7f38a9 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp @@ -22,10 +22,15 @@ #include <chrono> #include <vector> +#include <com_android_input_flags.h> #include <linux/input-event-codes.h> +namespace input_flags = com::android::input::flags; + namespace android { +const bool REPORT_PALMS_TO_GESTURES_LIBRARY = input_flags::report_palms_to_gestures_library(); + HardwareStateConverter::HardwareStateConverter(const InputDeviceContext& deviceContext, MultiTouchMotionAccumulator& motionAccumulator) : mDeviceContext(deviceContext), @@ -84,7 +89,7 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t } // Some touchpads continue to report contacts even after they've identified them as palms. // We want to exclude these contacts from the HardwareStates. - if (slot.getToolType() == ToolType::PALM) { + if (!REPORT_PALMS_TO_GESTURES_LIBRARY && slot.getToolType() == ToolType::PALM) { numPalms++; continue; } @@ -100,6 +105,11 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t fingerState.position_x = slot.getX(); fingerState.position_y = slot.getY(); fingerState.tracking_id = slot.getTrackingId(); + if (REPORT_PALMS_TO_GESTURES_LIBRARY) { + fingerState.tool_type = slot.getToolType() == ToolType::PALM + ? FingerState::ToolType::kPalm + : FingerState::ToolType::kFinger; + } } schs.state.fingers = schs.fingers.data(); schs.state.finger_cnt = schs.fingers.size(); diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp index 5bea2bac18..ff9bd9e75b 100644 --- a/services/inputflinger/tests/HardwareStateConverter_test.cpp +++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp @@ -18,6 +18,8 @@ #include <memory> #include <EventHub.h> +#include <com_android_input_flags.h> +#include <flag_macros.h> #include <gtest/gtest.h> #include <linux/input-event-codes.h> #include <utils/StrongPointer.h> @@ -31,6 +33,13 @@ namespace android { +namespace { + +const auto REPORT_PALMS = + ACONFIG_FLAG(com::android::input::flags, report_palms_to_gestures_library); + +} // namespace + class HardwareStateConverterTest : public testing::Test { public: HardwareStateConverterTest() @@ -192,7 +201,8 @@ TEST_F(HardwareStateConverterTest, TwoFingers) { EXPECT_EQ(0u, finger2.flags); } -TEST_F(HardwareStateConverterTest, OnePalm) { +TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmDisableReportPalms, + REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) { processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); @@ -207,7 +217,25 @@ TEST_F(HardwareStateConverterTest, OnePalm) { EXPECT_EQ(0, schs->state.finger_cnt); } -TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) { +TEST_F_WITH_FLAGS(HardwareStateConverterTest, OnePalmEnableReportPalms, + REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) { + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100); + + processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1); + processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1); + std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.touch_cnt); + EXPECT_EQ(1, schs->state.finger_cnt); + EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type); +} + +TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmDisableReportPalms, + REQUIRES_FLAGS_DISABLED(REPORT_PALMS)) { processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); @@ -252,6 +280,56 @@ TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) { EXPECT_NEAR(95, newFinger.position_y, EPSILON); } +TEST_F_WITH_FLAGS(HardwareStateConverterTest, OneFingerTurningIntoAPalmEnableReportPalms, + REQUIRES_FLAGS_ENABLED(REPORT_PALMS)) { + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, 0); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, 123); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 50); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 100); + + processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1); + processAxis(ARBITRARY_TIME, EV_KEY, BTN_TOOL_FINGER, 1); + + std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.touch_cnt); + EXPECT_EQ(1, schs->state.finger_cnt); + EXPECT_EQ(FingerState::ToolType::kFinger, schs->state.fingers[0].tool_type); + + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 51); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 99); + + schs = processSync(ARBITRARY_TIME); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.touch_cnt); + ASSERT_EQ(1, schs->state.finger_cnt); + EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type); + + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 53); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 97); + + schs = processSync(ARBITRARY_TIME); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.touch_cnt); + EXPECT_EQ(1, schs->state.finger_cnt); + EXPECT_EQ(FingerState::ToolType::kPalm, schs->state.fingers[0].tool_type); + + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, 55); + processAxis(ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, 95); + schs = processSync(ARBITRARY_TIME); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.touch_cnt); + ASSERT_EQ(1, schs->state.finger_cnt); + const FingerState& newFinger = schs->state.fingers[0]; + EXPECT_EQ(FingerState::ToolType::kFinger, newFinger.tool_type); + EXPECT_EQ(123, newFinger.tracking_id); + EXPECT_NEAR(55, newFinger.position_x, EPSILON); + EXPECT_NEAR(95, newFinger.position_y, EPSILON); +} + TEST_F(HardwareStateConverterTest, ButtonPressed) { processAxis(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); std::optional<SelfContainedHardwareState> schs = processSync(ARBITRARY_TIME); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 2f26c3590f..e22013327f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -9660,6 +9660,50 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherDragTests, DragAndDropNotCancelledIfSomeOtherPointerIsPilfered) { + startDrag(); + + // No cancel event after drag start + mSpyWindow->assertNoEvents(); + + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Receives cancel for first pointer after next pointer down + mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + mSpyWindow->assertNoEvents(); + + // Spy window calls pilfer pointers + EXPECT_EQ(OK, mDispatcher->pilferPointers(mSpyWindow->getToken())); + mDragWindow->assertNoEvents(); + + const MotionEvent firstFingerMoveEvent = + MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(60).y(60)) + .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(60).y(60)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Drag window should still receive the new event + mDragWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + mDragWindow->assertNoEvents(); +} + TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { startDrag(true, AINPUT_SOURCE_STYLUS); @@ -9997,6 +10041,19 @@ TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) { ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp()); } +TEST_F(InputDispatcherDragTests, NoDragAndDropWithHoveringPointer) { + // Start hovering over the window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, {50, 50})); + + ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER))); + ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER))); + + ASSERT_FALSE(startDrag(/*sendDown=*/false)) + << "Drag and drop should not work with a hovering pointer"; +} + class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {}; TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { @@ -10896,6 +10953,25 @@ TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer) { rightWindow->assertNoEvents(); } +TEST_F(InputDispatcherPilferPointersTest, NoPilferingWithHoveringPointers) { + auto window = createForeground(); + auto spy = createSpy(); + mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .deviceId(1) + .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(100).y(200)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + // Pilfer pointers from the spy window should fail. + EXPECT_NE(OK, mDispatcher->pilferPointers(spy->getToken())); + spy->assertNoEvents(); + window->assertNoEvents(); +} + class InputDispatcherStylusInterceptorTest : public InputDispatcherTest { public: std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() { 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/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 2740a979f3..e316190499 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -91,6 +91,9 @@ cc_library { ], local_include_dirs: ["include"], export_include_dirs: ["include"], + shared_libs: [ + "server_configurable_flags", + ], } cc_library { @@ -114,6 +117,9 @@ cc_library { "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", ], + shared_libs: [ + "server_configurable_flags", + ], local_include_dirs: ["include"], export_include_dirs: ["include"], } @@ -121,6 +127,10 @@ cc_library { cc_test { name: "libcompositionengine_test", test_suites: ["device-tests"], + include_dirs: [ + "frameworks/native/services/surfaceflinger/common/include", + "frameworks/native/services/surfaceflinger/tests/unittests", + ], defaults: ["libcompositionengine_defaults"], srcs: [ "tests/planner/CachedSetTest.cpp", @@ -150,10 +160,11 @@ cc_test { "libsurfaceflinger_common_test", "libsurfaceflingerflags_test", ], - // For some reason, libvulkan isn't picked up from librenderengine - // Probably ASAN related? shared_libs: [ + // For some reason, libvulkan isn't picked up from librenderengine + // Probably ASAN related? "libvulkan", + "server_configurable_flags", ], sanitize: { hwaddress: true, diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 422a79978e..f1d6f52eb8 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -26,6 +26,7 @@ #include <vector> #include <compositionengine/LayerFE.h> +#include <ftl/future.h> #include <renderengine/LayerSettings.h> #include <ui/Fence.h> #include <ui/FenceTime.h> @@ -263,8 +264,15 @@ public: // Prepare the output, updating the OutputLayers used in the output virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0; - // Presents the output, finalizing all composition details - virtual void present(const CompositionRefreshArgs&) = 0; + // Presents the output, finalizing all composition details. This may happen + // asynchronously, in which case the returned future must be waited upon. + virtual ftl::Future<std::monostate> present(const CompositionRefreshArgs&) = 0; + + // Whether this output can be presented from another thread. + virtual bool supportsOffloadPresent() const = 0; + + // Make the next call to `present` run asynchronously. + virtual void offloadPresentNextFrame() = 0; // Enables predicting composition strategy to run client composition earlier virtual void setPredictCompositionStrategy(bool) = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index de8293151b..eac5d97df3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -62,6 +62,7 @@ public: compositionengine::Output::FrameFences presentFrame() override; void setExpensiveRenderingExpected(bool) override; void finishFrame(GpuCompositionResult&&) override; + bool supportsOffloadPresent() const override; // compositionengine::Display overrides DisplayId getId() const override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index d95fbeab92..ec6a4e9c63 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -80,7 +80,9 @@ public: void setReleasedLayers(ReleasedLayers&&) override; void prepare(const CompositionRefreshArgs&, LayerFESet&) override; - void present(const CompositionRefreshArgs&) override; + ftl::Future<std::monostate> present(const CompositionRefreshArgs&) override; + bool supportsOffloadPresent() const override { return false; } + void offloadPresentNextFrame() override; void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override; @@ -121,6 +123,7 @@ public: virtual std::future<bool> chooseCompositionStrategyAsync( std::optional<android::HWComposer::DeviceRequestedChanges>*); virtual void resetCompositionStrategy(); + virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync(); protected: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; @@ -164,6 +167,7 @@ private: ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; compositionengine::Output::ColorProfile pickColorProfile( const compositionengine::CompositionRefreshArgs&) const; + void updateHwcAsyncWorker(); std::string mName; std::string mNamePlusId; @@ -177,6 +181,9 @@ private: std::unique_ptr<planner::Planner> mPlanner; std::unique_ptr<HwcAsyncWorker> mHwComposerAsyncWorker; + bool mPredictCompositionStrategy = false; + bool mOffloadPresent = false; + // Whether the content must be recomposed this frame. bool mMustRecompose = false; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index ce2b96fc2b..1f241b091c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -227,6 +227,7 @@ public: // Returns the bit-set of differing fields between this LayerState and another LayerState. // This bit-set is based on NonUniqueFields only, and excludes GraphicBuffers. ftl::Flags<LayerStateField> getDifferingFields(const LayerState& other) const; + bool isSourceCropSizeEqual(const LayerState& other) const; compositionengine::OutputLayer* getOutputLayer() const { return mOutputLayer; } int32_t getId() const { return mId.get(); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index c88fbd6d0f..95ea3a4ed7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -80,7 +80,10 @@ public: MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&)); MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&)); - MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&)); + MOCK_METHOD1(present, + ftl::Future<std::monostate>(const compositionengine::CompositionRefreshArgs&)); + MOCK_CONST_METHOD0(supportsOffloadPresent, bool()); + MOCK_METHOD(void, offloadPresentNextFrame, ()); MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&)); MOCK_METHOD2(rebuildLayerStacks, diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index 002177b572..7be5fe323a 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -20,6 +20,7 @@ #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/CompositionEngine.h> #include <compositionengine/impl/Display.h> +#include <ui/DisplayMap.h> #include <renderengine/RenderEngine.h> #include <utils/Trace.h> @@ -88,6 +89,41 @@ nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const { return mRefreshStartTime; } +namespace { +void offloadOutputs(Outputs& outputs) { + if (!FlagManager::getInstance().multithreaded_present() || outputs.size() < 2) { + return; + } + + ui::PhysicalDisplayVector<compositionengine::Output*> outputsToOffload; + for (const auto& output : outputs) { + if (!ftl::Optional(output->getDisplayId()).and_then(HalDisplayId::tryCast)) { + // Not HWC-enabled, so it is always client-composited. No need to offload. + continue; + } + + // Only run present in multiple threads if all HWC-enabled displays + // being refreshed support it. + if (!output->supportsOffloadPresent()) { + return; + } + outputsToOffload.push_back(output.get()); + } + + if (outputsToOffload.size() < 2) { + return; + } + + // Leave the last eligible display on the main thread, which will + // allow it to run concurrently without an extra thread hop. + outputsToOffload.pop_back(); + + for (compositionengine::Output* output : outputsToOffload) { + output->offloadPresentNextFrame(); + } +} +} // namespace + void CompositionEngine::present(CompositionRefreshArgs& args) { ATRACE_CALL(); ALOGV(__FUNCTION__); @@ -105,8 +141,23 @@ void CompositionEngine::present(CompositionRefreshArgs& args) { } } + // Offloading the HWC call for `present` allows us to simultaneously call it + // on multiple displays. This is desirable because these calls block and can + // be slow. + offloadOutputs(args.outputs); + + ui::DisplayVector<ftl::Future<std::monostate>> presentFutures; for (const auto& output : args.outputs) { - output->present(args); + presentFutures.push_back(output->present(args)); + } + + { + ATRACE_NAME("Waiting on HWC"); + for (auto& future : presentFutures) { + // TODO(b/185536303): Call ftl::Future::wait() once it exists, since + // we do not need the return value of get(). + future.get(); + } } } diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 469fb386fc..0475881bae 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -430,4 +430,13 @@ void Display::finishFrame(GpuCompositionResult&& result) { impl::Output::finishFrame(std::move(result)); } +bool Display::supportsOffloadPresent() const { + if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { + const auto& hwc = getCompositionEngine().getHwComposer(); + return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT); + } + + return false; +} + } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 2ae80de42a..e4d757810a 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -427,7 +427,8 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg uncacheBuffers(refreshArgs.bufferIdsToUncache); } -void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) { +ftl::Future<std::monostate> Output::present( + const compositionengine::CompositionRefreshArgs& refreshArgs) { ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str()); ALOGV(__FUNCTION__); @@ -448,8 +449,26 @@ void Output::present(const compositionengine::CompositionRefreshArgs& refreshArg devOptRepaintFlash(refreshArgs); finishFrame(std::move(result)); - presentFrameAndReleaseLayers(); + ftl::Future<std::monostate> future; + if (mOffloadPresent) { + future = presentFrameAndReleaseLayersAsync(); + + // Only offload for this frame. The next frame will determine whether it + // needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing, + // it is currently presenting. Further, it may be needed next frame, and + // we don't want to churn. + mOffloadPresent = false; + } else { + presentFrameAndReleaseLayers(); + future = ftl::yield<std::monostate>({}); + } renderCachedSets(refreshArgs); + return future; +} + +void Output::offloadPresentNextFrame() { + mOffloadPresent = true; + updateHwcAsyncWorker(); } void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { @@ -1084,6 +1103,14 @@ void Output::prepareFrame() { finishPrepareFrame(); } +ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() { + return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() { + presentFrameAndReleaseLayers(); + return true; + }))) + .then([](bool) { return std::monostate{}; }); +} + std::future<bool> Output::chooseCompositionStrategyAsync( std::optional<android::HWComposer::DeviceRequestedChanges>* changes) { return mHwComposerAsyncWorker->send( @@ -1600,8 +1627,15 @@ compositionengine::Output::FrameFences Output::presentFrame() { } void Output::setPredictCompositionStrategy(bool predict) { - if (predict) { - mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>(); + mPredictCompositionStrategy = predict; + updateHwcAsyncWorker(); +} + +void Output::updateHwcAsyncWorker() { + if (mPredictCompositionStrategy || mOffloadPresent) { + if (!mHwComposerAsyncWorker) { + mHwComposerAsyncWorker = std::make_unique<HwcAsyncWorker>(); + } } else { mHwComposerAsyncWorker.reset(nullptr); } @@ -1616,7 +1650,7 @@ bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refresh uint64_t outputLayerHash = getState().outputLayerHash; editState().lastOutputLayerHash = outputLayerHash; - if (!getState().isEnabled || !mHwComposerAsyncWorker) { + if (!getState().isEnabled || !mPredictCompositionStrategy) { ALOGV("canPredictCompositionStrategy disabled"); return false; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 13b6307aea..a18397d8ba 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -20,6 +20,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/properties.h> +#include <common/FlagManager.h> #include <compositionengine/impl/planner/Flattener.h> #include <compositionengine/impl/planner/LayerState.h> @@ -50,8 +51,19 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, for (size_t i = 0; i < incomingLayers.size(); i++) { // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try // to access destroyed OutputLayers later on. - if (incomingLayers[i]->getId() != existingLayers[i]->getId() || - incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) { + if (incomingLayers[i]->getId() != existingLayers[i]->getId()) { + return false; + } + + // Do not unflatten if source crop is only moved. + if (FlagManager::getInstance().cache_if_source_crop_layer_only_moved() && + incomingLayers[i]->isSourceCropSizeEqual(*(existingLayers[i])) && + incomingLayers[i]->getDifferingFields(*(existingLayers[i])) == + LayerStateField::SourceCrop) { + continue; + } + + if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) { return false; } } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index 8dab6ce61b..0e3fdbb0dc 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp @@ -76,6 +76,11 @@ size_t LayerState::getHash() const { return hash; } +bool LayerState::isSourceCropSizeEqual(const LayerState& other) const { + return mSourceCrop.get().getWidth() == other.mSourceCrop.get().getWidth() && + mSourceCrop.get().getHeight() == other.mSourceCrop.get().getHeight(); +} + ftl::Flags<LayerStateField> LayerState::getDifferingFields(const LayerState& other) const { ftl::Flags<LayerStateField> differences; auto myFields = getNonUniqueFields(); diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 60ed660c7a..602dd236f7 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -14,18 +14,25 @@ * limitations under the License. */ +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/CompositionEngine.h> #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/Output.h> #include <compositionengine/mock/OutputLayer.h> +#include <ftl/future.h> #include <gtest/gtest.h> #include <renderengine/mock/RenderEngine.h> #include "MockHWComposer.h" #include "TimeStats/TimeStats.h" +#include <variant> + +using namespace com::android::graphics::surfaceflinger; + namespace android::compositionengine { namespace { @@ -107,10 +114,17 @@ TEST_F(CompositionEnginePresentTest, worksAsExpected) { EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _)); EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _)); + // All of mOutput<i> are StrictMocks. If the flag is true, it will introduce + // calls to getDisplayId, which are not relevant to this test. + SET_FLAG_FOR_TEST(flags::multithreaded_present, false); + // The last step is to actually present each output. - EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))); - EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs))); - EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs))); + EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs))) + .WillOnce(Return(ftl::yield<std::monostate>({}))); + EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs))) + .WillOnce(Return(ftl::yield<std::monostate>({}))); + EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs))) + .WillOnce(Return(ftl::yield<std::monostate>({}))); mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3}; mEngine.present(mRefreshArgs); @@ -260,5 +274,164 @@ TEST_F(CompositionTestPreComposition, EXPECT_TRUE(mEngine.needsAnotherUpdate()); } +struct CompositionEngineOffloadTest : public testing::Test { + impl::CompositionEngine mEngine; + CompositionRefreshArgs mRefreshArgs; + + std::shared_ptr<mock::Output> mDisplay1{std::make_shared<StrictMock<mock::Output>>()}; + std::shared_ptr<mock::Output> mDisplay2{std::make_shared<StrictMock<mock::Output>>()}; + std::shared_ptr<mock::Output> mVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; + std::shared_ptr<mock::Output> mHalVirtualDisplay{std::make_shared<StrictMock<mock::Output>>()}; + + static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(123u); + static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(234u); + static constexpr GpuVirtualDisplayId kGpuVirtualDisplayId{789u}; + static constexpr HalVirtualDisplayId kHalVirtualDisplayId{456u}; + + void SetUp() override { + EXPECT_CALL(*mDisplay1, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1))); + EXPECT_CALL(*mDisplay2, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2))); + EXPECT_CALL(*mVirtualDisplay, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kGpuVirtualDisplayId))); + EXPECT_CALL(*mHalVirtualDisplay, getDisplayId) + .WillRepeatedly(Return(std::make_optional<DisplayId>(kHalVirtualDisplayId))); + } + + void setOutputs(std::initializer_list<std::shared_ptr<mock::Output>> outputs) { + for (auto& output : outputs) { + // If we call mEngine.present, prepare and present will be called on all the + // outputs in mRefreshArgs, but that's not the interesting part of the test. + EXPECT_CALL(*output, prepare(Ref(mRefreshArgs), _)).Times(1); + EXPECT_CALL(*output, present(Ref(mRefreshArgs))) + .WillOnce(Return(ftl::yield<std::monostate>({}))); + + mRefreshArgs.outputs.push_back(std::move(output)); + } + } +}; + +TEST_F(CompositionEngineOffloadTest, basic) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mDisplay2}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, dependsOnSupport) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(false)); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mDisplay2}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, dependsOnSupport2) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(false)); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mDisplay2}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, dependsOnFlag) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).Times(0); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, false); + setOutputs({mDisplay1, mDisplay2}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, oneDisplay) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).Times(0); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, virtualDisplay) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mDisplay2, mVirtualDisplay}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, virtualDisplay2) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mVirtualDisplay}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, halVirtual) { + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); + + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); + EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mDisplay1, mHalVirtualDisplay}); + + mEngine.present(mRefreshArgs); +} + +TEST_F(CompositionEngineOffloadTest, ordering) { + EXPECT_CALL(*mVirtualDisplay, supportsOffloadPresent).Times(0); + EXPECT_CALL(*mHalVirtualDisplay, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mDisplay1, supportsOffloadPresent).WillOnce(Return(true)); + EXPECT_CALL(*mDisplay2, supportsOffloadPresent).WillOnce(Return(true)); + + EXPECT_CALL(*mVirtualDisplay, offloadPresentNextFrame).Times(0); + EXPECT_CALL(*mHalVirtualDisplay, offloadPresentNextFrame).Times(1); + EXPECT_CALL(*mDisplay1, offloadPresentNextFrame).Times(1); + EXPECT_CALL(*mDisplay2, offloadPresentNextFrame).Times(0); + + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + setOutputs({mVirtualDisplay, mHalVirtualDisplay, mDisplay1, mDisplay2}); + + mEngine.present(mRefreshArgs); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 5537fcdcb5..5006e7d94a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -35,6 +35,7 @@ #include <cmath> #include <cstdint> +#include <variant> #include "CallOrderStateMachineHelper.h" #include "MockHWC2.h" @@ -54,6 +55,7 @@ using testing::InSequence; using testing::Invoke; using testing::IsEmpty; using testing::Mock; +using testing::NiceMock; using testing::Pointee; using testing::Property; using testing::Ref; @@ -4900,5 +4902,54 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]); } +struct OutputPresentFrameAndReleaseLayersAsyncTest : public ::testing::Test { + // Piggy-back on OutputPrepareFrameAsyncTest's version to avoid some boilerplate. + struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock { + // Set up the helper functions called by the function under test to use + // mock implementations. + MOCK_METHOD0(presentFrameAndReleaseLayers, void()); + MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>()); + }; + OutputPresentFrameAndReleaseLayersAsyncTest() { + mOutput->setDisplayColorProfileForTest( + std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); + mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); + mOutput->setCompositionEnabled(true); + mRefreshArgs.outputs = {mOutput}; + } + + mock::DisplayColorProfile* mDisplayColorProfile = new NiceMock<mock::DisplayColorProfile>(); + mock::RenderSurface* mRenderSurface = new NiceMock<mock::RenderSurface>(); + std::shared_ptr<OutputPartialMock> mOutput{std::make_shared<NiceMock<OutputPartialMock>>()}; + CompositionRefreshArgs mRefreshArgs; +}; + +TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) { + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0); + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1); + + mOutput->present(mRefreshArgs); +} + +TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) { + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()) + .WillOnce(Return(ftl::yield<std::monostate>({}))); + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0); + + mOutput->offloadPresentNextFrame(); + mOutput->present(mRefreshArgs); +} + +TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) { + ::testing::InSequence inseq; + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()) + .WillOnce(Return(ftl::yield<std::monostate>({}))); + EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1); + + mOutput->offloadPresentNextFrame(); + mOutput->present(mRefreshArgs); + mOutput->present(mRefreshArgs); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index d9318af371..763b998b3d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <common/include/common/test/FlagUtils.h> +#include "com_android_graphics_surfaceflinger_flags.h" + #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/planner/CachedSet.h> #include <compositionengine/impl/planner/Flattener.h> @@ -239,6 +242,30 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { expectAllLayersFlattened(layers); } +TEST_F(FlattenerTest, unflattenLayers_onlySourceCropMoved) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: + cache_if_source_crop_layer_only_moved, + true); + + auto& layerState1 = mTestLayers[0]->layerState; + auto& layerState2 = mTestLayers[1]->layerState; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + }; + + initializeFlattener(layers); + + mTestLayers[0]->outputLayerCompositionState.sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f}; + mTestLayers[1]->outputLayerCompositionState.sourceCrop = FloatRect{8.f, 16.f, 108.f, 116.f}; + + // only source crop is moved, so no flatten + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); +} + TEST_F(FlattenerTest, flattenLayers_basicFlatten) { auto& layerState1 = mTestLayers[0]->layerState; auto& layerState2 = mTestLayers[1]->layerState; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 7fdf9e7672..249c40b473 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -70,7 +70,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mIsPrimary(args.isPrimary), mRequestedRefreshRate(args.requestedRefreshRate), mRefreshRateSelector(std::move(args.refreshRateSelector)), - mDesiredModeChanged(concatId("DesiredModeChanged"), false) { + mHasDesiredModeTrace(concatId("HasDesiredMode"), false) { mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgsBuilder() @@ -217,20 +217,13 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps rende updateRefreshRateOverlayRate(vsyncRate, renderFps); } -bool DisplayDevice::initiateModeChange(const ActiveModeInfo& info, +bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { - if (!info.modeOpt || info.modeOpt->modePtr->getPhysicalDisplayId() != getPhysicalId()) { - ALOGE("Trying to initiate a mode change to invalid mode %s on display %s", - info.modeOpt ? std::to_string(info.modeOpt->modePtr->getId().value()).c_str() - : "null", - to_string(getId()).c_str()); - return BAD_VALUE; - } - mPendingMode = info; + mPendingModeOpt = std::move(desiredMode); mIsModeSetPending = true; - const auto& mode = *info.modeOpt->modePtr; + const auto& mode = *mPendingModeOpt->mode.modePtr; if (mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode.getHwcId(), constraints, &outTimeline) != OK) { @@ -528,35 +521,36 @@ void DisplayDevice::animateOverlay() { } } -auto DisplayDevice::setDesiredMode(const ActiveModeInfo& info, bool force) -> DesiredModeAction { +auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) + -> DesiredModeAction { ATRACE_CALL(); - LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided"); - LOG_ALWAYS_FATAL_IF(getPhysicalId() != info.modeOpt->modePtr->getPhysicalDisplayId(), + const auto& desiredModePtr = desiredMode.mode.modePtr; + + LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(), "DisplayId mismatch"); - ALOGV("%s(%s)", __func__, to_string(*info.modeOpt->modePtr).c_str()); + ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str()); std::scoped_lock lock(mDesiredModeLock); - if (mDesiredModeChanged) { + if (mDesiredModeOpt) { // A mode transition was already scheduled, so just override the desired mode. - const auto event = mDesiredMode.event; - mDesiredMode = info; - mDesiredMode.event = mDesiredMode.event | event; + const bool emitEvent = mDesiredModeOpt->emitEvent; + mDesiredModeOpt = std::move(desiredMode); + mDesiredModeOpt->emitEvent |= emitEvent; return DesiredModeAction::None; } - const auto& desiredMode = *info.modeOpt->modePtr; - // If the desired mode is already active... const auto activeMode = refreshRateSelector().getActiveMode(); - if (!force && activeMode.modePtr->getId() == desiredMode.getId()) { - if (activeMode == info.modeOpt) { + if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) { + if (activeMode == desiredMode.mode) { return DesiredModeAction::None; } // ...but the render rate changed: - setActiveMode(desiredMode.getId(), desiredMode.getVsyncRate(), info.modeOpt->fps); + setActiveMode(desiredModePtr->getId(), desiredModePtr->getVsyncRate(), + desiredMode.mode.fps); return DesiredModeAction::InitiateRenderRateSwitch; } @@ -566,21 +560,20 @@ auto DisplayDevice::setDesiredMode(const ActiveModeInfo& info, bool force) -> De activeMode.modePtr->getVsyncRate()); // Initiate a mode change. - mDesiredModeChanged = true; - mDesiredMode = info; + mDesiredModeOpt = std::move(desiredMode); + mHasDesiredModeTrace = true; return DesiredModeAction::InitiateDisplayModeSwitch; } -auto DisplayDevice::getDesiredMode() const -> ftl::Optional<ActiveModeInfo> { +auto DisplayDevice::getDesiredMode() const -> DisplayModeRequestOpt { std::scoped_lock lock(mDesiredModeLock); - if (mDesiredModeChanged) return mDesiredMode; - return std::nullopt; + return mDesiredModeOpt; } void DisplayDevice::clearDesiredMode() { std::scoped_lock lock(mDesiredModeLock); - mDesiredMode.event = scheduler::DisplayModeEvent::None; - mDesiredModeChanged = false; + mDesiredModeOpt.reset(); + mHasDesiredModeTrace = false; } void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index a061fcaa92..51c7be0490 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -186,35 +186,19 @@ public: * Display mode management. */ - // TODO(b/241285876): Replace ActiveModeInfo and DisplayModeEvent with DisplayModeRequest. - struct ActiveModeInfo { - using Event = scheduler::DisplayModeEvent; - - ActiveModeInfo() = default; - ActiveModeInfo(scheduler::FrameRateMode mode, Event event) - : modeOpt(std::move(mode)), event(event) {} - - explicit ActiveModeInfo(display::DisplayModeRequest&& request) - : ActiveModeInfo(std::move(request.mode), - request.emitEvent ? Event::Changed : Event::None) {} - - ftl::Optional<scheduler::FrameRateMode> modeOpt; - Event event = Event::None; - - bool operator!=(const ActiveModeInfo& other) const { - return modeOpt != other.modeOpt || event != other.event; - } - }; - enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch }; - DesiredModeAction setDesiredMode(const ActiveModeInfo&, bool force = false) + DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false) EXCLUDES(mDesiredModeLock); - ftl::Optional<ActiveModeInfo> getDesiredMode() const EXCLUDES(mDesiredModeLock); + using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; + + DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock); void clearDesiredMode() EXCLUDES(mDesiredModeLock); - ActiveModeInfo getPendingMode() const REQUIRES(kMainThreadContext) { return mPendingMode; } + DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) { + return mPendingModeOpt; + } bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; } scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) { @@ -223,7 +207,7 @@ public: void setActiveMode(DisplayModeId, Fps vsyncRate, Fps renderFps); - bool initiateModeChange(const ActiveModeInfo&, const hal::VsyncPeriodChangeConstraints&, + bool initiateModeChange(display::DisplayModeRequest&&, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline& outTimeline) REQUIRES(kMainThreadContext); @@ -316,10 +300,10 @@ private: float mHdrSdrRatio = 1.0f; mutable std::mutex mDesiredModeLock; - ActiveModeInfo mDesiredMode GUARDED_BY(mDesiredModeLock); - TracedOrdinal<bool> mDesiredModeChanged GUARDED_BY(mDesiredModeLock); + DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock); + TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock); - ActiveModeInfo mPendingMode GUARDED_BY(kMainThreadContext); + DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext); bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false; }; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 2d957e6334..369021995c 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -78,6 +78,7 @@ using AidlDisplayConnectionType = using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform; using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace; +using AidlDisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent; using AidlFRect = aidl::android::hardware::graphics::common::FRect; using AidlRect = aidl::android::hardware::graphics::common::Rect; using AidlTransform = aidl::android::hardware::graphics::common::Transform; @@ -174,9 +175,9 @@ public: AidlIComposerCallbackWrapper(HWC2::ComposerCallback& callback) : mCallback(callback) {} ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override { - const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED - : V2_4::IComposerCallback::Connection::DISCONNECTED; - mCallback.onComposerHalHotplug(translate<Display>(in_display), connection); + const auto event = in_connected ? AidlDisplayHotplugEvent::CONNECTED + : AidlDisplayHotplugEvent::DISCONNECTED; + mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event); return ::ndk::ScopedAStatus::ok(); } @@ -216,6 +217,12 @@ public: return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onHotplugEvent(int64_t in_display, + AidlDisplayHotplugEvent event) override { + mCallback.onComposerHalHotplugEvent(translate<Display>(in_display), event); + return ::ndk::ScopedAStatus::ok(); + } + private: HWC2::ComposerCallback& mCallback; }; @@ -1575,8 +1582,7 @@ void AidlComposer::onHotplugDisconnect(Display display) { } bool AidlComposer::hasMultiThreadedPresentSupport(Display display) { -#if 0 - // TODO (b/259132483): Reenable + if (!FlagManager::getInstance().multithreaded_present()) return false; const auto displayId = translate<int64_t>(display); std::vector<AidlDisplayCapability> capabilities; const auto status = mAidlComposerClient->getDisplayCapabilities(displayId, &capabilities); @@ -1586,10 +1592,6 @@ bool AidlComposer::hasMultiThreadedPresentSupport(Display display) { } return std::find(capabilities.begin(), capabilities.end(), AidlDisplayCapability::MULTI_THREADED_PRESENT) != capabilities.end(); -#else - (void) display; - return false; -#endif } void AidlComposer::addReader(Display display) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index e7f807f553..29fe38008d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -38,6 +38,7 @@ #include "Hal.h" #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <aidl/android/hardware/graphics/composer3/Capability.h> #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Color.h> @@ -64,15 +65,16 @@ class Layer; namespace hal = android::hardware::graphics::composer::hal; +using aidl::android::hardware::graphics::common::DisplayHotplugEvent; using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; // Implement this interface to receive hardware composer events. // // These callback functions will generally be called on a hwbinder thread, but -// when first registering the callback the onComposerHalHotplug() function will -// immediately be called on the thread calling registerCallback(). +// when first registering the callback the onComposerHalHotplugEvent() function +// will immediately be called on the thread calling registerCallback(). struct ComposerCallback { - virtual void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) = 0; + virtual void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) = 0; virtual void onComposerHalRefresh(hal::HWDisplayId) = 0; virtual void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp, std::optional<hal::VsyncPeriodNanos>) = 0; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 26c0d67af9..10df216b02 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -545,6 +545,7 @@ status_t HWComposer::getDeviceCompositionChanges( } } + ATRACE_FORMAT("NextFrameInterval %d_Hz", frameInterval.getIntValue()); if (canSkipValidate) { sp<Fence> outPresentFence = Fence::NO_FENCE; uint32_t state = UINT32_MAX; diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h index 20f7548bea..31c2833229 100644 --- a/services/surfaceflinger/DisplayHardware/Hal.h +++ b/services/surfaceflinger/DisplayHardware/Hal.h @@ -20,6 +20,7 @@ #include <android/hardware/graphics/composer/2.4/IComposer.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <aidl/android/hardware/graphics/common/Hdr.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> @@ -58,6 +59,7 @@ using Connection = IComposerCallback::Connection; using ContentType = IComposerClient::ContentType; using Capability = IComposer::Capability; using ClientTargetProperty = IComposerClient::ClientTargetProperty; +using DisplayHotplugEvent = aidl::android::hardware::graphics::common::DisplayHotplugEvent; using DisplayRequest = IComposerClient::DisplayRequest; using DisplayType = IComposerClient::DisplayType; using HWConfigId = V2_1::Config; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 1e7c6da592..ed52b9583c 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -25,6 +25,7 @@ #include "HidlComposerHal.h" #include <SurfaceFlingerProperties.h> +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <android/binder_manager.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <hidl/HidlTransportSupport.h> @@ -38,6 +39,7 @@ #include <algorithm> #include <cinttypes> +using aidl::android::hardware::graphics::common::DisplayHotplugEvent; using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; @@ -64,8 +66,13 @@ public: ComposerCallbackBridge(ComposerCallback& callback, bool vsyncSwitchingSupported) : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} + // For code sharing purposes, `ComposerCallback` (implemented by SurfaceFlinger) + // replaced `onComposerHalHotplug` with `onComposerHalHotplugEvent` by converting + // from HIDL's connection into an AIDL DisplayHotplugEvent. Return<void> onHotplug(Display display, Connection connection) override { - mCallback.onComposerHalHotplug(display, connection); + const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED + : DisplayHotplugEvent::DISCONNECTED; + mCallback.onComposerHalHotplugEvent(display, event); return Void(); } 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/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp index e55cd3ea42..55b395b458 100644 --- a/services/surfaceflinger/DisplayRenderArea.cpp +++ b/services/surfaceflinger/DisplayRenderArea.cpp @@ -18,41 +18,26 @@ #include "DisplayDevice.h" namespace android { -namespace { - -RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform, - const DisplayDevice& display) { - if (!useIdentityTransform) { - return RenderArea::RotationFlags::ROT_0; - } - - return ui::Transform::toRotationFlags(display.getOrientation()); -} - -} // namespace std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak, const Rect& sourceCrop, ui::Size reqSize, ui::Dataspace reqDataSpace, - bool useIdentityTransform, bool hintForSeamlessTransition, bool allowSecureLayers) { if (auto display = displayWeak.promote()) { // Using new to access a private constructor. return std::unique_ptr<DisplayRenderArea>( new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace, - useIdentityTransform, hintForSeamlessTransition, - allowSecureLayers)); + hintForSeamlessTransition, allowSecureLayers)); } return nullptr; } DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, ui::Size reqSize, ui::Dataspace reqDataSpace, - bool useIdentityTransform, bool hintForSeamlessTransition, - bool allowSecureLayers) + bool hintForSeamlessTransition, bool allowSecureLayers) : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition, - allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)), + allowSecureLayers), mDisplay(std::move(display)), mSourceCrop(sourceCrop) {} @@ -73,17 +58,7 @@ Rect DisplayRenderArea::getSourceCrop() const { if (mSourceCrop.isEmpty()) { return mDisplay->getLayerStackSpaceRect(); } - - // Correct for the orientation when the screen capture request contained - // useIdentityTransform. This will cause the rotation flag to be non 0 since - // it needs to rotate based on the screen orientation to allow the screenshot - // to be taken in the ROT_0 orientation - const auto flags = getRotationFlags(); - int width = mDisplay->getLayerStackSpaceRect().getWidth(); - int height = mDisplay->getLayerStackSpaceRect().getHeight(); - ui::Transform rotation; - rotation.set(flags, width, height); - return rotation.transform(mSourceCrop); + return mSourceCrop; } } // namespace android diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h index 9a4981c881..4555a9ed66 100644 --- a/services/surfaceflinger/DisplayRenderArea.h +++ b/services/surfaceflinger/DisplayRenderArea.h @@ -29,7 +29,6 @@ class DisplayRenderArea : public RenderArea { public: static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize, ui::Dataspace, - bool useIdentityTransform, bool hintForSeamlessTransition, bool allowSecureLayers = true); @@ -40,8 +39,7 @@ public: private: DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize, - ui::Dataspace, bool useIdentityTransform, bool hintForSeamlessTransition, - bool allowSecureLayers = true); + ui::Dataspace, bool hintForSeamlessTransition, bool allowSecureLayers = true); const sp<const DisplayDevice> mDisplay; const Rect mSourceCrop; 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/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 743cbf3cb1..a92cc03831 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -814,9 +814,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a RequestedLayerState::Changes::Hierarchy) || snapshot.changes.any(RequestedLayerState::Changes::FrameRate | RequestedLayerState::Changes::Hierarchy)) { - bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy == + const bool shouldOverrideChildren = parentSnapshot.frameRateSelectionStrategy == scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren; - if (!requested.requestedFrameRate.isValid() || shouldOverrideChildren) { + const bool propagationAllowed = parentSnapshot.frameRateSelectionStrategy != + scheduler::LayerInfo::FrameRateSelectionStrategy::Self; + if ((!requested.requestedFrameRate.isValid() && propagationAllowed) || + shouldOverrideChildren) { snapshot.inheritedFrameRate = parentSnapshot.inheritedFrameRate; } else { snapshot.inheritedFrameRate = requested.requestedFrameRate; @@ -828,12 +831,15 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionStrategyChanged) { - const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy( - requested.frameRateSelectionStrategy); - snapshot.frameRateSelectionStrategy = - strategy == scheduler::LayerInfo::FrameRateSelectionStrategy::Self - ? parentSnapshot.frameRateSelectionStrategy - : strategy; + if (parentSnapshot.frameRateSelectionStrategy == + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren) { + snapshot.frameRateSelectionStrategy = + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren; + } else { + const auto strategy = scheduler::LayerInfo::convertFrameRateSelectionStrategy( + requested.frameRateSelectionStrategy); + snapshot.frameRateSelectionStrategy = strategy; + } } if (forceUpdate || snapshot.clientChanges & layer_state_t::eFrameRateSelectionPriority) { diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index b1a18aefb5..21172ca015 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -125,7 +125,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default); frameRateCategorySmoothSwitchOnly = false; frameRateSelectionStrategy = - static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); dataspace = ui::Dataspace::V0_SRGB; gameMode = gui::GameMode::Unsupported; requestedFrameRate = {}; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index b9561dcb0c..f7800bb485 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -193,7 +193,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.dropInputMode = gui::DropInputMode::NONE; mDrawingState.dimmingEnabled = true; mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default; - mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Self; + mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate; if (args.flags & ISurfaceComposerClient::eNoColorFill) { // Set an invalid color so there is no color fill. @@ -1273,14 +1273,15 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overr auto now = systemTime(); *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now); - // The frame rate is propagated to the children + // The frame rate is propagated to the children by default, but some properties may override it. bool childrenHaveFrameRate = false; + const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate(); + const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate; for (const sp<Layer>& child : mCurrentChildren) { childrenHaveFrameRate |= - child->propagateFrameRateForLayerTree(frameRate, - overrideChildren || - shouldOverrideChildrenFrameRate(), - transactionNeeded); + child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate + : FrameRate(), + overrideChildrenFrameRate, transactionNeeded); } // If we don't have a valid frame rate specification, but the children do, we set this @@ -1718,10 +1719,18 @@ void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapsho StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, crop.bottom); const auto frameRate = snapshot.frameRate; + std::string frameRateStr; + if (frameRate.vote.rate.isValid()) { + StringAppendF(&frameRateStr, "%.2f", frameRate.vote.rate.getValue()); + } if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) { - StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(), + StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(), ftl::enum_string(frameRate.vote.type).c_str(), ftl::enum_string(frameRate.vote.seamlessness).c_str()); + } else if (frameRate.category != FrameRateCategory::Default) { + StringAppendF(&result, "%6s %15s %17s", frameRateStr.c_str(), + (std::string("Cat::") + ftl::enum_string(frameRate.category)).c_str(), + ftl::enum_string(frameRate.vote.seamlessness).c_str()); } else { result.append(41, ' '); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 28168c3f65..c772e0ea1b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -1178,6 +1178,10 @@ private: FrameRateSelectionStrategy::OverrideChildren; } + bool shouldPropagateFrameRate() const { + return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self; + } + // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is // a transform from the current layer coordinate space to display(screen) coordinate space. diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 8f658d5a09..6db39f143f 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -276,13 +276,11 @@ void RegionSamplingThread::captureSample() { } const Rect sampledBounds = sampleRegion.bounds(); - constexpr bool kUseIdentityTransform = false; constexpr bool kHintForSeamlessTransition = false; SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] { return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(), - ui::Dataspace::V0_SRGB, kUseIdentityTransform, - kHintForSeamlessTransition); + ui::Dataspace::V0_SRGB, kHintForSeamlessTransition); }); std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 71b85bd3b2..5de148e3bd 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -18,20 +18,16 @@ class DisplayDevice; // physical render area. class RenderArea { public: - using RotationFlags = ui::Transform::RotationFlags; - enum class CaptureFill {CLEAR, OPAQUE}; static float getCaptureFillValue(CaptureFill captureFill); RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace, - bool hintForSeamlessTransition, bool allowSecureLayers = false, - RotationFlags rotation = ui::Transform::ROT_0) + bool hintForSeamlessTransition, bool allowSecureLayers = false) : mAllowSecureLayers(allowSecureLayers), mReqSize(reqSize), mReqDataSpace(reqDataSpace), mCaptureFill(captureFill), - mRotationFlags(rotation), mHintForSeamlessTransition(hintForSeamlessTransition) {} static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda( @@ -72,9 +68,6 @@ public: // on the display). virtual Rect getSourceCrop() const = 0; - // Returns the rotation of the source crop and the layers. - RotationFlags getRotationFlags() const { return mRotationFlags; } - // Returns the size of the physical render area. int getReqWidth() const { return mReqSize.width; } int getReqHeight() const { return mReqSize.height; } @@ -103,7 +96,6 @@ private: const ui::Size mReqSize; const ui::Dataspace mReqDataSpace; const CaptureFill mCaptureFill; - const RotationFlags mRotationFlags; const bool mHintForSeamlessTransition; }; diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp index 6d2586ae9c..d71484889d 100644 --- a/services/surfaceflinger/Scheduler/Android.bp +++ b/services/surfaceflinger/Scheduler/Android.bp @@ -21,6 +21,7 @@ cc_defaults { "libui", "libutils", ], + static_libs: ["libsurfaceflinger_common"], } cc_library_headers { @@ -61,5 +62,9 @@ cc_test { "libgmock", "libgtest", "libscheduler", + "libsurfaceflingerflags_test", + ], + shared_libs: [ + "server_configurable_flags", ], } 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 bf3a7bc8b7..97fca395b4 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -276,17 +276,16 @@ std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(const RefreshRateSe if (const auto averageFrameTime = calculateAverageFrameTime()) { const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime); - const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); - if (refreshRateConsistent) { - const auto knownRefreshRate = selector.findClosestKnownFrameRate(refreshRate); + const auto closestKnownRefreshRate = mRefreshRateHistory.add(refreshRate, now, selector); + if (closestKnownRefreshRate.isValid()) { using fps_approx_ops::operator!=; // To avoid oscillation, use the last calculated refresh rate if it is close enough. if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) > MARGIN && - mLastRefreshRate.reported != knownRefreshRate) { + mLastRefreshRate.reported != closestKnownRefreshRate) { mLastRefreshRate.calculated = refreshRate; - mLastRefreshRate.reported = knownRefreshRate; + mLastRefreshRate.reported = closestKnownRefreshRate; } ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(), @@ -432,7 +431,8 @@ void LayerInfo::RefreshRateHistory::clear() { mRefreshRates.clear(); } -bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) { +Fps LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now, + const RefreshRateSelector& selector) { mRefreshRates.push_back({refreshRate, now}); while (mRefreshRates.size() >= HISTORY_SIZE || now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) { @@ -447,11 +447,11 @@ bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) { ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue()); } - return isConsistent(); + return selectRefreshRate(selector); } -bool LayerInfo::RefreshRateHistory::isConsistent() const { - if (mRefreshRates.empty()) return true; +Fps LayerInfo::RefreshRateHistory::selectRefreshRate(const RefreshRateSelector& selector) const { + if (mRefreshRates.empty()) return Fps(); const auto [min, max] = std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(), @@ -459,8 +459,19 @@ bool LayerInfo::RefreshRateHistory::isConsistent() const { return isStrictlyLess(lhs.refreshRate, rhs.refreshRate); }); - const bool consistent = - max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS; + const auto maxClosestRate = selector.findClosestKnownFrameRate(max->refreshRate); + const bool consistent = [&](Fps maxFps, Fps minFps) { + if (FlagManager::getInstance().use_known_refresh_rate_for_fps_consistency()) { + if (maxFps.getValue() - minFps.getValue() < + MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE) { + const auto minClosestRate = selector.findClosestKnownFrameRate(minFps); + using fps_approx_ops::operator==; + return maxClosestRate == minClosestRate; + } + return false; + } + return maxFps.getValue() - minFps.getValue() < MARGIN_CONSISTENT_FPS; + }(max->refreshRate, min->refreshRate); if (CC_UNLIKELY(sTraceEnabled)) { if (!mHeuristicTraceTagData.has_value()) { @@ -472,7 +483,7 @@ bool LayerInfo::RefreshRateHistory::isConsistent() const { ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent); } - return consistent; + return consistent ? maxClosestRate : Fps(); } FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) { @@ -485,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: @@ -526,10 +539,12 @@ FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) { LayerInfo::FrameRateSelectionStrategy LayerInfo::convertFrameRateSelectionStrategy( int8_t strategy) { switch (strategy) { - case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF: - return FrameRateSelectionStrategy::Self; + case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE: + return FrameRateSelectionStrategy::Propagate; case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN: return FrameRateSelectionStrategy::OverrideChildren; + case ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_SELF: + return FrameRateSelectionStrategy::Self; default: LOG_ALWAYS_FATAL("Invalid frame rate selection strategy value %d", strategy); return FrameRateSelectionStrategy::Self; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index d24fc33491..50bb83dc29 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -81,10 +81,11 @@ public: using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>; enum class FrameRateSelectionStrategy { - Self, + Propagate, OverrideChildren, + Self, - ftl_last = OverrideChildren + ftl_last = Self }; // Encapsulates the frame rate specifications of the layer. This information will be used @@ -264,8 +265,8 @@ private: // Clears History void clear(); - // Adds a new refresh rate and returns true if it is consistent - bool add(Fps refreshRate, nsecs_t now); + // Adds a new refresh rate and returns valid refresh rate if it is consistent enough + Fps add(Fps refreshRate, nsecs_t now, const RefreshRateSelector&); private: friend class LayerHistoryTest; @@ -285,13 +286,14 @@ private: std::string average; }; - bool isConsistent() const; + Fps selectRefreshRate(const RefreshRateSelector&) const; HeuristicTraceTagData makeHeuristicTraceTagData() const; const std::string mName; mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData; std::deque<RefreshRateData> mRefreshRates; static constexpr float MARGIN_CONSISTENT_FPS = 1.0; + static constexpr float MARGIN_CONSISTENT_FPS_FOR_CLOSEST_REFRESH_RATE = 5.0; }; // Represents whether we were able to determine either layer is frequent or infrequent 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..a1a7c289cf 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -39,13 +39,6 @@ namespace android::scheduler { using namespace std::chrono_literals; -enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 }; - -inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) { - using T = std::underlying_type_t<DisplayModeEvent>; - return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs)); -} - using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; // Selects the refresh rate of a display by ranking its `DisplayModes` in accordance with @@ -149,6 +142,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 +454,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/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 25d9f577de..aa8d54d69e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -92,7 +92,12 @@ void Scheduler::startTimers() { using namespace sysprop; using namespace std::string_literals; - if (const int64_t millis = set_touch_timer_ms(0); millis > 0) { + const int32_t defaultTouchTimerValue = + FlagManager::getInstance().enable_fro_dependent_features() && + sysprop::enable_frame_rate_override(true) + ? 200 + : 0; + if (const int32_t millis = set_touch_timer_ms(defaultTouchTimerValue); millis > 0) { // Touch events are coming to SF every 100ms, so the timer needs to be higher than that mTouchTimer.emplace( "TouchTimer", std::chrono::milliseconds(millis), @@ -118,7 +123,7 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { auto schedulePtr = std::make_shared<VsyncSchedule>( - displayId, mFeatures, + selectorPtr->getActiveMode().modePtr, mFeatures, [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, mVsyncTrackerCallback); @@ -181,32 +186,33 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, .expectedVsyncTime = expectedVsyncTime, .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration}; - LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId); - const auto pacesetterId = *mPacesetterDisplayId; - const auto pacesetterOpt = mDisplays.get(pacesetterId); + ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked(); + pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr); - FrameTargeter& pacesetterTargeter = *pacesetterOpt->get().targeterPtr; - pacesetterTargeter.beginFrame(beginFrameArgs, *pacesetterOpt->get().schedulePtr); + { + FrameTargets targets; + targets.try_emplace(pacesetterPtr->displayId, &pacesetterPtr->targeterPtr->target()); - FrameTargets targets; - targets.try_emplace(pacesetterId, &pacesetterTargeter.target()); + for (const auto& [id, display] : mDisplays) { + if (id == pacesetterPtr->displayId) continue; - for (const auto& [id, display] : mDisplays) { - if (id == pacesetterId) continue; + FrameTargeter& targeter = *display.targeterPtr; + targeter.beginFrame(beginFrameArgs, *display.schedulePtr); + targets.try_emplace(id, &targeter.target()); + } - FrameTargeter& targeter = *display.targeterPtr; - targeter.beginFrame(beginFrameArgs, *display.schedulePtr); - targets.try_emplace(id, &targeter.target()); + if (!compositor.commit(pacesetterPtr->displayId, targets)) return; } - if (!compositor.commit(pacesetterId, targets)) return; + // The pacesetter may have changed or been registered anew during commit. + pacesetterPtr = pacesetterPtrLocked(); // TODO(b/256196556): Choose the frontrunner display. FrameTargeters targeters; - targeters.try_emplace(pacesetterId, &pacesetterTargeter); + targeters.try_emplace(pacesetterPtr->displayId, pacesetterPtr->targeterPtr.get()); for (auto& [id, display] : mDisplays) { - if (id == pacesetterId) continue; + if (id == pacesetterPtr->displayId) continue; FrameTargeter& targeter = *display.targeterPtr; targeters.try_emplace(id, &targeter); @@ -214,7 +220,7 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, if (FlagManager::getInstance().vrr_config() && CC_UNLIKELY(mPacesetterFrameDurationFractionToSkip > 0.f)) { - const auto period = pacesetterTargeter.target().expectedFrameDuration(); + const auto period = pacesetterPtr->targeterPtr->target().expectedFrameDuration(); const auto skipDuration = Duration::fromNs( static_cast<nsecs_t>(period.ns() * mPacesetterFrameDurationFractionToSkip)); ATRACE_FORMAT("Injecting jank for %f%% of the frame (%" PRId64 " ns)", @@ -223,7 +229,19 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, mPacesetterFrameDurationFractionToSkip = 0.f; } - const auto resultsPerDisplay = compositor.composite(pacesetterId, targeters); + if (FlagManager::getInstance().vrr_config()) { + const auto minFramePeriod = pacesetterPtr->schedulePtr->minFramePeriod(); + const auto presentFenceForPastVsync = + pacesetterPtr->targeterPtr->target().presentFenceForPastVsync(minFramePeriod); + const auto lastConfirmedPresentTime = presentFenceForPastVsync->getSignalTime(); + if (lastConfirmedPresentTime != Fence::SIGNAL_TIME_PENDING && + lastConfirmedPresentTime != Fence::SIGNAL_TIME_INVALID) { + pacesetterPtr->schedulePtr->getTracker() + .onFrameBegin(expectedVsyncTime, TimePoint::fromNs(lastConfirmedPresentTime)); + } + } + + const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters); compositor.sample(); for (const auto& [id, targeter] : targeters) { @@ -497,13 +515,16 @@ void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) { std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); - for (const auto& [id, _] : mDisplays) { - resyncToHardwareVsyncLocked(id, allowToEnable); + for (const auto& [id, display] : mDisplays) { + if (display.powerMode != hal::PowerMode::OFF || + !FlagManager::getInstance().multithreaded_present()) { + resyncToHardwareVsyncLocked(id, allowToEnable); + } } } void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable, - std::optional<Fps> refreshRate) { + DisplayModePtr modePtr) { const auto displayOpt = mDisplays.get(id); if (!displayOpt) { ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str()); @@ -512,12 +533,12 @@ void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEn const Display& display = *displayOpt; if (display.schedulePtr->isHardwareVsyncAllowed(allowToEnable)) { - if (!refreshRate) { - refreshRate = display.selectorPtr->getActiveMode().modePtr->getVsyncRate(); + if (!modePtr) { + modePtr = display.selectorPtr->getActiveMode().modePtr.get(); } - if (refreshRate->isValid()) { + if (modePtr->getVsyncRate().isValid()) { constexpr bool kForce = false; - display.schedulePtr->startPeriodTransition(refreshRate->getPeriod(), kForce); + display.schedulePtr->onDisplayModeChanged(ftl::as_non_null(modePtr), kForce); } } } @@ -563,19 +584,25 @@ void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), to_string(mode.modePtr->getVsyncRate()).c_str()); - display.schedulePtr->getTracker().setDisplayModeData( - {.renderRate = renderFrameRate, - .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)}); + display.schedulePtr->getTracker().setRenderRate(renderFrameRate); } -std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) { - if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) { - return Period::fromNs( - mode.modePtr->getVrrConfig() - ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); - } else { - return std::nullopt; +Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id, + TimePoint currentExpectedPresentTime) const { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + + const auto displayOpt = mDisplays.get(id); + if (!displayOpt) { + ALOGW("%s: Invalid display %s!", __func__, to_string(id).c_str()); + return Fps{}; } + const Display& display = *displayOpt; + const nsecs_t threshold = + display.selectorPtr->getActiveMode().modePtr->getVsyncRate().getPeriodNsecs() / 2; + const nsecs_t nextVsyncTime = display.schedulePtr->getTracker().nextAnticipatedVSyncTimeFrom( + currentExpectedPresentTime.ns() + threshold); + return Fps::fromPeriodNsecs(nextVsyncTime - currentExpectedPresentTime.ns()); } void Scheduler::resync() { @@ -913,9 +940,9 @@ std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked( newVsyncSchedulePtr = pacesetter.schedulePtr; - const Fps refreshRate = pacesetter.selectorPtr->getActiveMode().modePtr->getVsyncRate(); constexpr bool kForce = true; - newVsyncSchedulePtr->startPeriodTransition(refreshRate.getPeriod(), kForce); + newVsyncSchedulePtr->onDisplayModeChanged(pacesetter.selectorPtr->getActiveMode().modePtr, + kForce); } return newVsyncSchedulePtr; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 1773d090dc..454ef83632 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -33,6 +33,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" #include <ftl/fake_guard.h> +#include <ftl/non_null.h> #include <ftl/optional.h> #include <scheduler/Features.h> #include <scheduler/FrameTargeter.h> @@ -210,13 +211,12 @@ public: // If allowToEnable is true, then hardware vsync will be turned on. // Otherwise, if hardware vsync is not already enabled then this method will // no-op. - // If refreshRate is nullopt, use the existing refresh rate of the display. + // If modePtr is nullopt, use the active display mode. void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable, - std::optional<Fps> refreshRate = std::nullopt) - EXCLUDES(mDisplayLock) { + DisplayModePtr modePtr = nullptr) EXCLUDES(mDisplayLock) { std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); - resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate); + resyncToHardwareVsyncLocked(id, allowToEnable, modePtr); } void forceNextResync() { mLastResyncTime = 0; } @@ -313,6 +313,9 @@ public: return pacesetterSelectorPtr()->getActiveMode().fps; } + Fps getNextFrameInterval(PhysicalDisplayId, TimePoint currentExpectedPresentTime) const + EXCLUDES(mDisplayLock); + // Returns the framerate of the layer with the given sequence ID float getLayerFramerate(nsecs_t now, int32_t id) const { return mLayerHistory.getLayerFramerate(now, id); @@ -355,7 +358,7 @@ private: void onHardwareVsyncRequest(PhysicalDisplayId, bool enable); void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable, - std::optional<Fps> refreshRate = std::nullopt) + DisplayModePtr modePtr = nullptr) REQUIRES(kMainThreadContext, mDisplayLock); void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock); void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod); @@ -432,9 +435,6 @@ private: Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); - std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&) - REQUIRES(mDisplayLock); - // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; @@ -521,13 +521,17 @@ private: }); } + // The pacesetter must exist as a precondition. + ftl::NonNull<const Display*> pacesetterPtrLocked() const REQUIRES(mDisplayLock) { + return ftl::as_non_null(&pacesetterDisplayLocked()->get()); + } + RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) { std::scoped_lock lock(mDisplayLock); return pacesetterSelectorPtrLocked(); } RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) { - ftl::FakeGuard guard(kMainThreadContext); return pacesetterDisplayLocked() .transform([](const Display& display) { return display.selectorPtr; }) .or_else([] { return std::optional<RefreshRateSelectorPtr>(nullptr); }) diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index f5f93ce2f1..7379a4605d 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -47,16 +47,16 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; -VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize, +VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, IVsyncTrackerCallback& callback) - : mId(id), + : mId(modePtr->getPhysicalDisplayId()), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), mVsyncTrackerCallback(callback), - mIdealPeriod(idealPeriod) { + mDisplayModePtr(modePtr) { resetModel(); } @@ -74,13 +74,18 @@ inline size_t VSyncPredictor::next(size_t i) const { return (i + 1) % mTimestamps.size(); } +nsecs_t VSyncPredictor::idealPeriod() const { + return mDisplayModePtr->getVsyncRate().getPeriodNsecs(); +} + bool VSyncPredictor::validate(nsecs_t timestamp) const { if (mLastTimestampIndex < 0 || mTimestamps.empty()) { return true; } - auto const aValidTimestamp = mTimestamps[mLastTimestampIndex]; - auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod; + const auto aValidTimestamp = mTimestamps[mLastTimestampIndex]; + const auto percent = + (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { return false; @@ -90,7 +95,7 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { [timestamp](nsecs_t a, nsecs_t b) { return std::abs(timestamp - a) < std::abs(timestamp - b); }); - const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / mIdealPeriod; + const auto distancePercent = std::abs(*iter - timestamp) * kMaxPercent / idealPeriod(); if (distancePercent < kOutlierTolerancePercent) { // duplicate timestamp return false; @@ -100,7 +105,24 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { nsecs_t VSyncPredictor::currentPeriod() const { std::lock_guard lock(mMutex); - return mRateMap.find(mIdealPeriod)->second.slope; + return mRateMap.find(idealPeriod())->second.slope; +} + +Period VSyncPredictor::minFramePeriod() const { + if (!FlagManager::getInstance().vrr_config()) { + return Period::fromNs(currentPeriod()); + } + + std::lock_guard lock(mMutex); + return minFramePeriodLocked(); +} + +Period VSyncPredictor::minFramePeriodLocked() const { + const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs(); + const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) / + static_cast<float>(idealPeriod()))); + const auto slope = mRateMap.find(idealPeriod())->second.slope; + return Period::fromNs(slope * numPeriods); } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { @@ -137,7 +159,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { const size_t numSamples = mTimestamps.size(); if (numSamples < kMinimumSamplesForPrediction) { - mRateMap[mIdealPeriod] = {mIdealPeriod, 0}; + mRateMap[idealPeriod()] = {idealPeriod(), 0}; return true; } @@ -161,7 +183,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // Normalizing to the oldest timestamp cuts down on error in calculating the intercept. const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end()); - auto it = mRateMap.find(mIdealPeriod); + auto it = mRateMap.find(idealPeriod()); auto const currentPeriod = it->second.slope; // The mean of the ordinals must be precise for the intercept calculation, so scale them up for @@ -199,7 +221,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { } if (CC_UNLIKELY(bottom == 0)) { - it->second = {mIdealPeriod, 0}; + it->second = {idealPeriod(), 0}; clearTimestamps(); return false; } @@ -207,9 +229,9 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom; nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor); - auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod; + auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent) { - it->second = {mIdealPeriod, 0}; + it->second = {idealPeriod(), 0}; clearTimestamps(); return false; } @@ -241,8 +263,8 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) co if (mTimestamps.empty()) { traceInt64("VSP-mode", 1); auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint; - auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1; - return knownTimestamp + numPeriodsOut * mIdealPeriod; + auto const numPeriodsOut = ((timePoint - knownTimestamp) / idealPeriod()) + 1; + return knownTimestamp + numPeriodsOut * idealPeriod(); } auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end()); @@ -278,27 +300,32 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { mLastVsyncSequence = getVsyncSequenceLocked(timePoint); const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mDisplayModeDataOpt) return 0; - + if (!mRenderRateOpt) return 0; const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), - mDisplayModeDataOpt->renderRate); + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), + *mRenderRateOpt); if (divisor <= 1) return 0; - const int mod = mLastVsyncSequence->seq % divisor; + int mod = mLastVsyncSequence->seq % divisor; if (mod == 0) return 0; + // This is actually a bug fix, but guarded with vrr_config since we found it with this + // config + if (FlagManager::getInstance().vrr_config()) { + if (mod < 0) mod += divisor; + } + return divisor - mod; }(); if (renderRatePhase == 0) { const auto vsyncTime = mLastVsyncSequence->vsyncTime; - if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + if (FlagManager::getInstance().vrr_config()) { const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__, ticks<std::milli, float>(vsyncTimePoint - TimePoint::now())); - mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt, - Period::fromNs(mIdealPeriod)); + const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); + mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); } return vsyncTime; } @@ -307,12 +334,13 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; const auto nextAnticipatedVsyncTime = nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); - if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + if (FlagManager::getInstance().vrr_config()) { const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime); ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__, ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now())); - mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint, - *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod)); + const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); + mVsyncTrackerCallback.onVsyncGenerated(nextAnticipatedVsyncTimePoint, mDisplayModePtr, + renderRate); } return nextAnticipatedVsyncTime; } @@ -328,7 +356,8 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const { std::lock_guard lock(mMutex); const auto divisor = - RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), frameRate); + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(idealPeriod()), + frameRate); return isVSyncInPhaseLocked(timePoint, static_cast<unsigned>(divisor)); } @@ -344,7 +373,7 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return true; } - const nsecs_t period = mRateMap[mIdealPeriod].slope; + const nsecs_t period = mRateMap[idealPeriod()].slope; const nsecs_t justBeforeTimePoint = timePoint - period / 2; const auto vsyncSequence = getVsyncSequenceLocked(justBeforeTimePoint); ATRACE_FORMAT_INSTANT("vsync in: %.2f sequence: %" PRId64, @@ -352,45 +381,139 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) { - ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), - to_string(displayModeData.renderRate).c_str(), - displayModeData.notifyExpectedPresentTimeoutOpt - ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str() - : "N/A"); +void VSyncPredictor::setRenderRate(Fps renderRate) { + ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str()); + ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str()); std::lock_guard lock(mMutex); - mDisplayModeDataOpt = displayModeData; + mRenderRateOpt = renderRate; } -VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { +void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { + LOG_ALWAYS_FATAL_IF(mId != modePtr->getPhysicalDisplayId(), + "mode does not belong to the display"); + ATRACE_FORMAT("%s %s", __func__, to_string(*modePtr).c_str()); + const auto timeout = modePtr->getVrrConfig() + ? modePtr->getVrrConfig()->notifyExpectedPresentConfig + : std::nullopt; + ALOGV("%s %s: DisplayMode %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), + to_string(*modePtr).c_str(), + timeout ? std::to_string(timeout->notifyExpectedPresentTimeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); - const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); - return {model.slope, model.intercept}; -} -VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { - return mRateMap.find(mIdealPeriod)->second; -} + mDisplayModePtr = modePtr; + traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); -void VSyncPredictor::setPeriod(nsecs_t period) { - ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str()); - traceInt64("VSP-setPeriod", period); - - std::lock_guard lock(mMutex); static constexpr size_t kSizeLimit = 30; if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) { mRateMap.erase(mRateMap.begin()); } - // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData - mIdealPeriod = period; - if (mRateMap.find(period) == mRateMap.end()) { - mRateMap[mIdealPeriod] = {period, 0}; + if (mRateMap.find(idealPeriod()) == mRateMap.end()) { + mRateMap[idealPeriod()] = {idealPeriod(), 0}; } clearTimestamps(); } +void VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, + TimePoint lastConfirmedPresentTime) { + const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; + const auto threshold = currentPeriod / 2; + const auto minFramePeriod = minFramePeriodLocked().ns(); + + auto prev = lastConfirmedPresentTime.ns(); + for (auto& current : mPastExpectedPresentTimes) { + if (CC_UNLIKELY(mTraceOn)) { + ATRACE_FORMAT_INSTANT("current %.2f past last signaled fence", + static_cast<float>(current.ns() - lastConfirmedPresentTime.ns()) / + 1e6f); + } + + const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod; + if (minPeriodViolation) { + ATRACE_NAME("minPeriodViolation"); + current = TimePoint::fromNs(prev + minFramePeriod); + prev = current.ns(); + } else { + break; + } + } + + if (!mPastExpectedPresentTimes.empty()) { + const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime); + if (phase > 0ns) { + if (mLastVsyncSequence) { + mLastVsyncSequence->vsyncTime += phase.ns(); + } + } + } +} + +void VSyncPredictor::onFrameBegin(TimePoint expectedPresentTime, + TimePoint lastConfirmedPresentTime) { + ATRACE_CALL(); + std::lock_guard lock(mMutex); + + if (!mDisplayModePtr->getVrrConfig()) return; + + if (CC_UNLIKELY(mTraceOn)) { + ATRACE_FORMAT_INSTANT("vsync is %.2f past last signaled fence", + static_cast<float>(expectedPresentTime.ns() - + lastConfirmedPresentTime.ns()) / + 1e6f); + } + mPastExpectedPresentTimes.push_back(expectedPresentTime); + + const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; + const auto threshold = currentPeriod / 2; + + const auto minFramePeriod = minFramePeriodLocked().ns(); + while (!mPastExpectedPresentTimes.empty()) { + const auto front = mPastExpectedPresentTimes.front().ns(); + const bool frontIsLastConfirmed = + std::abs(front - lastConfirmedPresentTime.ns()) < threshold; + const bool frontIsBeforeConfirmed = + front < lastConfirmedPresentTime.ns() - minFramePeriod + threshold; + if (frontIsLastConfirmed || frontIsBeforeConfirmed) { + if (CC_UNLIKELY(mTraceOn)) { + ATRACE_FORMAT_INSTANT("Discarding old vsync - %.2f before last signaled fence", + static_cast<float>(lastConfirmedPresentTime.ns() - + mPastExpectedPresentTimes.front().ns()) / + 1e6f); + } + mPastExpectedPresentTimes.pop_front(); + } else { + break; + } + } + + ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); +} + +void VSyncPredictor::onFrameMissed(TimePoint expectedPresentTime) { + ATRACE_CALL(); + + std::lock_guard lock(mMutex); + if (!mDisplayModePtr->getVrrConfig()) return; + + // We don't know when the frame is going to be presented, so we assume it missed one vsync + const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope; + const auto lastConfirmedPresentTime = + TimePoint::fromNs(expectedPresentTime.ns() + currentPeriod); + + ensureMinFrameDurationIsKept(expectedPresentTime, lastConfirmedPresentTime); +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { + std::lock_guard lock(mMutex); + const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); + return {model.slope, model.intercept}; +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { + return mRateMap.find(idealPeriod())->second; +} + void VSyncPredictor::clearTimestamps() { if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); @@ -412,18 +535,18 @@ bool VSyncPredictor::needsMoreSamples() const { void VSyncPredictor::resetModel() { std::lock_guard lock(mMutex); - mRateMap[mIdealPeriod] = {mIdealPeriod, 0}; + mRateMap[idealPeriod()] = {idealPeriod(), 0}; clearTimestamps(); } void VSyncPredictor::dump(std::string& result) const { std::lock_guard lock(mMutex); - StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f); + StringAppendF(&result, "\tmDisplayModePtr=%s\n", to_string(*mDisplayModePtr).c_str()); StringAppendF(&result, "\tRefresh Rate Map:\n"); - for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) { + for (const auto& [period, periodInterceptTuple] : mRateMap) { StringAppendF(&result, "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n", - idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f, + period / 1e6f, periodInterceptTuple.slope / 1e6f, periodInterceptTuple.intercept); } } diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index c271eb738e..72a343112a 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -16,6 +16,7 @@ #pragma once +#include <deque> #include <mutex> #include <unordered_map> #include <vector> @@ -31,14 +32,14 @@ class VSyncPredictor : public VSyncTracker { public: /* * \param [in] PhysicalDisplayid The display this corresponds to. - * \param [in] idealPeriod The initial ideal period to use. + * \param [in] modePtr The initial display mode * \param [in] historySize The internal amount of entries to store in the model. * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ - VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize, + VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, IVsyncTrackerCallback&); ~VSyncPredictor(); @@ -46,17 +47,9 @@ public: bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex); nsecs_t currentPeriod() const final EXCLUDES(mMutex); + Period minFramePeriod() const final EXCLUDES(mMutex); void resetModel() final EXCLUDES(mMutex); - /* - * Inform the model that the period is anticipated to change to a new value. - * model will use the period parameter to predict vsync events until enough - * timestamps with the new period have been collected. - * - * \param [in] period The new period that should be used. - */ - void setPeriod(nsecs_t period) final EXCLUDES(mMutex); - /* Query if the model is in need of more samples to make a prediction. * \return True, if model would benefit from more samples, False if not. */ @@ -71,7 +64,13 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); - void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex); + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex); + + void setRenderRate(Fps) final EXCLUDES(mMutex); + + void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final + EXCLUDES(mMutex); + void onFrameMissed(TimePoint expectedPresentTime) final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); @@ -90,12 +89,15 @@ private: Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); bool isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) const REQUIRES(mMutex); + Period minFramePeriodLocked() const REQUIRES(mMutex); + void ensureMinFrameDurationIsKept(TimePoint, TimePoint) REQUIRES(mMutex); struct VsyncSequence { nsecs_t vsyncTime; int64_t seq; }; VsyncSequence getVsyncSequenceLocked(nsecs_t timestamp) const REQUIRES(mMutex); + nsecs_t idealPeriod() const REQUIRES(mMutex); bool const mTraceOn; size_t const kHistorySize; @@ -104,7 +106,6 @@ private: IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; - nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); // Map between ideal vsync period and the calculated model @@ -113,9 +114,12 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); - std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex); + ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex); + std::optional<Fps> mRenderRateOpt GUARDED_BY(mMutex); mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); + + std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex); }; } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 2938aa3fb3..24737e4fb2 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -116,32 +116,34 @@ void VSyncReactor::updateIgnorePresentFencesInternal() { } } -void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { +void VSyncReactor::startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr> modePtr) { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodConfirmationInProgress = true; - mPeriodTransitioningTo = newPeriod; + mModePtrTransitioningTo = modePtr.get(); mMoreSamplesNeeded = true; setIgnorePresentFencesInternal(true); } void VSyncReactor::endPeriodTransition() { ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); - mPeriodTransitioningTo.reset(); + mModePtrTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); } -void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) { - ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period); +void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) { + ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), + modePtr->getVsyncRate().getPeriodNsecs()); std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) { + if (!mSupportKernelIdleTimer && + modePtr->getVsyncRate().getPeriodNsecs() == mTracker.currentPeriod() && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; } else { - startPeriodTransitionInternal(period); + startPeriodTransitionInternal(modePtr); } } @@ -159,14 +161,16 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return false; } - const bool periodIsChanging = - mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod()); + const std::optional<Period> newPeriod = mModePtrTransitioningTo + ? mModePtrTransitioningTo->getVsyncRate().getPeriod() + : std::optional<Period>{}; + const bool periodIsChanging = newPeriod && (newPeriod->ns() != mTracker.currentPeriod()); if (mSupportKernelIdleTimer && !periodIsChanging) { // Clear out the Composer-provided period and use the allowance logic below HwcVsyncPeriod = {}; } - auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod(); + auto const period = newPeriod ? newPeriod->ns() : mTracker.currentPeriod(); static constexpr int allowancePercent = 10; static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio; auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den; @@ -185,8 +189,8 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> std::lock_guard lock(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); - if (mPeriodTransitioningTo) { - mTracker.setPeriod(*mPeriodTransitioningTo); + if (mModePtrTransitioningTo) { + mTracker.setDisplayModePtr(ftl::as_non_null(mModePtrTransitioningTo)); *periodFlushed = true; } @@ -228,10 +232,11 @@ void VSyncReactor::dump(std::string& result) const { mInternalIgnoreFences, mExternalIgnoreFences); StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n", mMoreSamplesNeeded, mPeriodConfirmationInProgress); - if (mPeriodTransitioningTo) { - StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo); + if (mModePtrTransitioningTo) { + StringAppendF(&result, "mModePtrTransitioningTo=%s\n", + to_string(*mModePtrTransitioningTo).c_str()); } else { - StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n"); + StringAppendF(&result, "mModePtrTransitioningTo=nullptr\n"); } if (mLastHwVsync) { diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index f2302422ad..2415a66a64 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -27,6 +27,7 @@ #include <scheduler/TimeKeeper.h> +#include "VSyncTracker.h" #include "VsyncController.h" namespace android::scheduler { @@ -45,7 +46,7 @@ public: bool addPresentFence(std::shared_ptr<FenceTime>) final; void setIgnorePresentFences(bool ignore) final; - void startPeriodTransition(nsecs_t period, bool force) final; + void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) final; bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed) final; @@ -57,7 +58,7 @@ public: private: void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex); void updateIgnorePresentFencesInternal() REQUIRES(mMutex); - void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex); + void startPeriodTransitionInternal(ftl::NonNull<DisplayModePtr>) REQUIRES(mMutex); void endPeriodTransition() REQUIRES(mMutex); bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod) REQUIRES(mMutex); @@ -74,7 +75,7 @@ private: bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false; bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false; - std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex); + DisplayModePtr mModePtrTransitioningTo GUARDED_BY(mMutex); std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex); hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 7eedc312e2..1ed863cf7d 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -20,25 +20,16 @@ #include <utils/Timers.h> #include <scheduler/Fps.h> +#include <scheduler/FrameRateMode.h> #include "VSyncDispatch.h" namespace android::scheduler { -struct DisplayModeData { - Fps renderRate; - std::optional<Period> notifyExpectedPresentTimeoutOpt; - - bool operator==(const DisplayModeData& other) const { - return isApproxEqual(renderRate, other.renderRate) && - notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt; - } -}; - struct IVsyncTrackerCallback { virtual ~IVsyncTrackerCallback() = default; - virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, - const DisplayModeData&, Period vsyncPeriod) = 0; + virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) = 0; }; /* @@ -78,11 +69,9 @@ public: virtual nsecs_t currentPeriod() const = 0; /* - * Inform the tracker that the period is changing and the tracker needs to recalibrate itself. - * - * \param [in] period The period that the system is changing into. + * The minimal period frames can be displayed. */ - virtual void setPeriod(nsecs_t period) = 0; + virtual Period minFramePeriod() const = 0; /* Inform the tracker that the samples it has are not accurate for prediction. */ virtual void resetModel() = 0; @@ -98,20 +87,30 @@ public: virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; /* - * Sets the metadata about the currently active display mode such as VRR - * timeout period, vsyncPeriod and framework property such as render rate. - * If the render rate is not a divisor of the period, the render rate is - * ignored until the period changes. + * Sets the active mode of the display which includes the vsync period and other VRR attributes. + * This will inform the tracker that the period is changing and the tracker needs to recalibrate + * itself. + * + * \param [in] DisplayModePtr The display mode the tracker will use. + */ + virtual void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) = 0; + + /* + * Sets a render rate on the tracker. If the render rate is not a divisor + * of the period, the render rate is ignored until the period changes. * The tracker will continue to track the vsync timeline and expect it * to match the current period, however, nextAnticipatedVSyncTimeFrom will * return vsyncs according to the render rate set. Setting a render rate is useful * when a display is running at 120Hz but the render frame rate is 60Hz. - * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along - * the vsyncPeriod, render rate and timeoutNs. * - * \param [in] DisplayModeData The DisplayModeData the tracker will use. + * \param [in] Fps The render rate the tracker should operate at. */ - virtual void setDisplayModeData(const DisplayModeData&) = 0; + virtual void setRenderRate(Fps) = 0; + + virtual void onFrameBegin(TimePoint expectedPresentTime, + TimePoint lastConfirmedPresentTime) = 0; + + virtual void onFrameMissed(TimePoint expectedPresentTime) = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 917789934a..807a7fba53 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -22,6 +22,7 @@ #include <DisplayHardware/HWComposer.h> #include <DisplayHardware/Hal.h> +#include <scheduler/FrameRateMode.h> #include <ui/FenceTime.h> #include <utils/Mutex.h> #include <utils/RefBase.h> @@ -59,13 +60,14 @@ public: bool* periodFlushed) = 0; /* - * Inform the controller that the period is changing and the controller needs to recalibrate - * itself. The controller will end the period transition internally. + * Inform the controller that the display mode is changing and the controller needs to + * recalibrate itself to the new vsync period. The controller will end the period transition + * internally. * - * \param [in] period The period that the system is changing into. + * \param [in] DisplayModePtr The new mode the display is changing to. * \param [in] force True to recalibrate even if period matches the existing period. */ - virtual void startPeriodTransition(nsecs_t period, bool force) = 0; + virtual void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force) = 0; /* * Tells the tracker to stop using present fences to get a vsync signal. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 5fb53f9e20..ff1c9e909a 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -16,6 +16,8 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/FlagManager.h> + #include <ftl/fake_guard.h> #include <scheduler/Fps.h> #include <scheduler/Timer.h> @@ -53,14 +55,14 @@ private: VSyncCallbackRegistration mRegistration; }; -VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features, +VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features, RequestHardwareVsync requestHardwareVsync, IVsyncTrackerCallback& callback) - : mId(id), + : mId(modePtr->getPhysicalDisplayId()), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(id, callback)), + mTracker(createTracker(modePtr, callback)), mDispatch(createDispatch(mTracker)), - mController(createController(id, *mTracker, features)), + mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) ? std::make_unique<PredictedVsyncTracer>(mDispatch) : nullptr) {} @@ -79,6 +81,13 @@ Period VsyncSchedule::period() const { return Period::fromNs(mTracker->currentPeriod()); } +Period VsyncSchedule::minFramePeriod() const { + if (FlagManager::getInstance().vrr_config()) { + return mTracker->minFramePeriod(); + } + return period(); +} + TimePoint VsyncSchedule::vsyncDeadlineAfter(TimePoint timePoint) const { return TimePoint::fromNs(mTracker->nextAnticipatedVSyncTimeFrom(timePoint.ns())); } @@ -101,17 +110,15 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id, +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback& callback) { // TODO(b/144707443): Tune constants. - constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs(); constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; - return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize, - kMinSamplesForPrediction, kDiscardOutlierPercent, - callback); + return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, + kDiscardOutlierPercent, callback); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { @@ -140,9 +147,9 @@ VsyncSchedule::ControllerPtr VsyncSchedule::createController(PhysicalDisplayId i return reactor; } -void VsyncSchedule::startPeriodTransition(Period period, bool force) { +void VsyncSchedule::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bool force) { std::lock_guard<std::mutex> lock(mHwVsyncLock); - mController->startPeriodTransition(period.ns(), force); + mController->onDisplayModeChanged(modePtr, force); enableHardwareVsyncLocked(); } diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index ca61f875c3..722ea0b836 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -57,21 +57,23 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&); + VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync, + IVsyncTrackerCallback&); ~VsyncSchedule(); // IVsyncSource overrides: Period period() const override; TimePoint vsyncDeadlineAfter(TimePoint) const override; + Period minFramePeriod() const override; - // Inform the schedule that the period is changing and the schedule needs to recalibrate - // itself. The schedule will end the period transition internally. This will - // enable hardware VSYNCs in order to calibrate. + // Inform the schedule that the display mode changed the schedule needs to recalibrate + // itself to the new vsync period. The schedule will end the period transition internally. + // This will enable hardware VSYNCs in order to calibrate. // - // \param [in] period The period that the system is changing into. + // \param [in] DisplayModePtr The mode that the display is changing to. // \param [in] force True to force a transition even if it is not a // change. - void startPeriodTransition(Period period, bool force); + void onDisplayModeChanged(ftl::NonNull<DisplayModePtr>, bool force); // Pass a VSYNC sample to VsyncController. Return true if // VsyncController detected that the VSYNC period changed. Enable or disable @@ -125,7 +127,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&); + static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index ae74205720..70d4846070 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -50,11 +50,11 @@ public: TimePoint expectedPresentTime() const { return mExpectedPresentTime; } // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details. - TimePoint pastVsyncTime(Period vsyncPeriod) const; + TimePoint pastVsyncTime(Period minFramePeriod) const; // Equivalent to `pastVsyncTime` unless running N VSYNCs ahead. - TimePoint previousFrameVsyncTime(Period vsyncPeriod) const { - return mExpectedPresentTime - vsyncPeriod; + TimePoint previousFrameVsyncTime(Period minFramePeriod) const { + return mExpectedPresentTime - minFramePeriod; } // The present fence for the frame that had targeted the most recent VSYNC before this frame. @@ -62,14 +62,14 @@ public: // VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the // `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been // signaled by now (unless that frame missed). - const FenceTimePtr& presentFenceForPastVsync(Period vsyncPeriod) const; + const FenceTimePtr& presentFenceForPastVsync(Period minFramePeriod) const; // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead. const FenceTimePtr& presentFenceForPreviousFrame() const { return mPresentFences.front().fenceTime; } - bool wouldPresentEarly(Period vsyncPeriod) const; + bool wouldPresentEarly(Period minFramePeriod) const; bool isFramePending() const { return mFramePending; } bool didMissFrame() const { return mFrameMissed; } @@ -96,9 +96,9 @@ protected: private: template <int N> - inline bool targetsVsyncsAhead(Period vsyncPeriod) const { + inline bool targetsVsyncsAhead(Period minFramePeriod) const { static_assert(N > 1); - return expectedFrameDuration() > (N - 1) * vsyncPeriod; + return expectedFrameDuration() > (N - 1) * minFramePeriod; } }; diff --git a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h index bb2de75ac4..0154060f36 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h @@ -23,6 +23,7 @@ namespace android::scheduler { struct IVsyncSource { virtual Period period() const = 0; virtual TimePoint vsyncDeadlineAfter(TimePoint) const = 0; + virtual Period minFramePeriod() const = 0; protected: ~IVsyncSource() = default; diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index 7a18654346..e80372be31 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -27,28 +27,28 @@ FrameTarget::FrameTarget(const std::string& displayLabel) mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false), mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {} -TimePoint FrameTarget::pastVsyncTime(Period vsyncPeriod) const { +TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const { // TODO(b/267315508): Generalize to N VSYNCs. - const int shift = static_cast<int>(targetsVsyncsAhead<2>(vsyncPeriod)); - return mExpectedPresentTime - Period::fromNs(vsyncPeriod.ns() << shift); + const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod)); + return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift); } -const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period vsyncPeriod) const { +const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const { // TODO(b/267315508): Generalize to N VSYNCs. - const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(vsyncPeriod)); + const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod)); return mPresentFences[i].fenceTime; } -bool FrameTarget::wouldPresentEarly(Period vsyncPeriod) const { +bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const { // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead` // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`. // TODO(b/267315508): Generalize to N VSYNCs. - if (targetsVsyncsAhead<3>(vsyncPeriod)) { + if (targetsVsyncsAhead<3>(minFramePeriod)) { return true; } - const auto fence = presentFenceForPastVsync(vsyncPeriod); + const auto fence = presentFenceForPastVsync(minFramePeriod); return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING; } @@ -68,6 +68,7 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v mScheduledPresentTime = args.expectedVsyncTime; const Period vsyncPeriod = vsyncSource.period(); + const Period minFramePeriod = vsyncSource.minFramePeriod(); // Calculate the expected present time once and use the cached value throughout this frame to // make sure all layers are seeing this same value. @@ -85,7 +86,7 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)"); - const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(vsyncPeriod); + const FenceTimePtr& pastPresentFence = presentFenceForPastVsync(minFramePeriod); // In cases where the present fence is about to fire, give it a small grace period instead of // giving up on the frame. diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp index 1e038d1753..c883385755 100644 --- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp +++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp @@ -17,23 +17,29 @@ #include <ftl/optional.h> #include <gtest/gtest.h> +#include <common/test/FlagUtils.h> #include <scheduler/Fps.h> #include <scheduler/FrameTargeter.h> #include <scheduler/IVsyncSource.h> +#include <com_android_graphics_surfaceflinger_flags.h> + using namespace std::chrono_literals; namespace android::scheduler { namespace { struct VsyncSource final : IVsyncSource { - VsyncSource(Period period, TimePoint deadline) : vsyncPeriod(period), vsyncDeadline(deadline) {} + VsyncSource(Period period, Period minFramePeriod, TimePoint deadline) + : vsyncPeriod(period), framePeriod(minFramePeriod), vsyncDeadline(deadline) {} const Period vsyncPeriod; + const Period framePeriod; const TimePoint vsyncDeadline; Period period() const override { return vsyncPeriod; } TimePoint vsyncDeadlineAfter(TimePoint) const override { return vsyncDeadline; } + Period minFramePeriod() const override { return framePeriod; } }; } // namespace @@ -44,10 +50,13 @@ public: struct Frame { Frame(FrameTargeterTest* testPtr, VsyncId vsyncId, TimePoint& frameBeginTime, - Duration frameDuration, Fps refreshRate, + Duration frameDuration, Fps refreshRate, Fps peakRefreshRate, FrameTargeter::IsFencePendingFuncPtr isFencePendingFuncPtr = Frame::fenceSignaled, const ftl::Optional<VsyncSource>& vsyncSourceOpt = std::nullopt) - : testPtr(testPtr), frameBeginTime(frameBeginTime), period(refreshRate.getPeriod()) { + : testPtr(testPtr), + frameBeginTime(frameBeginTime), + period(refreshRate.getPeriod()), + minFramePeriod(peakRefreshRate.getPeriod()) { const FrameTargeter::BeginFrameArgs args{.frameBeginTime = frameBeginTime, .vsyncId = vsyncId, .expectedVsyncTime = @@ -58,7 +67,7 @@ public: vsyncSourceOpt .or_else([&] { return std::make_optional( - VsyncSource(period, + VsyncSource(period, period, args.expectedVsyncTime)); }) .value(), @@ -88,6 +97,7 @@ public: TimePoint& frameBeginTime; const Period period; + const Period minFramePeriod; bool ended = false; }; @@ -103,7 +113,7 @@ TEST_F(FrameTargeterTest, targetsFrames) { VsyncId vsyncId{42}; { TimePoint frameBeginTime(989ms); - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, 60_Hz, 60_Hz); EXPECT_EQ(target().vsyncId(), VsyncId{42}); EXPECT_EQ(target().frameBeginTime(), TimePoint(989ms)); @@ -112,7 +122,7 @@ TEST_F(FrameTargeterTest, targetsFrames) { } { TimePoint frameBeginTime(1100ms); - const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz); + const Frame frame(this, vsyncId++, frameBeginTime, 11ms, 60_Hz, 60_Hz); EXPECT_EQ(target().vsyncId(), VsyncId{43}); EXPECT_EQ(target().frameBeginTime(), TimePoint(1100ms)); @@ -127,9 +137,10 @@ TEST_F(FrameTargeterTest, inflatesExpectedPresentTime) { TimePoint frameBeginTime(777ms); constexpr Fps kRefreshRate = 120_Hz; - const VsyncSource vsyncSource(kRefreshRate.getPeriod(), frameBeginTime + 5ms); + const VsyncSource vsyncSource(kRefreshRate.getPeriod(), kRefreshRate.getPeriod(), + frameBeginTime + 5ms); const Frame frame(this, VsyncId{123}, frameBeginTime, kFrameDuration, kRefreshRate, - Frame::fenceSignaled, vsyncSource); + kRefreshRate, Frame::fenceSignaled, vsyncSource); EXPECT_EQ(target().expectedPresentTime(), vsyncSource.vsyncDeadline + vsyncSource.vsyncPeriod); } @@ -142,7 +153,7 @@ TEST_F(FrameTargeterTest, recallsPastVsync) { constexpr Duration kFrameDuration = 13ms; for (int n = 5; n-- > 0;) { - Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); const auto fence = frame.end(); EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod); @@ -160,7 +171,31 @@ TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) { FenceTimePtr previousFence = FenceTime::NO_FENCE; for (int n = 5; n-- > 0;) { - Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); + const auto fence = frame.end(); + + EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); + EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), previousFence); + + previousFence = fence; + } +} + +TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true); + + VsyncId vsyncId{222}; + TimePoint frameBeginTime(2000ms); + constexpr Fps kRefreshRate = 120_Hz; + constexpr Fps kPeakRefreshRate = 240_Hz; + constexpr Period kPeriod = kRefreshRate.getPeriod(); + constexpr Duration kFrameDuration = 10ms; + + FenceTimePtr previousFence = FenceTime::NO_FENCE; + + for (int n = 5; n-- > 0;) { + Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, + kPeakRefreshRate); const auto fence = frame.end(); EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod); @@ -184,12 +219,12 @@ TEST_F(FrameTargeterTest, detectsEarlyPresent) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); } // The target is early if the past present fence was signaled. - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); @@ -204,18 +239,18 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) { // The target is not early while past present fences are pending. for (int n = 3; n-- > 0;) { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); } - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); const auto fence = frame.end(); fence->signalForTest(frameBeginTime.ns()); // The target is two VSYNCs ahead, so the past present fence is still pending. EXPECT_FALSE(target().wouldPresentEarly(kPeriod)); - { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); } + { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); } // The target is early if the past present fence was signaled. EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); @@ -226,7 +261,7 @@ TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) { constexpr Fps kRefreshRate = 144_Hz; constexpr Period kPeriod = kRefreshRate.getPeriod(); - const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate); + const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); // The target is more than two VSYNCs ahead, but present fences are not tracked that far back. EXPECT_TRUE(target().wouldPresentEarly(kPeriod)); @@ -243,7 +278,7 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { EXPECT_FALSE(target().didMissHwcFrame()); { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); // The frame did not miss if the past present fence is invalid. @@ -251,7 +286,8 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { EXPECT_FALSE(target().didMissHwcFrame()); } { - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate, + Frame::fencePending); EXPECT_TRUE(target().isFramePending()); // The frame missed if the past present fence is pending. @@ -261,7 +297,8 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { frame.end(CompositionCoverage::Gpu); } { - const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, Frame::fencePending); + const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate, + Frame::fencePending); EXPECT_TRUE(target().isFramePending()); // The GPU frame missed if the past present fence is pending. @@ -269,7 +306,7 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { EXPECT_FALSE(target().didMissHwcFrame()); } { - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); const auto fence = frame.end(); @@ -277,7 +314,7 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { fence->signalForTest(expectedPresentTime.ns() + kPeriod.ns() / 2 + 1); } { - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); const auto fence = frame.end(); @@ -289,7 +326,7 @@ TEST_F(FrameTargeterTest, detectsMissedFrames) { EXPECT_TRUE(target().didMissHwcFrame()); } { - Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate); + Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); EXPECT_FALSE(target().isFramePending()); // The frame did not miss if the past present fence was signaled within slop. diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index ef9b457fc9..57b0d5e0ea 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -16,6 +16,7 @@ #include "ScreenCaptureOutput.h" #include "ScreenCaptureRenderSurface.h" +#include "ui/Rotation.h" #include <compositionengine/CompositionEngine.h> #include <compositionengine/DisplayColorProfileCreationArgs.h> @@ -24,24 +25,6 @@ namespace android { -namespace { - -ui::Size getDisplaySize(ui::Rotation orientation, const Rect& sourceCrop) { - if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) { - return {sourceCrop.getHeight(), sourceCrop.getWidth()}; - } - return {sourceCrop.getWidth(), sourceCrop.getHeight()}; -} - -Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int reqHeight) { - if (orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270) { - return {reqHeight, reqWidth}; - } - return {reqWidth, reqHeight}; -} - -} // namespace - std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) { std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, @@ -62,11 +45,10 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp .Build())); const Rect& sourceCrop = args.renderArea.getSourceCrop(); - const ui::Rotation orientation = ui::Transform::toRotation(args.renderArea.getRotationFlags()); - output->setDisplaySize(getDisplaySize(orientation, sourceCrop)); + const ui::Rotation orientation = ui::ROTATION_0; + output->setDisplaySize({sourceCrop.getWidth(), sourceCrop.getHeight()}); output->setProjection(orientation, sourceCrop, - getOrientedDisplaySpaceRect(orientation, args.renderArea.getReqWidth(), - args.renderArea.getReqHeight())); + {args.renderArea.getReqWidth(), args.renderArea.getReqHeight()}); { std::string name = args.regionSampling ? "RegionSampling" : "ScreenCaptureOutput"; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 7049068e6b..848cbb511b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1190,7 +1190,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool const auto mode = request.mode; const bool emitEvent = request.emitEvent; - switch (display->setDesiredMode(DisplayDevice::ActiveModeInfo(std::move(request)), force)) { + switch (display->setDesiredMode(std::move(request), force)) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1202,7 +1202,7 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool // Start receiving vsync samples now, so that we can detect a period // switch. mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, - mode.modePtr->getVsyncRate()); + mode.modePtr.get()); // As we called to set period, we will call to onRefreshRateChangeCompleted once // VsyncController model is locked. @@ -1286,27 +1286,27 @@ void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) { const auto displayId = display.getPhysicalId(); ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); - const auto pendingMode = display.getPendingMode(); - if (!pendingMode.modeOpt) { + const auto pendingModeOpt = display.getPendingMode(); + if (!pendingModeOpt) { // There is no pending mode change. This can happen if the active // display changed and the mode change happened on a different display. return; } - if (display.getActiveMode().modePtr->getResolution() != - pendingMode.modeOpt->modePtr->getResolution()) { + const auto& activeMode = pendingModeOpt->mode; + + if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) { auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken()); // We need to generate new sequenceId in order to recreate the display (and this // way the framebuffer). state.sequenceId = DisplayDeviceState{}.sequenceId; - state.physical->activeMode = pendingMode.modeOpt->modePtr.get(); + state.physical->activeMode = activeMode.modePtr.get(); processDisplayChangesLocked(); // processDisplayChangesLocked will update all necessary components so we're done here. return; } - const auto& activeMode = *pendingMode.modeOpt; display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(), activeMode.fps); @@ -1315,7 +1315,7 @@ void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) { updatePhaseConfiguration(activeMode.fps); } - if (pendingMode.event != scheduler::DisplayModeEvent::None) { + if (pendingModeOpt->emitEvent) { dispatchDisplayModeChangeEvent(displayId, activeMode); } } @@ -1329,13 +1329,15 @@ void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) { } void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) { - const auto desiredModeOpt = display->getDesiredMode(); - const auto& modeOpt = desiredModeOpt->modeOpt; - const auto displayId = modeOpt->modePtr->getPhysicalDisplayId(); - const auto vsyncRate = modeOpt->modePtr->getVsyncRate(); - const auto renderFps = modeOpt->fps; + const auto activeModeOpt = display->getDesiredMode(); + auto activeModePtr = activeModeOpt->mode.modePtr; + const auto displayId = activeModePtr->getPhysicalDisplayId(); + const auto renderFps = activeModeOpt->mode.fps; + dropModeRequest(display); - mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, vsyncRate); + + constexpr bool kAllowToEnable = true; + mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take()); mScheduler->setRenderRate(displayId, renderFps); if (displayId == mActiveDisplayId) { @@ -1352,7 +1354,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { const auto display = getDisplayDeviceLocked(id); if (!display) continue; - const auto desiredModeOpt = display->getDesiredMode(); + auto desiredModeOpt = display->getDesiredMode(); if (!desiredModeOpt) { continue; } @@ -1362,7 +1364,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { continue; } - const auto desiredModeId = desiredModeOpt->modeOpt->modePtr->getId(); + const auto desiredModeId = desiredModeOpt->mode.modePtr->getId(); const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId); if (!displayModePtrOpt) { @@ -1376,7 +1378,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), to_string(display->getId()).c_str()); - if (display->getActiveMode() == desiredModeOpt->modeOpt) { + if (display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); continue; } @@ -1384,9 +1386,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { // Desired active mode was set, it is different than the mode currently in use, however // allowed modes might have changed by the time we process the refresh. // Make sure the desired mode is still allowed - const auto displayModeAllowed = - display->refreshRateSelector().isModeAllowed(*desiredModeOpt->modeOpt); - if (!displayModeAllowed) { + if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) { dropModeRequest(display); continue; } @@ -1397,7 +1397,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { constraints.seamlessRequired = false; hal::VsyncPeriodChangeTimeline outTimeline; - if (!display->initiateModeChange(*desiredModeOpt, constraints, outTimeline)) { + if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) { continue; } @@ -1419,7 +1419,7 @@ void SurfaceFlinger::initiateDisplayModeChanges() { finalizeDisplayModeChange(*display); const auto desiredModeOpt = display->getDesiredMode(); - if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->modeOpt) { + if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); } } @@ -2102,15 +2102,28 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t } } -void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, - hal::Connection connection) { - { - std::lock_guard<std::mutex> lock(mHotplugMutex); - mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection}); +void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, + DisplayHotplugEvent event) { + if (event == DisplayHotplugEvent::CONNECTED || event == DisplayHotplugEvent::DISCONNECTED) { + hal::Connection connection = (event == DisplayHotplugEvent::CONNECTED) + ? hal::Connection::CONNECTED + : hal::Connection::DISCONNECTED; + { + std::lock_guard<std::mutex> lock(mHotplugMutex); + mPendingHotplugEvents.push_back(HotplugEvent{hwcDisplayId, connection}); + } + + if (mScheduler) { + mScheduler->scheduleConfigure(); + } + + return; } - if (mScheduler) { - mScheduler->scheduleConfigure(); + if (FlagManager::getInstance().hotplug2()) { + ALOGD("SurfaceFlinger got hotplug event=%d", static_cast<int32_t>(event)); + // TODO(b/311403559): use enum type instead of int + mScheduler->onHotplugConnectionError(mAppConnectionHandle, static_cast<int32_t>(event)); } } @@ -2477,6 +2490,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, if (pacesetterFrameTarget.isFramePending()) { if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) { + if (FlagManager::getInstance().vrr_config()) { + mScheduler->getVsyncSchedule()->getTracker().onFrameMissed( + pacesetterFrameTarget.expectedPresentTime()); + } scheduleCommit(FrameHint::kNone); return false; } @@ -2616,10 +2633,6 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.outputs.push_back(display->getCompositionDisplay()); } } - if (display->getId() == pacesetterId) { - // TODO(b/255601557) Update frameInterval per display - refreshArgs.frameInterval = display->refreshRateSelector().getActiveMode().fps; - } } mPowerAdvisor->setDisplays(displayIds); @@ -2672,20 +2685,23 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const Period vsyncPeriod = mScheduler->getVsyncSchedule()->period(); + const Period minFramePeriod = mScheduler->getVsyncSchedule()->minFramePeriod(); if (!getHwComposer().getComposer()->isSupported( Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && - pacesetterTarget.wouldPresentEarly(vsyncPeriod)) { + pacesetterTarget.wouldPresentEarly(minFramePeriod)) { const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; // TODO(b/255601557): Calculate and pass per-display values for each FrameTarget. refreshArgs.earliestPresentTime = - pacesetterTarget.previousFrameVsyncTime(vsyncPeriod) - hwcMinWorkDuration; + pacesetterTarget.previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration; } + const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime(); + // TODO(b/255601557) Update frameInterval per display + refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime); refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); - refreshArgs.expectedPresentTime = pacesetterTarget.expectedPresentTime().ns(); + refreshArgs.expectedPresentTime = expectedPresentTime.ns(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; // Store the present time just before calling to the composition engine so we could notify @@ -4035,15 +4051,23 @@ void SurfaceFlinger::onChoreographerAttached() { } } -void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime, - const scheduler::DisplayModeData& displayModeData, - Period vsyncPeriod) { - const auto status = - getHwComposer() - .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, - displayModeData.renderRate, - displayModeData - .notifyExpectedPresentTimeoutOpt); +void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, + ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { + const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); + const auto timeout = [&]() -> std::optional<Period> { + const auto vrrConfig = modePtr->getVrrConfig(); + if (!vrrConfig) return std::nullopt; + + const auto notifyExpectedPresentConfig = + modePtr->getVrrConfig()->notifyExpectedPresentConfig; + if (!notifyExpectedPresentConfig) return std::nullopt; + return Period::fromNs(notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); + }(); + + const auto displayId = modePtr->getPhysicalDisplayId(); + const auto status = getHwComposer().notifyExpectedPresentIfRequired(displayId, vsyncPeriod, + expectedPresentTime, + renderRate, timeout); if (status != NO_ERROR) { ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__, displayId.value); @@ -4064,7 +4088,10 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { FeatureFlags features; - if (sysprop::use_content_detection_for_refresh_rate(false)) { + const auto defaultContentDetectionValue = + FlagManager::getInstance().enable_fro_dependent_features() && + sysprop::enable_frame_rate_override(true); + if (sysprop::use_content_detection_for_refresh_rate(defaultContentDetectionValue)) { features |= Feature::kContentDetection; if (FlagManager::getInstance().enable_small_area_detection()) { features |= Feature::kSmallDirtyContentDetection; @@ -4696,7 +4723,7 @@ bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId return false; } - const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->period() / 2; + const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->minFramePeriod() / 2; return predictedPresentTime >= expectedPresentTime && predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold; @@ -5765,7 +5792,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: display->setPowerMode(mode); - const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getVsyncRate(); + const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) { // Turn on the display @@ -5802,7 +5829,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mScheduler->enableSyntheticVsync(false); constexpr bool kAllowToEnable = true; - mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, refreshRate); + mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get()); } mVisibleRegionsDirty = true; @@ -5844,7 +5871,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mVisibleRegionsDirty = true; scheduleRepaint(); mScheduler->enableSyntheticVsync(false); - mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, + activeMode.get()); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze @@ -7046,7 +7074,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r const hal::HWDisplayId hwcId = (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId()); - onComposerHalHotplug(hwcId, hal::Connection::CONNECTED); + onComposerHalHotplugEvent(hwcId, DisplayHotplugEvent::CONNECTED); return NO_ERROR; } // Modify the max number of display frames stored within FrameTimeline @@ -7274,8 +7302,8 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { if (!display->isRefreshRateOverlayEnabled()) return; const auto desiredModeIdOpt = - display->getDesiredMode().transform([](const DisplayDevice::ActiveModeInfo& info) { - return info.modeOpt->modePtr->getId(); + display->getDesiredMode().transform([](const display::DisplayModeRequest& request) { + return request.mode.modePtr->getId(); }); const bool timerExpired = mKernelIdleTimerEnabled && expired; @@ -7548,8 +7576,7 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, RenderAreaFuture renderAreaFuture = ftl::defer([=] { return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, args.dataspace, - args.useIdentityTransform, args.hintForSeamlessTransition, - args.captureSecureLayers); + args.hintForSeamlessTransition, args.captureSecureLayers); }); GetLayerSnapshotsFunction getLayerSnapshots; @@ -7602,7 +7629,6 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args RenderAreaFuture renderAreaFuture = ftl::defer([=] { return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace, - false /* useIdentityTransform */, args.hintForSeamlessTransition, false /* captureSecureLayers */); }); @@ -8948,7 +8974,10 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t u .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(), .skipRoundCornersWhenProtected = !getRenderEngine().supportsProtectedContent()}; - args.rootSnapshot.isSecure = mLayerLifecycleManager.isLayerSecure(rootLayerId); + // The layer may not exist if it was just created and a screenshot was requested immediately + // after. In this case, the hierarchy will be empty so we will not render any layers. + args.rootSnapshot.isSecure = mLayerLifecycleManager.getLayerFromId(rootLayerId) && + mLayerLifecycleManager.isLayerSecure(rootLayerId); mLayerSnapshotBuilder.update(args); auto getLayerSnapshotsFn = diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 75fd25a200..788fe73519 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -106,6 +106,7 @@ #include <vector> #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h> #include "Client.h" @@ -130,6 +131,7 @@ class FrameTracer; class ScreenCapturer; class WindowInfosListenerInvoker; +using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using frontend::TransactionHandler; using gui::CaptureArgs; @@ -629,7 +631,7 @@ private: // HWC2::ComposerCallback overrides: void onComposerHalVsync(hal::HWDisplayId, nsecs_t timestamp, std::optional<hal::VsyncPeriodNanos>) override; - void onComposerHalHotplug(hal::HWDisplayId, hal::Connection) override; + void onComposerHalHotplugEvent(hal::HWDisplayId, DisplayHotplugEvent) override; void onComposerHalRefresh(hal::HWDisplayId) override; void onComposerHalVsyncPeriodTimingChanged(hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&) override; @@ -658,8 +660,8 @@ private: void notifyCpuLoadUp() override; // IVsyncTrackerCallback overrides - void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, - const scheduler::DisplayModeData&, Period vsyncPeriod) override; + void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) override; // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp index 9d6d87eded..ce8119eecd 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.cpp +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -16,12 +16,10 @@ #undef LOG_TAG #define LOG_TAG "TransactionTracing" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/stringprintf.h> #include <log/log.h> #include <utils/SystemClock.h> -#include <utils/Trace.h> #include "Client.h" #include "FrontEnd/LayerCreationArgs.h" @@ -230,7 +228,6 @@ void TransactionTracing::loop() { void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committedUpdates, const std::vector<uint32_t>& destroyedLayers) { - ATRACE_CALL(); std::scoped_lock lock(mTraceLock); std::vector<std::string> removedEntries; perfetto::protos::TransactionTraceEntry entryProto; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index e2a1498003..07a606c9ed 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -121,6 +121,9 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(hdcp_level_hal); DUMP_READ_ONLY_FLAG(multithreaded_present); DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace); + DUMP_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency); + DUMP_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved); + DUMP_READ_ONLY_FLAG(enable_fro_dependent_features); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -190,6 +193,10 @@ FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "") +FLAG_MANAGER_READ_ONLY_FLAG(use_known_refresh_rate_for_fps_consistency, "") +FLAG_MANAGER_READ_ONLY_FLAG(cache_if_source_crop_layer_only_moved, + "debug.sf.cache_source_crop_only_moved") +FLAG_MANAGER_READ_ONLY_FLAG(enable_fro_dependent_features, "") /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 9aabbb9870..38cb43a269 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -60,6 +60,9 @@ public: bool hdcp_level_hal() const; bool multithreaded_present() const; bool add_sf_skipped_frames_to_trace() const; + bool use_known_refresh_rate_for_fps_consistency() const; + bool cache_if_source_crop_layer_only_moved() const; + bool enable_fro_dependent_features() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/tests/unittests/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h index 550c70d98f..550c70d98f 100644 --- a/services/surfaceflinger/tests/unittests/FlagUtils.h +++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h index 1a951b34ac..b2dc20e216 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h @@ -41,6 +41,7 @@ class SurfaceComposerClient; namespace android::hardware::graphics::composer::hal { +using aidl::android::hardware::graphics::common::DisplayHotplugEvent; using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using ::android::hardware::Return; using ::android::hardware::Void; @@ -52,7 +53,9 @@ public: : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} Return<void> onHotplug(HWDisplayId display, Connection connection) override { - mCallback->onComposerHalHotplug(display, connection); + const auto event = connection == Connection::CONNECTED ? DisplayHotplugEvent::CONNECTED + : DisplayHotplugEvent::DISCONNECTED; + mCallback->onComposerHalHotplugEvent(display, event); return Void(); } @@ -94,7 +97,7 @@ private: struct TestHWC2ComposerCallback : public HWC2::ComposerCallback { virtual ~TestHWC2ComposerCallback() = default; - void onComposerHalHotplug(HWDisplayId, Connection){}; + void onComposerHalHotplugEvent(HWDisplayId, DisplayHotplugEvent) {} void onComposerHalRefresh(HWDisplayId) {} void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {} void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {} diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 9b2d4536cd..4fc39cc912 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -806,8 +806,7 @@ private: void onChoreographerAttached() override {} // IVsyncTrackerCallback overrides - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override {} + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 8fcfd8131a..b690d8d98e 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -28,6 +28,7 @@ #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" @@ -179,8 +180,7 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { } struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override {} + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} }; void SchedulerFuzzer::fuzzVSyncPredictor() { @@ -189,14 +189,14 @@ void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); VsyncTrackerCallback callback; - scheduler::VSyncPredictor tracker{kDisplayId, - idealPeriod, - historySize, - minimumSamplesForPrediction, + const auto mode = ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod))); + scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction, mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, callback}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); - tracker.setPeriod(period); + tracker.setDisplayModePtr(ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period)))); for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { if (!tracker.needsMoreSamples()) { break; @@ -271,7 +271,10 @@ void SchedulerFuzzer::fuzzVSyncReactor() { *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/, false); - reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool()); + const auto mode = ftl::as_non_null( + mock::createDisplayMode(DisplayModeId(0), + Fps::fromPeriodNsecs(mFdp.ConsumeIntegral<nsecs_t>()))); + reactor.onDisplayModeChanged(mode, mFdp.ConsumeBool()); bool periodFlushed = false; // Value does not matter, since this is an out // param from addHwVsyncTimestamp. reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); @@ -430,6 +433,7 @@ void SchedulerFuzzer::fuzzFrameTargeter() { Period period() const { return getFuzzedDuration(fuzzer); } TimePoint vsyncDeadlineAfter(TimePoint) const { return getFuzzedTimePoint(fuzzer); } + Period minFramePeriod() const { return period(); } } vsyncSource{mFdp}; int i = 10; diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 728708f05c..fa307e9bb4 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -89,8 +89,7 @@ public: nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; } nsecs_t currentPeriod() const override { return 1; } - - void setPeriod(nsecs_t /* period */) override {} + Period minFramePeriod() const override { return Period::fromNs(currentPeriod()); } void resetModel() override {} @@ -100,7 +99,7 @@ public: return true; } - void setDisplayModeData(const scheduler::DisplayModeData&) override {} + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) override {} nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { @@ -109,6 +108,12 @@ public: return (timePoint - (timePoint % mPeriod) + mPeriod); } + void setRenderRate(Fps) override {} + + void onFrameBegin(TimePoint, TimePoint) override {} + + void onFrameMissed(TimePoint) override {} + void dump(std::string& /* result */) const override {} protected: diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 71c59b26f8..fcb52c72f4 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -84,3 +84,26 @@ flag { description: "enable refresh rate indicator on the external display" bug: "301647974" } + +flag { + name: "use_known_refresh_rate_for_fps_consistency" + namespace: "core_graphics" + description: "Whether to use the closest known refresh rate to determine the fps consistency." + bug: "299201319" + is_fixed_read_only: true +} + +flag { + name: "cache_if_source_crop_layer_only_moved" + namespace: "core_graphics" + description: "do not flatten layers if source crop is only moved" + bug: "305718400" +} + +flag { + name: "enable_fro_dependent_features" + namespace: "core_graphics" + description: "enable frame rate override dependent features by default" + bug: "314217419" + is_fixed_read_only: true +} diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp index 0a951d49e3..aeff5a530d 100644 --- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp +++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp @@ -56,60 +56,44 @@ protected: ASSERT_EQ(res, NO_ERROR); } - void testSetAllowGroupSwitching(bool allowGroupSwitching); + void testSetDesiredDisplayModeSpecs(bool allowGroupSwitching = false) { + ui::DynamicDisplayInfo info; + status_t res = + SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId), + &info); + const auto& modes = info.supportedDisplayModes; + ASSERT_EQ(res, NO_ERROR); + ASSERT_GT(modes.size(), 0); + for (const auto& mode : modes) { + gui::DisplayModeSpecs setSpecs; + setSpecs.defaultMode = mode.id; + setSpecs.allowGroupSwitching = allowGroupSwitching; + setSpecs.primaryRanges.physical.min = mode.peakRefreshRate; + setSpecs.primaryRanges.physical.max = mode.peakRefreshRate; + setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; + setSpecs.appRequestRanges = setSpecs.primaryRanges; + + res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); + ASSERT_EQ(res, NO_ERROR); + gui::DisplayModeSpecs getSpecs; + res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); + ASSERT_EQ(res, NO_ERROR); + ASSERT_EQ(setSpecs, getSpecs); + } + } sp<IBinder> mDisplayToken; uint64_t mDisplayId; }; TEST_F(RefreshRateRangeTest, setAllConfigs) { - ui::DynamicDisplayInfo info; - status_t res = - SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId), - &info); - const auto& modes = info.supportedDisplayModes; - ASSERT_EQ(res, NO_ERROR); - ASSERT_GT(modes.size(), 0); - - gui::DisplayModeSpecs setSpecs; - setSpecs.allowGroupSwitching = false; - for (size_t i = 0; i < modes.size(); i++) { - setSpecs.defaultMode = modes[i].id; - setSpecs.primaryRanges.physical.min = modes[i].peakRefreshRate; - setSpecs.primaryRanges.physical.max = modes[i].peakRefreshRate; - setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; - setSpecs.appRequestRanges = setSpecs.primaryRanges; - res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); - ASSERT_EQ(res, NO_ERROR); - - gui::DisplayModeSpecs getSpecs; - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); - ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(setSpecs, getSpecs); - } -} - -void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) { - gui::DisplayModeSpecs setSpecs; - setSpecs.defaultMode = 0; - setSpecs.allowGroupSwitching = allowGroupSwitching; - setSpecs.primaryRanges.physical.min = 0; - setSpecs.primaryRanges.physical.max = 90; - setSpecs.primaryRanges.render = setSpecs.primaryRanges.physical; - setSpecs.appRequestRanges = setSpecs.primaryRanges; - - status_t res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, setSpecs); - ASSERT_EQ(res, NO_ERROR); - gui::DisplayModeSpecs getSpecs; - res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &getSpecs); - ASSERT_EQ(res, NO_ERROR); - ASSERT_EQ(setSpecs, getSpecs); + testSetDesiredDisplayModeSpecs(); } TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) { - testSetAllowGroupSwitching(true); - testSetAllowGroupSwitching(false); - testSetAllowGroupSwitching(true); + testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true); + testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/false); + testSetDesiredDisplayModeSpecs(/*allowGroupSwitching=*/true); } } // namespace android diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp index 21813700a2..15a98df275 100644 --- a/services/surfaceflinger/tests/LayerState_test.cpp +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -38,7 +38,6 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { args.displayToken = sp<BBinder>::make(); args.width = 10; args.height = 20; - args.useIdentityTransform = true; args.grayscale = true; Parcel p; @@ -56,7 +55,6 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { ASSERT_EQ(args.displayToken, args2.displayToken); ASSERT_EQ(args.width, args2.width); ASSERT_EQ(args.height, args2.height); - ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform); ASSERT_EQ(args.grayscale, args2.grayscale); } diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index 3873b0c076..c463a9271b 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -23,16 +23,20 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \ + ASSERT_TRUE(requestOpt); \ + EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \ + EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent) + namespace android { namespace { using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; +using DisplayModeRequest = display::DisplayModeRequest; class InitiateModeChangeTest : public DisplayTransactionTest { public: using Action = DisplayDevice::DesiredModeAction; - using Event = scheduler::DisplayModeEvent; - void SetUp() override { injectFakeBufferQueueFactory(); injectFakeNativeWindowSurfaceFactory(); @@ -43,7 +47,8 @@ public: PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this); PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this); - mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID, + DisplayHotplugEvent::CONNECTED); mFlinger.configureAndCommit(); mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) @@ -64,32 +69,30 @@ protected: ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz)); static inline const ftl::NonNull<DisplayModePtr> kMode120 = ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz)); + + static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false}; + static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true}; + static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false}; + static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true}; }; TEST_F(InitiateModeChangeTest, setDesiredModeToActiveMode) { - EXPECT_EQ(Action::None, - mDisplay->setDesiredMode({scheduler::FrameRateMode{60_Hz, kMode60}, Event::None})); + EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60))); EXPECT_FALSE(mDisplay->getDesiredMode()); } TEST_F(InitiateModeChangeTest, setDesiredMode) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, - mDisplay->setDesiredMode({scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredMode()->modeOpt); - EXPECT_EQ(Event::None, mDisplay->getDesiredMode()->event); + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); - // Setting another mode should be cached but return None. - EXPECT_EQ(Action::None, - mDisplay->setDesiredMode({scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredMode()->modeOpt); - EXPECT_EQ(Event::None, mDisplay->getDesiredMode()->event); + EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120))); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode()); } TEST_F(InitiateModeChangeTest, clearDesiredMode) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, - mDisplay->setDesiredMode({scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); EXPECT_TRUE(mDisplay->getDesiredMode()); mDisplay->clearDesiredMode(); @@ -98,10 +101,8 @@ TEST_F(InitiateModeChangeTest, clearDesiredMode) { TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, - mDisplay->setDesiredMode({scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredMode()->modeOpt); - EXPECT_EQ(Event::None, mDisplay->getDesiredMode()->event); + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); const hal::VsyncPeriodChangeConstraints constraints{ .desiredTimeNanos = systemTime(), @@ -109,8 +110,7 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) }; hal::VsyncPeriodChangeTimeline timeline; EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getPendingMode().modeOpt); - EXPECT_EQ(Event::None, mDisplay->getPendingMode().event); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); mDisplay->clearDesiredMode(); EXPECT_FALSE(mDisplay->getDesiredMode()); @@ -118,16 +118,14 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) { EXPECT_EQ(Action::InitiateRenderRateSwitch, - mDisplay->setDesiredMode({scheduler::FrameRateMode{30_Hz, kMode60}, Event::None})); + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode30))); EXPECT_FALSE(mDisplay->getDesiredMode()); } TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) { EXPECT_EQ(Action::InitiateDisplayModeSwitch, - mDisplay->setDesiredMode({scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); - ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredMode()->modeOpt); - EXPECT_EQ(Event::None, mDisplay->getDesiredMode()->event); + mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90))); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode()); const hal::VsyncPeriodChangeConstraints constraints{ .desiredTimeNanos = systemTime(), @@ -135,21 +133,16 @@ TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainTh }; hal::VsyncPeriodChangeTimeline timeline; EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getPendingMode().modeOpt); - EXPECT_EQ(Event::None, mDisplay->getPendingMode().event); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); - EXPECT_EQ(Action::None, - mDisplay->setDesiredMode({scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); + EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120))); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredMode()->modeOpt); - EXPECT_EQ(Event::None, mDisplay->getDesiredMode()->event); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getPendingMode().modeOpt); - EXPECT_EQ(Event::None, mDisplay->getPendingMode().event); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode()); EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline)); - EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getPendingMode().modeOpt); - EXPECT_EQ(Event::None, mDisplay->getPendingMode().event); + EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode()); mDisplay->clearDesiredMode(); EXPECT_FALSE(mDisplay->getDesiredMode()); diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index c040f29fec..803710d5d6 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "FlagManagerTest" #include <common/FlagManager.h> -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include <gmock/gmock.h> #include <gtest/gtest.h> diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp index 20ea0c080a..5c742d7360 100644 --- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp @@ -49,6 +49,7 @@ protected: const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default); const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default); const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default); + const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default); const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote); FrameRateSelectionStrategyTest(); @@ -102,7 +103,7 @@ TEST_P(FrameRateSelectionStrategyTest, SetAndGet) { layer->getDrawingState().frameRateSelectionStrategy); } -TEST_P(FrameRateSelectionStrategyTest, SetChildAndGetParent) { +TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) { EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -116,17 +117,17 @@ TEST_P(FrameRateSelectionStrategyTest, SetChildAndGetParent) { child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); commitTransaction(); EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, parent->getDrawingState().frameRateSelectionStrategy); EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, child1->getDrawingState().frameRateSelectionStrategy); EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree()); EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, child2->getDrawingState().frameRateSelectionStrategy); } -TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) { +TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) { EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); const auto& layerFactory = GetParam(); @@ -137,7 +138,6 @@ TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) { addChild(layer2, layer3); layer1->setFrameRate(FRAME_RATE_VOTE1.vote); - layer1->setFrameRate(FRAME_RATE_VOTE1.vote); layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); layer2->setFrameRate(FRAME_RATE_VOTE2.vote); layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); @@ -151,20 +151,72 @@ TEST_P(FrameRateSelectionStrategyTest, SetParentAndGet) { EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, layer2->getDrawingState().frameRateSelectionStrategy); EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, layer3->getDrawingState().frameRateSelectionStrategy); - layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self); + layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate); commitTransaction(); EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); - EXPECT_EQ(FrameRateSelectionStrategy::Self, + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, layer1->getDrawingState().frameRateSelectionStrategy); EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, layer2->getDrawingState().frameRateSelectionStrategy); EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, + layer3->getDrawingState().frameRateSelectionStrategy); +} + +TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) { + EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); + + const auto& layerFactory = GetParam(); + auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); + auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); + auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); + addChild(layer1, layer2); + addChild(layer2, layer3); + + layer1->setFrameRate(FRAME_RATE_VOTE1.vote); + layer2->setFrameRate(FRAME_RATE_VOTE2.vote); + layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self); + commitTransaction(); + + EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, + layer1->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); EXPECT_EQ(FrameRateSelectionStrategy::Self, + layer2->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, + layer3->getDrawingState().frameRateSelectionStrategy); + + layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren); + commitTransaction(); + + EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, + layer1->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Self, + layer2->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, + layer3->getDrawingState().frameRateSelectionStrategy); + + layer1->setFrameRate(FRAME_RATE_DEFAULT.vote); + commitTransaction(); + + EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren, + layer1->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Self, + layer2->getDrawingState().frameRateSelectionStrategy); + EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree()); + EXPECT_EQ(FrameRateSelectionStrategy::Propagate, layer3->getDrawingState().frameRateSelectionStrategy); } diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 6d87717404..ddc3967c40 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "com_android_graphics_surfaceflinger_flags.h" #include "gmock/gmock-spec-builders.h" #include "mock/MockTimeStats.h" @@ -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/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index f1e841b88f..d3ce4f2b90 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -35,11 +35,11 @@ #include <log/log.h> #include <chrono> +#include <common/test/FlagUtils.h> #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" #include "DisplayIdentificationTestHelpers.h" -#include "FlagUtils.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockHWC2.h" @@ -57,6 +57,7 @@ using namespace std::chrono_literals; using Hwc2::Config; +using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using hal::IComposerClient; using ::testing::_; @@ -555,7 +556,8 @@ TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { } struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { - MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection)); + MOCK_METHOD(void, onComposerHalHotplugEvent, (hal::HWDisplayId, DisplayHotplugEvent), + (override)); MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId)); MOCK_METHOD3(onComposerHalVsync, void(hal::HWDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos>)); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index befef482b9..787fa1c5e4 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -25,7 +25,7 @@ #include <renderengine/mock/FakeExternalTexture.h> -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "FpsOps.h" #include "LayerHierarchyTest.h" #include "Scheduler/LayerHistory.h" @@ -768,6 +768,7 @@ TEST_F(LayerHistoryIntegrationTest, heuristicLayer60_30Hz) { } TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) { + SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false); auto layer = createLegacyAndFrontedEndLayer(1); nsecs_t time = systemTime(); @@ -779,6 +780,20 @@ TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) { recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); } +TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating_useKnownRefreshRate) { + SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true); + auto layer = createLegacyAndFrontedEndLayer(1); + + nsecs_t time = systemTime(); + + recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); +} + TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) { auto layer = createLegacyAndFrontedEndLayer(1); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 1adf14f587..b88ef563c3 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -27,7 +27,7 @@ #include <gtest/gtest.h> #include <log/log.h> -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "FpsOps.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/LayerInfo.h" @@ -1169,6 +1169,24 @@ TEST_F(LayerHistoryTest, heuristicLayer60_30Hz) { } TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) { + SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false); + + const auto layer = createLayer(); + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + nsecs_t time = systemTime(); + + recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); +} + +TEST_F(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) { + SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true); + const auto layer = createLayer(); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); @@ -1179,6 +1197,7 @@ TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) { recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); + recordFramesAndExpect(layer, time, 27.1_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE); recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE); } diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 047ef5a928..07a522ac4e 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -21,7 +21,7 @@ #include <scheduler/Fps.h> -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "FpsOps.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/LayerInfo.h" diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 16143e3a7f..040a3bfb3e 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -814,24 +814,24 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategy) { EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote); EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate)); EXPECT_FALSE(getSnapshot({.id = 121})->frameRate.vote.rate.isValid()); EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type, scheduler::FrameRateCompatibility::Default); EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate)); EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f); EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate)); EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f); EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate)); } @@ -904,12 +904,12 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { scheduler::FrameRateCompatibility::NoVote); EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::Default); EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate)); EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid()); EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::Default); EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type, scheduler::FrameRateCompatibility::Default); @@ -917,17 +917,105 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f); EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Default); EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate)); EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f); EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy, - scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Default); EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate)); } +TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithOverrideChildrenAndSelf) { + // ROOT + // ├── 1 + // │ ├── 11 (frame rate set to 11.f with strategy Self) + // │ │ └── 111 (frame rate is not inherited) + // │ ├── 12 (frame rate set to 244.f) + // │ │ ├── 121 + // │ │ └── 122 (strategy OverrideChildren and inherits frame rate 244.f) + // │ │ └── 1221 (frame rate set to 123.f but should be overridden by layer 122) + // │ └── 13 + // └── 2 + setFrameRate(11, 11.f, 0, 0); + setFrameRateSelectionStrategy(11, 2 /* Self */); + setFrameRate(12, 244.f, 0, 0); + setFrameRateSelectionStrategy(122, 1 /* OverrideChildren */); + setFrameRate(1221, 123.f, 0, 0); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + // verify parent 1 gets no vote + EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid()); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type, + scheduler::FrameRateCompatibility::NoVote); + EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); + EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); + + EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 11.f); + EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::Self); + EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate)); + + // verify layer 11 does does not propagate its framerate to 111. + EXPECT_FALSE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid()); + EXPECT_EQ(getSnapshot({.id = 111})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); + EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate)); + + // verify layer 12 and all descendants (121, 122, 1221) get the requested vote + EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.rate.getValue(), 244.f); + EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); + EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate)); + + EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.rate.getValue(), 244.f); + EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::Propagate); + EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate)); + + EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f); + EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren); + EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate)); + + EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 244.f); + EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren); + EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate)); + + // ROOT + // ├── 1 (frame rate set to 1.f with strategy OverrideChildren) + // │ ├── 11 (frame rate set to 11.f with strategy Self, but overridden by 1) + // │ │ └── 111 (frame rate inherited from 11 due to override from 1) + // ⋮ ⋮ + setFrameRate(1, 1.f, 0, 0); + setFrameRateSelectionStrategy(1, 1 /* OverrideChildren */); + setFrameRate(11, 11.f, 0, 0); + setFrameRateSelectionStrategy(11, 2 /* Self */); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.rate.getValue(), 1.f); + EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type, + scheduler::FrameRateCompatibility::Default); + EXPECT_EQ(getSnapshot({.id = 1})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren); + EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate)); + + EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 1.f); + EXPECT_EQ(getSnapshot({.id = 11})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren); + EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate)); + + // verify layer 11 does does not propagate its framerate to 111. + EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 1.f); + EXPECT_EQ(getSnapshot({.id = 111})->frameRateSelectionStrategy, + scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren); + EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate)); +} + TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) { setRoundedCorners(1, 42.f); setRoundedCorners(2, 42.f); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index dc13a8d9d7..0cacf810c0 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -26,6 +26,7 @@ #include <log/log.h> #include <ui/Size.h> +#include <common/test/FlagUtils.h> #include <scheduler/Fps.h> #include <scheduler/FrameRateMode.h> #include "DisplayHardware/HWC2.h" @@ -34,7 +35,6 @@ #include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockFrameRateMode.h" -#include "FlagUtils.h" #include "libsurfaceflinger_unittest_main.h" #include <com_android_graphics_surfaceflinger_flags.h> @@ -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. diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index b5eb777a4d..e515895a1a 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <common/test/FlagUtils.h> #include <ftl/fake_guard.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -23,6 +24,7 @@ #include "Scheduler/EventThread.h" #include "Scheduler/RefreshRateSelector.h" +#include "Scheduler/VSyncPredictor.h" #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockDisplayMode.h" @@ -32,11 +34,15 @@ #include <FrontEnd/LayerHierarchy.h> +#include <com_android_graphics_surfaceflinger_flags.h> #include "FpsOps.h" +using namespace com::android::graphics::surfaceflinger; + namespace android::scheduler { using android::mock::createDisplayMode; +using android::mock::createVrrDisplayMode; using testing::_; using testing::Return; @@ -457,26 +463,51 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>; struct Compositor final : ICompositor { - VsyncIds vsyncIds; + explicit Compositor(TestableScheduler& scheduler) : scheduler(scheduler) {} + + TestableScheduler& scheduler; + + struct { + PhysicalDisplayId commit; + PhysicalDisplayId composite; + } pacesetterIds; + + struct { + VsyncIds commit; + VsyncIds composite; + } vsyncIds; + bool committed = true; + bool changePacesetter = false; void configure() override {} - bool commit(PhysicalDisplayId, const scheduler::FrameTargets& targets) override { - vsyncIds.clear(); + bool commit(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargets& targets) override { + pacesetterIds.commit = pacesetterId; + + vsyncIds.commit.clear(); + vsyncIds.composite.clear(); for (const auto& [id, target] : targets) { - vsyncIds.emplace_back(id, target->vsyncId()); + vsyncIds.commit.emplace_back(id, target->vsyncId()); + } + + if (changePacesetter) { + scheduler.setPacesetterDisplay(kDisplayId2); } return committed; } - CompositeResultsPerDisplay composite(PhysicalDisplayId, - const scheduler::FrameTargeters&) override { + CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& targeters) override { + pacesetterIds.composite = pacesetterId; + CompositeResultsPerDisplay results; - for (const auto& [id, _] : vsyncIds) { + for (const auto& [id, targeter] : targeters) { + vsyncIds.composite.emplace_back(id, targeter->target().vsyncId()); results.try_emplace(id, CompositeResult{.compositionCoverage = CompositionCoverage::Hwc}); @@ -486,21 +517,174 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } void sample() override {} - } compositor; + } compositor(*mScheduler); mScheduler->doFrameSignal(compositor, VsyncId(42)); - const auto makeVsyncIds = [](VsyncId vsyncId) -> VsyncIds { - return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}}; + const auto makeVsyncIds = [](VsyncId vsyncId, bool swap = false) -> VsyncIds { + if (swap) { + return {{kDisplayId2, vsyncId}, {kDisplayId1, vsyncId}}; + } else { + return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}}; + } }; - EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds); + EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit); + EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.composite); + EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds.commit); + EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds.composite); + // FrameTargets should be updated despite the skipped commit. compositor.committed = false; mScheduler->doFrameSignal(compositor, VsyncId(43)); - // FrameTargets should be updated despite the skipped commit. - EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds); + EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit); + EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.composite); + EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds.commit); + EXPECT_TRUE(compositor.vsyncIds.composite.empty()); + + // The pacesetter may change during commit. + compositor.committed = true; + compositor.changePacesetter = true; + mScheduler->doFrameSignal(compositor, VsyncId(44)); + + EXPECT_EQ(kDisplayId1, compositor.pacesetterIds.commit); + EXPECT_EQ(kDisplayId2, compositor.pacesetterIds.composite); + EXPECT_EQ(makeVsyncIds(VsyncId(44)), compositor.vsyncIds.commit); + EXPECT_EQ(makeVsyncIds(VsyncId(44), true), compositor.vsyncIds.composite); +} + +TEST_F(SchedulerTest, nextFrameIntervalTest) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + static constexpr size_t kHistorySize = 10; + static constexpr size_t kMinimumSamplesForPrediction = 6; + static constexpr size_t kOutlierTolerancePercent = 25; + const auto refreshRate = Fps::fromPeriodNsecs(500); + auto frameRate = Fps::fromPeriodNsecs(1000); + + const ftl::NonNull<DisplayModePtr> kMode = ftl::as_non_null( + createVrrDisplayMode(DisplayModeId(0), refreshRate, + hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>( + frameRate.getPeriodNsecs())})); + std::shared_ptr<VSyncPredictor> vrrTracker = + std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction, + kOutlierTolerancePercent, mVsyncTrackerCallback); + std::shared_ptr<RefreshRateSelector> vrrSelectorPtr = + std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId()); + TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(), + vrrTracker, + vrrSelectorPtr, + sp<VsyncModulator>::make(VsyncConfigSet{}), + mSchedulerCallback, + mVsyncTrackerCallback}; + + scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); + vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); + scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); + vrrTracker->addVsyncTimestamp(0); + + // Next frame at refresh rate as no previous frame + EXPECT_EQ(refreshRate, + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), TimePoint::fromNs(0))); + + EXPECT_EQ(Fps::fromPeriodNsecs(1000), + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), + TimePoint::fromNs(500))); + EXPECT_EQ(Fps::fromPeriodNsecs(1000), + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), + TimePoint::fromNs(1500))); + + // Change render rate + frameRate = Fps::fromPeriodNsecs(2000); + vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); + scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate); + + EXPECT_EQ(Fps::fromPeriodNsecs(2000), + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), + TimePoint::fromNs(2500))); + EXPECT_EQ(Fps::fromPeriodNsecs(2000), + scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(), + TimePoint::fromNs(4500))); +} + +TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) { + // resyncAllToHardwareVsync will result in requesting hardware VSYNC on both displays, since + // they are both on. + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1); + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1); + + mScheduler->registerDisplay(kDisplayId2, + std::make_shared<RefreshRateSelector>(kDisplay2Modes, + kDisplay2Mode60->getId())); + mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); + mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON); + + static constexpr bool kDisallow = true; + mScheduler->disableHardwareVsync(kDisplayId1, kDisallow); + mScheduler->disableHardwareVsync(kDisplayId2, kDisallow); + + static constexpr bool kAllowToEnable = true; + mScheduler->resyncAllToHardwareVsync(kAllowToEnable); +} + +TEST_F(SchedulerTest, resyncAllDoNotAllow) FTL_FAKE_GUARD(kMainThreadContext) { + // Without setting allowToEnable to true, resyncAllToHardwareVsync does not + // result in requesting hardware VSYNC. + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, _)).Times(0); + + mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); + + static constexpr bool kDisallow = true; + mScheduler->disableHardwareVsync(kDisplayId1, kDisallow); + + static constexpr bool kAllowToEnable = false; + mScheduler->resyncAllToHardwareVsync(kAllowToEnable); +} + +TEST_F(SchedulerTest, resyncAllSkipsOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); + + // resyncAllToHardwareVsync will result in requesting hardware VSYNC on display 1, which is on, + // but not on display 2, which is off. + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1); + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, _)).Times(0); + + mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); + + mScheduler->registerDisplay(kDisplayId2, + std::make_shared<RefreshRateSelector>(kDisplay2Modes, + kDisplay2Mode60->getId())); + ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2)); + + static constexpr bool kDisallow = true; + mScheduler->disableHardwareVsync(kDisplayId1, kDisallow); + mScheduler->disableHardwareVsync(kDisplayId2, kDisallow); + + static constexpr bool kAllowToEnable = true; + mScheduler->resyncAllToHardwareVsync(kAllowToEnable); +} + +TEST_F(SchedulerTest, resyncAllLegacyAppliesToOffDisplays) FTL_FAKE_GUARD(kMainThreadContext) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, false); + + // In the legacy code, prior to the flag, resync applied to OFF displays. + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId1, true)).Times(1); + EXPECT_CALL(mScheduler->mockRequestHardwareVsync, Call(kDisplayId2, true)).Times(1); + + mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON); + + mScheduler->registerDisplay(kDisplayId2, + std::make_shared<RefreshRateSelector>(kDisplay2Modes, + kDisplay2Mode60->getId())); + ASSERT_EQ(hal::PowerMode::OFF, mScheduler->getDisplayPowerMode(kDisplayId2)); + + static constexpr bool kDisallow = true; + mScheduler->disableHardwareVsync(kDisplayId1, kDisallow); + mScheduler->disableHardwareVsync(kDisplayId2, kDisallow); + + static constexpr bool kAllowToEnable = true; + mScheduler->resyncAllToHardwareVsync(kAllowToEnable); } class AttachedChoreographerTest : public SchedulerTest { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 075f974db0..7ad97a2133 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -46,7 +46,8 @@ public: setupScheduler(selectorPtr); - mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED); + mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID, + DisplayHotplugEvent::CONNECTED); mFlinger.configureAndCommit(); auto vsyncController = std::make_unique<mock::VsyncController>(); @@ -56,6 +57,9 @@ public: EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return( TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, minFramePeriod()) + .WillRepeatedly(Return(Period::fromNs( + TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) .setRefreshRateSelector(std::move(selectorPtr)) @@ -138,6 +142,9 @@ void DisplayModeSwitchingTest::setupScheduler( EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly( Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, minFramePeriod()) + .WillRepeatedly(Return(Period::fromNs( + TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), @@ -158,7 +165,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->modeOpt->modePtr->getId(), kModeId90); + EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC @@ -199,7 +206,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshR 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->modeOpt->modePtr->getId(), kModeId90); + EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC @@ -247,7 +254,7 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { 180)); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->modeOpt->modePtr->getId(), kModeId120); + EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120); EXPECT_CALL(*mComposer, setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, @@ -257,7 +264,7 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.commit(); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->modeOpt->modePtr->getId(), kModeId120); + EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120); mFlinger.commit(); @@ -278,7 +285,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); - EXPECT_EQ(mDisplay->getDesiredMode()->modeOpt->modePtr->getId(), kModeId90_4K); + EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K); EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); // Verify that next commit will call setActiveConfigWithConstraints in HWC @@ -323,7 +330,7 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { return false; } - if (arg->getDesiredMode()->modeOpt->modePtr->getId() != modeId) { + if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) { *result_listener << "Unexpected desired mode " << modeId; return false; } @@ -338,7 +345,7 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { MATCHER_P(ModeSettledTo, modeId, "") { if (const auto desiredOpt = arg->getDesiredMode()) { - *result_listener << "Unsettled desired mode " << desiredOpt->modeOpt->modePtr->getId(); + *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId(); return false; } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index 1210d0b472..a270dc91f2 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -27,10 +27,10 @@ TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) { EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2); constexpr HWDisplayId hwcDisplayId1 = 456; - mFlinger.onComposerHalHotplug(hwcDisplayId1, Connection::CONNECTED); + mFlinger.onComposerHalHotplugEvent(hwcDisplayId1, DisplayHotplugEvent::CONNECTED); constexpr HWDisplayId hwcDisplayId2 = 654; - mFlinger.onComposerHalHotplug(hwcDisplayId2, Connection::DISCONNECTED); + mFlinger.onComposerHalHotplugEvent(hwcDisplayId2, DisplayHotplugEvent::DISCONNECTED); const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); ASSERT_EQ(2u, pendingEvents.size()); @@ -45,7 +45,7 @@ TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) { EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); constexpr HWDisplayId displayId1 = 456; - mFlinger.onComposerHalHotplug(displayId1, Connection::DISCONNECTED); + mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED); mFlinger.configure(); // The configure stage should consume the hotplug queue and produce a display transaction. diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index cf3fab3aa3..31e13305a4 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -25,6 +25,11 @@ namespace android { namespace { +MATCHER_P(DisplayModeFps, value, "equals") { + using fps_approx_ops::operator==; + return arg->getVsyncRate() == value; +} + // Used when we simulate a display that supports doze. template <typename Display> struct DozeIsSupportedVariant { @@ -94,7 +99,8 @@ struct DispSyncIsSupportedVariant { static void setupResetModelCallExpectations(DisplayTransactionTest* test) { auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule(); EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()), - startPeriodTransition(DEFAULT_VSYNC_PERIOD, false)) + onDisplayModeChanged(DisplayModeFps(Fps::fromPeriodNsecs(DEFAULT_VSYNC_PERIOD)), + false)) .Times(1); EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel()) .Times(1); diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 8b6f0f1b94..2a1b88e6fa 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -78,10 +78,11 @@ public: auto refreshRateSelector() { return pacesetterSelectorPtr(); } - void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { + void registerDisplay( + PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, + std::shared_ptr<VSyncTracker> vsyncTracker = std::make_shared<mock::VSyncTracker>()) { registerDisplay(displayId, std::move(selectorPtr), - std::make_unique<mock::VsyncController>(), - std::make_shared<mock::VSyncTracker>()); + std::make_unique<mock::VsyncController>(), vsyncTracker); } void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, @@ -114,6 +115,15 @@ public: Scheduler::setPacesetterDisplay(displayId); } + std::optional<hal::PowerMode> getDisplayPowerMode(PhysicalDisplayId id) { + ftl::FakeGuard guard1(kMainThreadContext); + ftl::FakeGuard guard2(mDisplayLock); + return mDisplays.get(id).transform( + [](const Display& display) { return display.powerMode; }); + } + + using Scheduler::resyncAllToHardwareVsync; + auto& mutableAppConnectionHandle() { return mAppConnectionHandle; } auto& mutableLayerHistory() { return mLayerHistory; } auto& mutableAttachedChoreographers() { return mAttachedChoreographers; } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index bca14f5217..0909178777 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -306,6 +306,9 @@ public: EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, minFramePeriod()) + .WillRepeatedly( + Return(Period::fromNs(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD))); EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, @@ -463,8 +466,8 @@ public: mFlinger->commitTransactionsLocked(transactionFlags); } - void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) { - mFlinger->onComposerHalHotplug(hwcDisplayId, connection); + void onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, DisplayHotplugEvent event) { + mFlinger->onComposerHalHotplugEvent(hwcDisplayId, event); } auto setDisplayStateLocked(const DisplayState& s) { diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 4be07a1ddb..6a5635305a 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -34,38 +34,48 @@ constexpr nsecs_t toNs(std::chrono::duration<Rep, Per> const& tp) { return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count(); } -class FixedRateIdealStubTracker : public VSyncTracker { +class StubTracker : public VSyncTracker { public: - FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {} + StubTracker(nsecs_t period) : mPeriod(period) {} bool addVsyncTimestamp(nsecs_t) final { return true; } - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final { - auto const floor = timePoint % mPeriod; - if (floor == 0) { - return timePoint; - } - return timePoint - floor + mPeriod; + nsecs_t currentPeriod() const final { + std::lock_guard lock(mMutex); + return mPeriod; } - nsecs_t currentPeriod() const final { return mPeriod; } - - void setPeriod(nsecs_t) final {} + Period minFramePeriod() const final { return Period::fromNs(currentPeriod()); } void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setDisplayModeData(const DisplayModeData&) final {} + void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {} + void setRenderRate(Fps) final {} + void onFrameBegin(TimePoint, TimePoint) final {} + void onFrameMissed(TimePoint) final {} void dump(std::string&) const final {} -private: - nsecs_t const mPeriod; +protected: + std::mutex mutable mMutex; + nsecs_t mPeriod; }; -class VRRStubTracker : public VSyncTracker { +class FixedRateIdealStubTracker : public StubTracker { public: - VRRStubTracker(nsecs_t period) : mPeriod{period} {} + FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {} - bool addVsyncTimestamp(nsecs_t) final { return true; } + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final { + auto const floor = timePoint % mPeriod; + if (floor == 0) { + return timePoint; + } + return timePoint - floor + mPeriod; + } +}; + +class VRRStubTracker : public StubTracker { +public: + VRRStubTracker(nsecs_t period) : StubTracker(period) {} nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final { std::lock_guard lock(mMutex); @@ -83,21 +93,7 @@ public: mBase = last_known; } - nsecs_t currentPeriod() const final { - std::lock_guard lock(mMutex); - return mPeriod; - } - - void setPeriod(nsecs_t) final {} - void resetModel() final {} - bool needsMoreSamples() const final { return false; } - bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setDisplayModeData(const DisplayModeData&) final {} - void dump(std::string&) const final {} - private: - std::mutex mutable mMutex; - nsecs_t mPeriod; nsecs_t mBase = 0; }; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 83108662b9..2047018a15 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -30,9 +30,10 @@ #include <scheduler/TimeKeeper.h> -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncTracker.h" +#include "mock/MockVSyncTracker.h" #include <com_android_graphics_surfaceflinger_flags.h> @@ -42,7 +43,7 @@ using namespace std::literals; namespace android::scheduler { using namespace com::android::graphics::surfaceflinger; -class MockVSyncTracker : public VSyncTracker { +class MockVSyncTracker : public mock::VSyncTracker { public: MockVSyncTracker(nsecs_t period) : mPeriod{period} { ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_)) @@ -52,16 +53,6 @@ public: .WillByDefault(Invoke(this, &MockVSyncTracker::getCurrentPeriod)); } - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); - nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { return timePoint; diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 30a2855955..7a498c9d29 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -23,8 +23,9 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #define LOG_NDEBUG 0 -#include "FlagUtils.h" +#include <common/test/FlagUtils.h> #include "Scheduler/VSyncPredictor.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> @@ -39,12 +40,25 @@ using namespace testing; using namespace std::literals; using namespace com::android::graphics::surfaceflinger; +using NotifyExpectedPresentConfig = + ::aidl::android::hardware::graphics::composer3::VrrConfig::NotifyExpectedPresentConfig; + +using android::mock::createDisplayMode; +using android::mock::createDisplayModeBuilder; +using android::mock::createVrrDisplayMode; + namespace android::scheduler { +namespace { MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") { return arg <= value + tolerance && arg >= value - tolerance; } +MATCHER_P(FpsMatcher, value, "equals") { + using fps_approx_ops::operator==; + return arg == value; +} + std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) { std::vector<nsecs_t> vsyncs(count); std::generate(vsyncs.begin(), vsyncs.end(), @@ -54,21 +68,27 @@ std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); +ftl::NonNull<DisplayModePtr> displayMode(nsecs_t period) { + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(period); + return ftl::as_non_null(createDisplayMode(DisplayModeId(0), refreshRate, kGroup, kResolution, + DEFAULT_DISPLAY_ID)); +} +} // namespace + struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; + ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod); scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, - mPeriod, - kHistorySize, - kMinimumSamplesForPrediction, - kOutlierTolerancePercent, - mVsyncTrackerCallback}; + VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, + kOutlierTolerancePercent, mVsyncTrackerCallback}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -78,7 +98,7 @@ TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { EXPECT_THAT(model.intercept, Eq(0)); auto const changedPeriod = 2000; - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); model = tracker.getVSyncPredictionModel(); EXPECT_THAT(model.slope, Eq(changedPeriod)); EXPECT_THAT(model.intercept, Eq(0)); @@ -99,7 +119,7 @@ TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) { EXPECT_FALSE(tracker.needsMoreSamples()); auto const changedPeriod = mPeriod * 2; - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); EXPECT_TRUE(tracker.needsMoreSamples()); for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { @@ -133,7 +153,7 @@ TEST_F(VSyncPredictorTest, uponNotifiedOfInaccuracyUsesSynthetic) { } EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod)); - tracker.setPeriod(changedPeriod); + tracker.setDisplayModePtr(displayMode(changedPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod)); } @@ -179,7 +199,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) { auto constexpr expectedPeriod = 16639242; auto constexpr expectedIntercept = 1049341; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -198,7 +218,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_90hzLowVariance) { auto expectedPeriod = 11089413; auto expectedIntercept = 94421; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -225,7 +245,7 @@ TEST_F(VSyncPredictorTest, adaptsToFenceTimelinesDiscontinuous_22hzLowVariance) auto expectedPeriod = 45450152; auto expectedIntercept = 469647; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -251,7 +271,7 @@ TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) { auto expectedPeriod = 1999892; auto expectedIntercept = 86342; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -271,7 +291,7 @@ TEST_F(VSyncPredictorTest, handlesVsyncChange) { auto const simulatedVsyncsSlow = generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase); - tracker.setPeriod(fastPeriod); + tracker.setDisplayModePtr(displayMode(fastPeriod)); for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } @@ -281,7 +301,7 @@ TEST_F(VSyncPredictorTest, handlesVsyncChange) { EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError)); EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); - tracker.setPeriod(slowPeriod); + tracker.setDisplayModePtr(displayMode(slowPeriod)); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } @@ -305,7 +325,7 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase); auto idealPeriod = 100000; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } @@ -313,14 +333,14 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { EXPECT_THAT(model.slope, Eq(fastPeriod)); EXPECT_THAT(model.intercept, Eq(0)); - tracker.setPeriod(slowPeriod); + tracker.setDisplayModePtr(displayMode(slowPeriod)); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } // we had a model for 100ns mPeriod before, use that until the new samples are // sufficiently built up - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); model = tracker.getVSyncPredictionModel(); EXPECT_THAT(model.slope, Eq(fastPeriod)); EXPECT_THAT(model.intercept, Eq(0)); @@ -369,7 +389,7 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { auto const expectedPeriod = 11113919; auto const expectedIntercept = -1195945; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -388,11 +408,8 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, - mPeriod, - 20, - kMinimumSamplesForPrediction, - kOutlierTolerancePercent, + const auto mode = displayMode(mPeriod); + VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent, mVsyncTrackerCallback}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, @@ -407,7 +424,7 @@ TEST_F(VSyncPredictorTest, hasEnoughPrecision) { auto const expectedPeriod = 16698426; auto const expectedIntercept = 58055; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -420,7 +437,7 @@ TEST_F(VSyncPredictorTest, hasEnoughPrecision) { TEST_F(VSyncPredictorTest, resetsWhenInstructed) { auto const idealPeriod = 10000; auto const realPeriod = 10500; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto i = 0; i < kMinimumSamplesForPrediction; i++) { tracker.addVsyncTimestamp(i * realPeriod); } @@ -562,7 +579,7 @@ TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) { auto constexpr expectedPeriod = 16'644'742; auto constexpr expectedIntercept = 125'626; - tracker.setPeriod(idealPeriod); + tracker.setDisplayModePtr(displayMode(idealPeriod)); for (auto const& timestamp : simulatedVsyncs) { tracker.addVsyncTimestamp(timestamp); } @@ -580,7 +597,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)}); + tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -602,12 +619,12 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - tracker.setDisplayModeData({.renderRate = refreshRate / 4}); + tracker.setRenderRate(refreshRate / 4); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setDisplayModeData({.renderRate = refreshRate / 2}); + tracker.setRenderRate(refreshRate / 2); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod)); @@ -615,7 +632,7 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setDisplayModeData({.renderRate = refreshRate / 6}); + tracker.setRenderRate(refreshRate / 6); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); } @@ -629,7 +646,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { tracker.addVsyncTimestamp(mNow); } - tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)}); + tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -642,16 +659,27 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { SET_FLAG_FOR_TEST(flags::vrr_config, true); + const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - DisplayModeData displayModeData = - DisplayModeData{.renderRate = refreshRate, - .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; - tracker.setDisplayModeData(displayModeData); + NotifyExpectedPresentConfig notifyExpectedPresentConfig; + notifyExpectedPresentConfig.notifyExpectedPresentTimeoutNs = Period::fromNs(30).ns(); + + hal::VrrConfig vrrConfig; + vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; + vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs(); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto mode = + ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup, + kResolution, DEFAULT_DISPLAY_ID)); + + tracker.setDisplayModePtr(mode); auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod), - displayModeData, Period::fromNs(mPeriod))) + onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode, + FpsMatcher(refreshRate))) .Times(1); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); mNow += mPeriod; @@ -659,20 +687,50 @@ TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { tracker.addVsyncTimestamp(mNow); } - displayModeData = DisplayModeData{.renderRate = refreshRate / 2, - .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; - tracker.setDisplayModeData(displayModeData); + tracker.setRenderRate(refreshRate / 2); { // out of render rate phase EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod), - displayModeData, Period::fromNs(mPeriod))) + onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode, + FpsMatcher(refreshRate / 2))) .Times(1); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); } } +TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(500); + const auto minFrameRate = Fps::fromPeriodNsecs(1000); + hal::VrrConfig vrrConfig; + vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs(); + const ftl::NonNull<DisplayModePtr> kMode = + ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID) + .setVrrConfig(std::move(vrrConfig)) + .build()); + + VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction, + kOutlierTolerancePercent, mVsyncTrackerCallback}; + + vrrTracker.setRenderRate(minFrameRate); + vrrTracker.addVsyncTimestamp(0); + EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700)); + EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); + + vrrTracker.onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500)); + EXPECT_EQ(1500, vrrTracker.nextAnticipatedVSyncTimeFrom(1300)); + EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); + + vrrTracker.onFrameMissed(TimePoint::fromNs(2500)); + EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2300)); + EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3300)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index aca3ccca6d..8d9623de1c 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -31,6 +31,9 @@ #include <scheduler/TimeKeeper.h> +#include "mock/DisplayHardware/MockDisplayMode.h" +#include "mock/MockVSyncTracker.h" + #include "Scheduler/VSyncDispatch.h" #include "Scheduler/VSyncReactor.h" #include "Scheduler/VSyncTracker.h" @@ -40,20 +43,7 @@ using namespace std::literals; namespace android::scheduler { -class MockVSyncTracker : public VSyncTracker { -public: - MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); } - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); -}; - +namespace { class MockClock : public Clock { public: MOCK_CONST_METHOD0(now, nsecs_t()); @@ -93,18 +83,33 @@ std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); +ftl::NonNull<DisplayModePtr> displayMode(nsecs_t vsyncPeriod) { + const int32_t kGroup = 0; + const auto kResolution = ui::Size(1920, 1080); + const auto refreshRate = Fps::fromPeriodNsecs(vsyncPeriod); + return ftl::as_non_null(mock::createDisplayMode(DisplayModeId(0), refreshRate, kGroup, + kResolution, DEFAULT_DISPLAY_ID)); +} + +MATCHER_P(DisplayModeMatcher, value, "display mode equals") { + return arg->getId() == value->getId() && equalsExceptDisplayModeId(*arg, *value); +} + +} // namespace + class VSyncReactorTest : public testing::Test { protected: VSyncReactorTest() - : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()), + : mMockTracker(std::make_shared<NiceMock<mock::VSyncTracker>>()), mMockClock(std::make_shared<NiceMock<MockClock>>()), mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit, false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); + ON_CALL(*mMockTracker, addVsyncTimestamp(_)).WillByDefault(Return(true)); } - std::shared_ptr<MockVSyncTracker> mMockTracker; + std::shared_ptr<mock::VSyncTracker> mMockTracker; std::shared_ptr<MockClock> mMockClock; static constexpr size_t kPendingLimit = 3; static constexpr nsecs_t mDummyTime = 47; @@ -194,7 +199,8 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -206,8 +212,8 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { nsecs_t const newPeriod = 5000; - EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod, false); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); @@ -217,7 +223,7 @@ TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); - EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(/*displayMode(newPeriod)*/ _)).Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); @@ -226,7 +232,7 @@ TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { nsecs_t sampleTime = 0; nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -234,7 +240,7 @@ TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(period, false); + mReactor.onDisplayModeChanged(displayMode(period), false); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -244,13 +250,13 @@ TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) { nsecs_t const secondPeriod = 5000; nsecs_t const thirdPeriod = 2000; - mReactor.startPeriodTransition(secondPeriod, false); + mReactor.onDisplayModeChanged(displayMode(secondPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(thirdPeriod, false); + mReactor.onDisplayModeChanged(displayMode(thirdPeriod), false); EXPECT_TRUE( mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -291,21 +297,22 @@ TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncP TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) { nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) { nsecs_t const newPeriod = 5000; - EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod, false); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(_)).Times(0); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); - EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); + EXPECT_CALL(*mMockTracker, setDisplayModePtr(DisplayModeMatcher(displayMode(newPeriod)))) + .Times(1); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } @@ -323,7 +330,7 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); auto time = 0; auto constexpr numTimestampSubmissions = 10; @@ -348,7 +355,7 @@ TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); auto time = 0; // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps. @@ -365,7 +372,7 @@ TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsH auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); time += period; mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); @@ -381,7 +388,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); static auto constexpr numSamplesWithNewPeriod = 4; Sequence seq; @@ -408,7 +415,7 @@ TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -428,7 +435,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { nsecs_t const newPeriod1 = 4000; nsecs_t const newPeriod2 = 7000; - mReactor.startPeriodTransition(newPeriod1, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod1), false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -447,7 +454,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); - mReactor.startPeriodTransition(newPeriod2, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod2), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); @@ -460,7 +467,7 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod, false); + mReactor.onDisplayModeChanged(displayMode(newPeriod), false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -484,7 +491,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // First, set the same period, which should only be confirmed when we receive two // matching callbacks - idleReactor.startPeriodTransition(10000, false); + idleReactor.onDisplayModeChanged(displayMode(10000), false); EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period but incorrect timestamp delta @@ -497,7 +504,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Then, set a new period, which should be confirmed as soon as we receive a callback // reporting the new period nsecs_t const newPeriod = 5000; - idleReactor.startPeriodTransition(newPeriod, false); + idleReactor.onDisplayModeChanged(displayMode(newPeriod), false); // Incorrect timestamp delta and period EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp index a8a3cd0293..bfdd5963f3 100644 --- a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp +++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp @@ -25,10 +25,12 @@ #include <scheduler/Fps.h> #include "Scheduler/VsyncSchedule.h" #include "ThreadContext.h" +#include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +using android::mock::createDisplayMode; using testing::_; namespace android { @@ -157,35 +159,35 @@ TEST_F(VsyncScheduleTest, StartPeriodTransition) { // allowed. ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true)); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, false)); - mVsyncSchedule->startPeriodTransition(period, false); + mVsyncSchedule->onDisplayModeChanged(mode, false); } TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) { ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); mVsyncSchedule->enableHardwareVsync(); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(_, _)).Times(0); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, false)); - mVsyncSchedule->startPeriodTransition(period, false); + mVsyncSchedule->onDisplayModeChanged(mode, false); } TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) { ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); - const Period period = (60_Hz).getPeriod(); + const auto mode = ftl::as_non_null(createDisplayMode(DisplayModeId(0), 60_Hz)); EXPECT_CALL(mRequestHardwareVsync, Call(kDisplayId, true)); - EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true)); + EXPECT_CALL(getController(), onDisplayModeChanged(mode, true)); - mVsyncSchedule->startPeriodTransition(period, true); + mVsyncSchedule->onDisplayModeChanged(mode, true); } TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h index cb05c00699..5bcce501e6 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h @@ -52,6 +52,7 @@ inline DisplayModePtr createVrrDisplayMode( .setVrrConfig(std::move(vrrConfig)) .build(); } + inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) { return DisplayMode::Builder(modePtr->getHwcId()) .setId(modePtr->getId()) diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp index bcccae5b1b..cc24f42d2e 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp @@ -17,9 +17,13 @@ #include "mock/MockVSyncTracker.h" namespace android::mock { +using testing::Return; // Explicit default instantiation is recommended. -VSyncTracker::VSyncTracker() = default; VSyncTracker::~VSyncTracker() = default; +VSyncTracker::VSyncTracker() { + ON_CALL(*this, minFramePeriod()).WillByDefault(Return(Period::fromNs(0))); +} + } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 31eb86e4c5..e588bb9a3f 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -27,15 +27,18 @@ public: VSyncTracker(); ~VSyncTracker() override; - MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); - MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); - MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(resetModel, void()); - MOCK_CONST_METHOD0(needsMoreSamples, bool()); - MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); + MOCK_METHOD(bool, addVsyncTimestamp, (nsecs_t), (override)); + MOCK_METHOD(nsecs_t, nextAnticipatedVSyncTimeFrom, (nsecs_t), (const, override)); + MOCK_METHOD(nsecs_t, currentPeriod, (), (const, override)); + MOCK_METHOD(Period, minFramePeriod, (), (const, override)); + MOCK_METHOD(void, resetModel, (), (override)); + MOCK_METHOD(bool, needsMoreSamples, (), (const, override)); + MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (const, override)); + MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override)); + MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override)); + MOCK_METHOD(void, onFrameMissed, (TimePoint), (override)); + MOCK_METHOD(void, dump, (std::string&), (const, override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 69ec60acd4..f7433908b0 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -29,7 +29,7 @@ public: MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override)); MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override)); - MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override)); + MOCK_METHOD(void, onDisplayModeChanged, (ftl::NonNull<DisplayModePtr>, bool), (override)); MOCK_METHOD(void, setIgnorePresentFences, (bool), (override)); MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h index b8e24e0593..b48529f4ac 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h @@ -23,13 +23,10 @@ namespace android::scheduler::mock { struct VsyncTrackerCallback final : IVsyncTrackerCallback { - MOCK_METHOD(void, onVsyncGenerated, - (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period), - (override)); + MOCK_METHOD(void, onVsyncGenerated, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); }; struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { - void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, - Period) override{}; + void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{}; }; } // namespace android::scheduler::mock diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING index 7e382a3841..63a2bd0059 100644 --- a/services/vibratorservice/TEST_MAPPING +++ b/services/vibratorservice/TEST_MAPPING @@ -6,10 +6,6 @@ // TODO(b/293603710): Fix flakiness { "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay" - }, - // TODO(b/293623689): Fix flakiness - { - "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleMultipleCallbacksRunsInDelayOrder" } ] } |