Merge "SF: guard texture view update tolerance logic with small area allow list" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 17e5cc1..83b336c 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1035,8 +1035,6 @@
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 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 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 6f5deb9..c30c00f 100644
--- a/cmds/evemu-record/main.rs
+++ b/cmds/evemu-record/main.rs
@@ -26,7 +26,7 @@
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 @@
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 @@
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 @@
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 @@
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 ba8b02d..9d2c791 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -60,6 +60,27 @@
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 @@
*
* @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 @@
* 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 @@
* @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 @@
* 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 @@
* 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 @@
* @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 @@
* 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 @@
* 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 eccd5db..d73c3a4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -90,7 +90,6 @@
"Stability.cpp",
"Status.cpp",
"TextOutput.cpp",
- "Trace.cpp",
"Utils.cpp",
"file.cpp",
],
@@ -251,7 +250,6 @@
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 c5f0730..0035aeb 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 ad458eb..155588d 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 @@
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 1ebfa1a..0000000
--- 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 dc572ac..ac845bc 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -119,8 +119,8 @@
"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 @@
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 9937842..95318b2 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 47b9f58..ccf3ce8 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -139,6 +139,7 @@
"performance*",
"portability*",
],
+ afdo: true,
}
cc_library_headers {
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index ca14286..99da1eb 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -31,6 +31,18 @@
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 d2b37aa..69737fa 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -24,13 +24,13 @@
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 dbddbe1..96c66a8 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -34,7 +34,6 @@
$(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 c84ee1f..44a9e52 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -423,10 +423,10 @@
"libhidlbase",
"liblog",
"libnativewindow",
+ "libselinux",
"libsync",
"libui",
"libutils",
- "libvndksupport",
],
static_libs: [
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index b6a47fb..744201a 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 @@
// 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 fd8fc8d..38fab9c 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,7 +86,7 @@
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 @@
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 @@
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 2676e0a..e29ce41 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -76,7 +76,6 @@
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 bf354e7..1fc80c3 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 @@
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 a807d82..d13a260 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -43,6 +43,13 @@
}
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 b068f48..a98ea86 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1057,7 +1057,12 @@
/**
* 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 {
/**
* 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 8821c0e..ba20d1f 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -433,6 +433,10 @@
// 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 37ebfc4..2ec6d18 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -518,14 +518,16 @@
}
}
{
- 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 7eacb0a..65d2b8f 100644
--- a/libs/ui/include/ui/DisplayMap.h
+++ b/libs/ui/include/ui/DisplayMap.h
@@ -23,13 +23,18 @@
// 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 9deba01..eda5ea4 100644
--- a/libs/ultrahdr/Android.bp
+++ b/libs/ultrahdr/Android.bp
@@ -21,7 +21,8 @@
}
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 {
- name: "libjpegencoder",
+ name: "libjpegencoder-deprecated",
+ enabled: false,
host_supported: true,
vendor_available: true,
@@ -64,7 +66,8 @@
}
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 e999a8b..2fa361f 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 6c0a2f5..8d9132f 100644
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ b/libs/ultrahdr/fuzzer/Android.bp
@@ -22,7 +22,8 @@
}
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_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 {
- 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 bda804a..00cc797 100644
--- a/libs/ultrahdr/tests/Android.bp
+++ b/libs/ultrahdr/tests/Android.bp
@@ -22,7 +22,8 @@
}
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 f2a313c..cb063af 100644
--- a/libs/vibrator/fuzzer/Android.bp
+++ b/libs/vibrator/fuzzer/Android.bp
@@ -47,6 +47,17 @@
],
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 54da8e8..6ad3de0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3980,16 +3980,6 @@
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 @@
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 @@
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 5367751..cd0500c 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -128,20 +128,14 @@
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 6d2283e..9a31678 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -48,15 +48,7 @@
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 6780dce..b89b7f3 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 @@
}
// 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 @@
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 5bea2ba..ff9bd9e 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 @@
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 @@
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 @@
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 2f26c35..e220133 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -9660,6 +9660,50 @@
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 @@
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 @@
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 8b16890..1f72e8b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -19,6 +19,7 @@
"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 0000000..ef723c2
--- /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 0000000..99b5b8b
--- /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 2740a97..e316190 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -91,6 +91,9 @@
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
}
cc_library {
@@ -114,6 +117,9 @@
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
],
+ shared_libs: [
+ "server_configurable_flags",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -121,6 +127,10 @@
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 @@
"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 422a799..f1d6f52 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 @@
// 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 de82931..eac5d97 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -62,6 +62,7 @@
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 d95fbea..ec6a4e9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -80,7 +80,9 @@
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 @@
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 @@
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 @@
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 ce2b96f..1f241b0 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 @@
// 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 c88fbd6..95ea3a4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -80,7 +80,10 @@
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 002177b..7be5fe3 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 @@
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 @@
}
}
+ // 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 469fb38..0475881 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -430,4 +430,13 @@
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 2ae80de..e4d7578 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -427,7 +427,8 @@
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 @@
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 @@
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 @@
}
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 @@
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 13b6307..a18397d 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 @@
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 8dab6ce..0e3fdbb 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -76,6 +76,11 @@
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 60ed660..602dd23 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 @@
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 @@
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 5537fcd..5006e7d 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::Invoke;
using testing::IsEmpty;
using testing::Mock;
+using testing::NiceMock;
using testing::Pointee;
using testing::Property;
using testing::Ref;
@@ -4900,5 +4902,54 @@
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 d9318af..763b998 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 @@
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 7fdf9e7..249c40b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -70,7 +70,7 @@
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 @@
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 @@
}
}
-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 @@
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 a061fca..51c7be0 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -186,35 +186,19 @@
* 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 @@
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 @@
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 2d957e6..3690219 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -78,6 +78,7 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 e7f807f..29fe380 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 @@
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 26c0d67..10df216 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -545,6 +545,7 @@
}
}
+ 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 20f7548..31c2833 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 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 1e7c6da..ed52b95 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 @@
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 f00ef67..e005ad3 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -251,7 +251,12 @@
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 e55cd3e..55b395b 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 @@
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 9a4981c..4555a9e 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -29,7 +29,6 @@
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 @@
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 9dc3938..803299c 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -280,6 +280,19 @@
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 @@
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 @@
}
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 @@
// 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 @@
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 @@
mJankType = JankType::Dropped;
// Since frame was not presented, lets drop any present value
mActuals.presentTime = 0;
+ mJankSeverityType = JankSeverityType::Unknown;
}
}
@@ -625,7 +655,7 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 6598e21..b5047a3 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -168,6 +168,7 @@
// 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 @@
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 @@
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 @@
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 @@
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 743cbf3..a92cc03 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -814,9 +814,12 @@
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 @@
}
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 b1a18ae..21172ca 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -125,7 +125,7 @@
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 b9561dc..f7800bb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -193,7 +193,7 @@
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 @@
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 @@
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 28168c3..c772e0e 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1178,6 +1178,10 @@
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 8f658d5..6db39f1 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -276,13 +276,11 @@
}
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 71b85bd..5de148e 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -18,20 +18,16 @@
// 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 @@
// 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 @@
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 6d2586a..d714848 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -21,6 +21,7 @@
"libui",
"libutils",
],
+ static_libs: ["libsurfaceflinger_common"],
}
cc_library_headers {
@@ -61,5 +62,9 @@
"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 405c982..d8c408f 100644
--- a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
+++ b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
@@ -29,6 +29,8 @@
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 d309adc..9c00302 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -278,6 +278,8 @@
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 bf3a7bc..97fca39 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -276,17 +276,16 @@
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 @@
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 @@
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 @@
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 @@
ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
}
- return consistent;
+ return consistent ? maxClosestRate : Fps();
}
FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
@@ -485,6 +496,8 @@
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 @@
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 d24fc33..50bb83d 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -81,10 +81,11 @@
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 @@
// 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 @@
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 6a7063e..e06221a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -332,6 +332,15 @@
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 @@
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 @@
// 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 @@
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 @@
case LayerVoteType::ExplicitExact:
explicitExact++;
break;
+ case LayerVoteType::ExplicitGte:
+ explicitGteLayers++;
+ break;
case LayerVoteType::ExplicitCategory:
explicitCategoryVoteLayers++;
if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
@@ -535,7 +555,7 @@
}
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 @@
case LayerVoteType::Max:
case LayerVoteType::ExplicitDefault:
case LayerVoteType::ExplicitExact:
+ case LayerVoteType::ExplicitGte:
case LayerVoteType::ExplicitCategory:
return false;
}
@@ -1081,7 +1102,7 @@
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 40e9a83..a1a7c28 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -39,13 +39,6 @@
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 @@
// 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 @@
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 25d9f57..aa8d54d 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -92,7 +92,12 @@
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::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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 1773d09..454ef83 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 @@
// 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 @@
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 @@
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 @@
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 @@
});
}
+ // 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 f5f93ce..7379a46 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -47,16 +47,16 @@
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 @@
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 @@
[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 @@
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 @@
const size_t numSamples = mTimestamps.size();
if (numSamples < kMinimumSamplesForPrediction) {
- mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+ mRateMap[idealPeriod()] = {idealPeriod(), 0};
return true;
}
@@ -161,7 +183,7 @@
// 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 @@
}
if (CC_UNLIKELY(bottom == 0)) {
- it->second = {mIdealPeriod, 0};
+ it->second = {idealPeriod(), 0};
clearTimestamps();
return false;
}
@@ -207,9 +229,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,14 +381,127 @@
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;
+}
+
+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);
+
+ mDisplayModePtr = modePtr;
+ traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
+
+ static constexpr size_t kSizeLimit = 30;
+ if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+ mRateMap.erase(mRateMap.begin());
+ }
+
+ 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 {
@@ -369,26 +511,7 @@
}
VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
- return mRateMap.find(mIdealPeriod)->second;
-}
-
-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};
- }
-
- clearTimestamps();
+ return mRateMap.find(idealPeriod())->second;
}
void VSyncPredictor::clearTimestamps() {
@@ -412,18 +535,18 @@
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 c271eb7..72a3431 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 2938aa3..24737e4 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -116,32 +116,34 @@
}
}
-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 @@
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 @@
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 @@
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 f230242..2415a66 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 @@
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 @@
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 @@
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 7eedc31..1ed863c 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 @@
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 @@
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 9177899..807a7fb 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 @@
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 5fb53f9..ff1c9e9 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 @@
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 @@
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 @@
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 @@
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 ca61f87..722ea0b 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -57,21 +57,23 @@
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 @@
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 ae74205..70d4846 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -50,11 +50,11 @@
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 @@
// 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 @@
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 bb2de75..0154060 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/IVsyncSource.h
@@ -23,6 +23,7 @@
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 7a18654..e80372b 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -27,28 +27,28 @@
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 @@
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 @@
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 1e038d1..c883385 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 @@
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 @@
vsyncSourceOpt
.or_else([&] {
return std::make_optional(
- VsyncSource(period,
+ VsyncSource(period, period,
args.expectedVsyncTime));
})
.value(),
@@ -88,6 +97,7 @@
TimePoint& frameBeginTime;
const Period period;
+ const Period minFramePeriod;
bool ended = false;
};
@@ -103,7 +113,7 @@
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 @@
}
{
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ef9b457..57b0d5e 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 @@
.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 7049068..848cbb5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1190,7 +1190,7 @@
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 @@
// 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 @@
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 @@
updatePhaseConfiguration(activeMode.fps);
}
- if (pendingMode.event != scheduler::DisplayModeEvent::None) {
+ if (pendingModeOpt->emitEvent) {
dispatchDisplayModeChangeEvent(displayId, activeMode);
}
}
@@ -1329,13 +1329,15 @@
}
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 @@
const auto display = getDisplayDeviceLocked(id);
if (!display) continue;
- const auto desiredModeOpt = display->getDesiredMode();
+ auto desiredModeOpt = display->getDesiredMode();
if (!desiredModeOpt) {
continue;
}
@@ -1362,7 +1364,7 @@
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 @@
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 @@
// 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 @@
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 @@
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::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 @@
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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
mScheduler->enableSyntheticVsync(false);
constexpr bool kAllowToEnable = true;
- mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, refreshRate);
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
}
mVisibleRegionsDirty = true;
@@ -5844,7 +5871,8 @@
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 @@
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 @@
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 @@
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 @@
RenderAreaFuture renderAreaFuture = ftl::defer([=] {
return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace,
- false /* useIdentityTransform */,
args.hintForSeamlessTransition,
false /* captureSecureLayers */);
});
@@ -8948,7 +8974,10 @@
.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 75fd25a..788fe73 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 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 @@
// 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 @@
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 9d6d87e..ce8119e 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::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 e2a1498..07a606c 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -121,6 +121,9 @@
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(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 9aabbb9..38cb43a 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -60,6 +60,9 @@
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
similarity index 100%
rename from services/surfaceflinger/tests/unittests/FlagUtils.h
rename to 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 1a951b3..b2dc20e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h
@@ -41,6 +41,7 @@
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 @@
: 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 @@
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 9b2d453..4fc39cc 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -806,8 +806,7 @@
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 8fcfd81..b690d8d 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 @@
}
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 @@
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 @@
*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 @@
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 728708f..fa307e9 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h
@@ -89,8 +89,7 @@
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 @@
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 @@
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 71c59b2..fcb52c7 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -84,3 +84,26 @@
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 0a951d4..aeff5a5 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -56,60 +56,44 @@
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 2181370..15a98df 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -38,7 +38,6 @@
args.displayToken = sp<BBinder>::make();
args.width = 10;
args.height = 20;
- args.useIdentityTransform = true;
args.grayscale = true;
Parcel p;
@@ -56,7 +55,6 @@
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 3873b0c..c463a92 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 @@
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 @@
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, 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 @@
};
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, 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 @@
};
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 c040f29..803710d 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 20ea0c0..5c742d7 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
@@ -49,6 +49,7 @@
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 @@
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 @@
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 @@
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 @@
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 6d87717..ddc3967 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
kSurfaceFlingerPid,
FrameTimelineEvent::PRESENT_UNSPECIFIED, false,
false, FrameTimelineEvent::JANK_UNKNOWN,
+ FrameTimelineEvent::SEVERITY_UNKNOWN,
FrameTimelineEvent::PREDICTION_EXPIRED);
auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1);
@@ -1330,6 +1354,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 f1e841b..d3ce4f2 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 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 @@
}
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 befef48..787fa1c 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, 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 @@
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 1adf14f..b88ef56 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,8 @@
}
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()));
@@ -1182,6 +1184,23 @@
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()));
+
+ 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(LayerHistoryTest, smallDirtyLayer) {
auto layer = createLayer();
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 047ef5a..07a522a 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 16143e3..040a3bf 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -814,24 +814,24 @@
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 @@
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 @@
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 dc13a8d..0cacf81 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 @@
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 b5eb777..e515895 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 @@
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 @@
}
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 075f974..7ad97a2 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -46,7 +46,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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_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 1210d0b..a270dc9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -27,10 +27,10 @@
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 @@
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 cf3fab3..31e1330 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 @@
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 8b6f0f1..2a1b88e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -78,10 +78,11 @@
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 @@
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 bca14f5..0909178 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -306,6 +306,9 @@
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 @@
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 4be07a1..6a56353 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -34,12 +34,36 @@
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 currentPeriod() const final {
+ std::lock_guard lock(mMutex);
+ return mPeriod;
+ }
+
+ 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 setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
+ void setRenderRate(Fps) final {}
+ void onFrameBegin(TimePoint, TimePoint) final {}
+ void onFrameMissed(TimePoint) final {}
+ void dump(std::string&) const final {}
+
+protected:
+ std::mutex mutable mMutex;
+ nsecs_t mPeriod;
+};
+
+class FixedRateIdealStubTracker : public StubTracker {
+public:
+ FixedRateIdealStubTracker() : StubTracker{toNs(3ms)} {}
+
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
auto const floor = timePoint % mPeriod;
if (floor == 0) {
@@ -47,25 +71,11 @@
}
return timePoint - floor + mPeriod;
}
-
- nsecs_t currentPeriod() const final { 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:
- nsecs_t const mPeriod;
};
-class VRRStubTracker : public VSyncTracker {
+class VRRStubTracker : public StubTracker {
public:
- VRRStubTracker(nsecs_t period) : mPeriod{period} {}
-
- bool addVsyncTimestamp(nsecs_t) final { return true; }
+ VRRStubTracker(nsecs_t period) : StubTracker(period) {}
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
std::lock_guard lock(mMutex);
@@ -83,21 +93,7 @@
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 8310866..2047018 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 @@
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 @@
.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 30a2855..7a498c9 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 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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, 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 @@
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 @@
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 @@
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 @@
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 @@
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, 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 @@
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 aca3ccc..8d9623d 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 @@
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 @@
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 @@
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, 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 @@
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, 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 @@
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 @@
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, 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 a8a3cd0..bfdd596 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 @@
// 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 cb05c00..5bcce50 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -52,6 +52,7 @@
.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 bcccae5..cc24f42 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 31eb86e..e588bb9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -27,15 +27,18 @@
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 69ec60a..f743390 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -29,7 +29,7 @@
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 b8e24e0..b48529f 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 7e382a3..63a2bd0 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"
}
]
}