Merge changes I635d7d4c,I917e9385 into main
* changes:
ultrahdr: Correct hdr white nits for linear input
ultrahdr: updates to jpegr impl - 2
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 8105626..ea2ddda 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -692,7 +692,7 @@
while (func) {
if (!strchr(func, '*')) {
String8 fancyFunc = String8::format("\n%s\n", func);
- bool found = funcList.find(fancyFunc.string(), 0) >= 0;
+ bool found = funcList.find(fancyFunc.c_str(), 0) >= 0;
if (!found || func[0] == '\0') {
fprintf(stderr, "error: \"%s\" is not a valid kernel function "
"to trace.\n", func);
@@ -800,7 +800,7 @@
tokenizer->skipDelimiters(" ");
continue;
}
- ok &= setCategoryEnable(token.string());
+ ok &= setCategoryEnable(token.c_str());
}
delete tokenizer;
return ok;
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 8f1c01a..b727398 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -78,7 +78,7 @@
return -EPERM;
}
#if DEBUG
- ALOGD("openFile: %s, full=%s", path8.string(), fullPath.string());
+ ALOGD("openFile: %s, full=%s", path8.c_str(), fullPath.c_str());
#endif
int flags = 0;
bool checkRead = false;
@@ -96,10 +96,10 @@
flags = O_RDWR;
checkRead = checkWrite = true;
} else {
- mErrorLog << "Invalid mode requested: " << mode.string() << endl;
+ mErrorLog << "Invalid mode requested: " << mode.c_str() << endl;
return -EINVAL;
}
- int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG);
+ int fd = open(fullPath.c_str(), flags, S_IRWXU|S_IRWXG);
#if DEBUG
ALOGD("openFile: fd=%d", fd);
#endif
@@ -109,29 +109,29 @@
if (is_selinux_enabled() && seLinuxContext.size() > 0) {
String8 seLinuxContext8(seLinuxContext);
char* tmp = nullptr;
- getfilecon(fullPath.string(), &tmp);
+ getfilecon(fullPath.c_str(), &tmp);
Unique_SecurityContext context(tmp);
if (checkWrite) {
- int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
+ int accessGranted = selinux_check_access(seLinuxContext8.c_str(), context.get(),
"file", "write", nullptr);
if (accessGranted != 0) {
#if DEBUG
ALOGD("openFile: failed selinux write check!");
#endif
close(fd);
- mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl;
+ mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.c_str() << ", context " << seLinuxContext8.c_str() << ")" << endl;
return -EPERM;
}
}
if (checkRead) {
- int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(),
+ int accessGranted = selinux_check_access(seLinuxContext8.c_str(), context.get(),
"file", "read", nullptr);
if (accessGranted != 0) {
#if DEBUG
ALOGD("openFile: failed selinux read check!");
#endif
close(fd);
- mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl;
+ mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.c_str() << ", context " << seLinuxContext8.c_str() << ")" << endl;
return -EPERM;
}
}
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index a7bc018..c787113 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -141,6 +141,7 @@
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI &&
+ bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_ONBOARDING &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) {
MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode);
signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 0dc8f5a..fa9bcf3 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -49,6 +49,9 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
+ // Bugreport taken for onboarding related flows.
+ const int BUGREPORT_MODE_ONBOARDING = 7;
+
// Use pre-dumped data.
const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1;
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index e132b35..376d57a 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2176,6 +2176,11 @@
printf("========================================================\n");
}
+// Collects a lightweight dumpstate to be used for debugging onboarding related flows.
+static void DumpstateOnboardingOnly() {
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
+}
+
Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX";
const size_t buf_size = temp_file_pattern.length() + 1;
@@ -2308,6 +2313,7 @@
return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return dumpstate_hal_hidl::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
}
@@ -2329,6 +2335,7 @@
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
}
@@ -2812,6 +2819,8 @@
return "BUGREPORT_TELEPHONY";
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
return "BUGREPORT_WIFI";
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
+ return "BUGREPORT_ONBOARDING";
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
return "BUGREPORT_DEFAULT";
}
@@ -2857,6 +2866,10 @@
options->wifi_only = true;
options->do_screenshot = false;
break;
+ case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING:
+ options->onboarding_only = true;
+ options->do_screenshot = false;
+ break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
break;
}
@@ -3276,6 +3289,8 @@
DumpstateWifiOnly();
} else if (options_->limited_only) {
DumpstateLimitedOnly();
+ } else if (options_->onboarding_only) {
+ DumpstateOnboardingOnly();
} else {
// Dump state for the default case. This also drops root.
RunStatus s = DumpstateDefaultAfterCritical();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8a31c31..0a032ec 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -201,6 +201,7 @@
BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR,
BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY,
BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI,
+ BUGREPORT_ONBOARDING = android::os::IDumpstate::BUGREPORT_MODE_ONBOARDING,
BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT
};
@@ -412,6 +413,7 @@
bool show_header_only = false;
bool telephony_only = false;
bool wifi_only = false;
+ bool onboarding_only = false;
// Trimmed-down version of dumpstate to only include whitelisted logs.
bool limited_only = false;
// Whether progress updates should be published.
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 3d2bdf1..6c4e4b3 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -543,7 +543,7 @@
if ((status == TIMED_OUT) && (!asProto)) {
std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
- String8(serviceName).string(), timeout.count());
+ String8(serviceName).c_str(), timeout.count());
WriteStringToFd(msg, fd);
}
@@ -562,6 +562,6 @@
oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
std::string msg =
StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
- elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+ elapsedDuration.count(), String8(serviceName).c_str(), oss.str().c_str());
WriteStringToFd(msg, fd);
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b302f52..e2a2927 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fts.h>
#include <inttypes.h>
+#include <linux/fsverity.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -51,6 +52,7 @@
#include <android-base/unique_fd.h>
#include <cutils/ashmem.h>
#include <cutils/fs.h>
+#include <cutils/misc.h>
#include <cutils/properties.h>
#include <cutils/sched_policy.h>
#include <linux/quota.h>
@@ -84,6 +86,8 @@
using android::base::ParseUint;
using android::base::Split;
using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
using std::endl;
namespace android {
@@ -229,6 +233,14 @@
return ok();
}
+binder::Status checkUidInAppRange(int32_t appUid) {
+ if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) {
+ return ok();
+ }
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
+ StringPrintf("UID %d is outside of the range", appUid));
+}
+
#define ENFORCE_UID(uid) { \
binder::Status status = checkUid((uid)); \
if (!status.isOk()) { \
@@ -283,6 +295,14 @@
} \
}
+#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \
+ { \
+ binder::Status status = checkUidInAppRange((uid)); \
+ if (!status.isOk()) { \
+ return status; \
+ } \
+ }
+
#ifdef GRANULAR_LOCKS
/**
@@ -383,6 +403,33 @@
} // namespace
+binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate(
+ const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) {
+ int open_flags = fcntl(authFd.get(), F_GETFL);
+ if (open_flags < 0) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed");
+ }
+ if ((open_flags & O_ACCMODE) != O_WRONLY && (open_flags & O_ACCMODE) != O_RDWR) {
+ return exception(binder::Status::EX_SECURITY, "Received FD with unexpected open flag");
+ }
+ if (fstat(authFd.get(), &this->mStatFromAuthFd) < 0) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "fstat failed");
+ }
+ if (!S_ISREG(this->mStatFromAuthFd.st_mode)) {
+ return exception(binder::Status::EX_SECURITY, "Not a regular file");
+ }
+ // Don't accept a file owned by a different app.
+ uid_t uid = multiuser_get_uid(userId, appUid);
+ if (this->mStatFromAuthFd.st_uid != uid) {
+ return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid");
+ }
+ return ok();
+}
+
+bool InstalldNativeService::FsveritySetupAuthToken::isSameStat(const struct stat& st) const {
+ return memcmp(&st, &mStatFromAuthFd, sizeof(st)) == 0;
+}
+
status_t InstalldNativeService::start() {
IPCThreadState::self()->disableBackgroundScheduling(true);
status_t ret = BinderService<InstalldNativeService>::publish();
@@ -3857,5 +3904,84 @@
return *_aidl_return == -1 ? error() : ok();
}
+// Creates an auth token to be used in enableFsverity. This token is really to store a proof that
+// the caller can write to a file, represented by the authFd. Effectively, system_server as the
+// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable,
+// return null.
+//
+// appUid and userId are passed for additional ownership check, such that one app can not be
+// authenticated for another app's file. These parameters are assumed trusted for this purpose of
+// consistency check.
+//
+// Notably, creating the token allows us to manage the writable FD easily during enableFsverity.
+// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the
+// server allows the server to hold the only reference (as long as the client app doesn't).
+binder::Status InstalldNativeService::createFsveritySetupAuthToken(
+ const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId,
+ sp<IFsveritySetupAuthToken>* _aidl_return) {
+ CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid);
+ ENFORCE_VALID_USER(userId);
+
+ auto token = sp<FsveritySetupAuthToken>::make();
+ binder::Status status = token->authenticate(authFd, appUid, userId);
+ if (!status.isOk()) {
+ return status;
+ }
+ *_aidl_return = token;
+ return ok();
+}
+
+// Enables fs-verity for filePath, which must be an absolute path and the same inode as in the auth
+// token previously returned from createFsveritySetupAuthToken, and owned by the app uid. As
+// installd is more privileged than its client / system server, we attempt to limit what a
+// (compromised) client can do.
+//
+// The reason for this app request to go through installd is to avoid exposing a risky area (PKCS#7
+// signature verification) in the kernel to the app as an attack surface (it can't be system server
+// because it can't override DAC and manipulate app files). Note that we should be able to drop
+// these hops and simply the app calls the ioctl, once all upgrading devices run with a kernel
+// without fs-verity built-in signature (https://r.android.com/2650402).
+binder::Status InstalldNativeService::enableFsverity(const sp<IFsveritySetupAuthToken>& authToken,
+ const std::string& filePath,
+ const std::string& packageName,
+ int32_t* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PATH(filePath);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ LOCK_PACKAGE();
+ if (authToken == nullptr) {
+ return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token");
+ }
+
+ // Authenticate to check the targeting file is the same inode as the authFd.
+ sp<IBinder> authTokenBinder = IInterface::asBinder(authToken)->localBinder();
+ if (authTokenBinder == nullptr) {
+ return exception(binder::Status::EX_SECURITY, "Received a non-local auth token");
+ }
+ auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder);
+ unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ struct stat stFromPath;
+ if (fstat(rfd.get(), &stFromPath) < 0) {
+ *_aidl_return = errno;
+ return ok();
+ }
+ if (!authTokenInstance->isSameStat(stFromPath)) {
+ LOG(DEBUG) << "FD authentication failed";
+ *_aidl_return = EPERM;
+ return ok();
+ }
+
+ fsverity_enable_arg arg = {};
+ arg.version = 1;
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+ if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
+ *_aidl_return = errno;
+ } else {
+ *_aidl_return = 0;
+ }
+ return ok();
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 521afc3..0f28234 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -19,6 +19,7 @@
#define COMMANDS_H_
#include <inttypes.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <shared_mutex>
@@ -35,8 +36,26 @@
namespace android {
namespace installd {
+using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken;
+
class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld {
public:
+ class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken {
+ public:
+ FsveritySetupAuthToken() : mStatFromAuthFd() {}
+
+ binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid,
+ int32_t userId);
+ bool isSameStat(const struct stat& st) const;
+
+ private:
+ // Not copyable or movable
+ FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete;
+ FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete;
+
+ struct stat mStatFromAuthFd;
+ };
+
static status_t start();
static char const* getServiceName() { return "installd"; }
virtual status_t dump(int fd, const Vector<String16> &args) override;
@@ -192,6 +211,13 @@
const std::optional<std::string>& outputPath,
int32_t* _aidl_return);
+ binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd,
+ int32_t appUid, int32_t userId,
+ android::sp<IFsveritySetupAuthToken>* _aidl_return);
+ binder::Status enableFsverity(const android::sp<IFsveritySetupAuthToken>& authToken,
+ const std::string& filePath, const std::string& packageName,
+ int32_t* _aidl_return);
+
private:
std::recursive_mutex mLock;
std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 9ad853b..8893e38 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -134,6 +134,22 @@
int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath,
@utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+ interface IFsveritySetupAuthToken {
+ // Using an interface here is an easy way to create and maintain an IBinder object across
+ // the processes. When installd creates this binder object, it stores the file stat
+ // privately for later authentication, and only returns the reference to the caller process.
+ // Once the binder object has no reference count, it gets destructed automatically
+ // (alternatively, installd can maintain an internal mapping, but it is more error prone
+ // because the app may crash and not finish the fs-verity setup, keeping the memory unused
+ // forever).
+ //
+ // We don't necessarily need a method here, so it's left blank intentionally.
+ }
+ IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid,
+ int userId);
+ int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath,
+ @utf8InCpp String packageName);
+
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
const int FLAG_STORAGE_EXTERNAL = 0x4;
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 4221a3a..7648265 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -208,36 +208,13 @@
}
// Compute compiler filter.
- {
- std::string dex2oat_compiler_filter_arg;
- {
- // If we are booting without the real /data, don't spend time compiling.
- std::string vold_decrypt = GetProperty("vold.decrypt", "");
- bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
- vold_decrypt == "1";
-
- bool have_dex2oat_relocation_skip_flag = false;
- if (skip_compilation) {
- dex2oat_compiler_filter_arg = "--compiler-filter=extract";
- have_dex2oat_relocation_skip_flag = true;
- } else if (compiler_filter != nullptr) {
- dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s",
- compiler_filter);
- }
- if (have_dex2oat_relocation_skip_flag) {
- AddRuntimeArg("-Xnorelocate");
- }
- }
-
- if (dex2oat_compiler_filter_arg.empty()) {
- dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter",
- "--compiler-filter=%s");
- }
- AddArg(dex2oat_compiler_filter_arg);
-
- if (compilation_reason != nullptr) {
- AddArg(std::string("--compilation-reason=") + compilation_reason);
- }
+ if (compiler_filter != nullptr) {
+ AddArg(StringPrintf("--compiler-filter=%s", compiler_filter));
+ } else {
+ AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s"));
+ }
+ if (compilation_reason != nullptr) {
+ AddArg(std::string("--compilation-reason=") + compilation_reason);
}
AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 304ba7b..56f84a5 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -441,24 +441,6 @@
VerifyExpectedFlags();
}
-TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) {
- setSystemProperty("vold.decrypt", "trigger_restart_min_framework");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--compiler-filter", "=extract");
- SetExpectedFlagUsed("-Xnorelocate", "");
- VerifyExpectedFlags();
-}
-
-TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) {
- setSystemProperty("vold.decrypt", "1");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--compiler-filter", "=extract");
- SetExpectedFlagUsed("-Xnorelocate", "");
- VerifyExpectedFlags();
-}
-
TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) {
setSystemProperty("dalvik.vm.dex2oat-filter", "speed");
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 858a92c..4bc92af 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -42,9 +42,12 @@
#include "binder_test_utils.h"
#include "dexopt.h"
#include "globals.h"
+#include "unique_file.h"
#include "utils.h"
using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::os::ParcelFileDescriptor;
using std::filesystem::is_empty;
namespace android {
@@ -136,6 +139,16 @@
return fd;
}
+static void create_with_content(const std::string& path, uid_t owner, gid_t group, mode_t mode,
+ const std::string& content) {
+ int fd = ::open(path.c_str(), O_RDWR | O_CREAT, mode);
+ EXPECT_NE(fd, -1);
+ EXPECT_TRUE(android::base::WriteStringToFd(content, fd));
+ EXPECT_EQ(::fchown(fd, owner, group), 0);
+ EXPECT_EQ(::fchmod(fd, mode), 0);
+ close(fd);
+}
+
static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0);
}
@@ -527,6 +540,94 @@
externalStorageAppId, ceDataInodes, codePaths,
&externalStorageSize));
}
+
+class FsverityTest : public ServiceTest {
+protected:
+ binder::Status createFsveritySetupAuthToken(const std::string& path, int open_mode,
+ sp<IFsveritySetupAuthToken>* _aidl_return) {
+ unique_fd ufd(open(path.c_str(), open_mode));
+ EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno);
+ ParcelFileDescriptor rfd(std::move(ufd));
+ return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId,
+ _aidl_return);
+ }
+};
+
+TEST_F(FsverityTest, enableFsverity) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect to fs-verity setup to succeed
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(authToken != nullptr);
+
+ // Verity auth token works to enable fs-verity
+ int32_t errno_local;
+ status = service->enableFsverity(authToken, path, "fake.package.name", &errno_local);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_EQ(errno_local, 0);
+}
+
+TEST_F(FsverityTest, enableFsverity_nullAuthToken) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Verity null auth token fails
+ sp<IFsveritySetupAuthToken> authToken;
+ int32_t errno_local;
+ binder::Status status =
+ service->enableFsverity(authToken, path, "fake.package.name", &errno_local);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(FsverityTest, enableFsverity_differentFile) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect to fs-verity setup to succeed
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_TRUE(authToken != nullptr);
+
+ // Verity auth token does not work for a different file
+ const std::string anotherPath = kTestPath + "/bar";
+ ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath));
+ UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); });
+ int32_t errno_local;
+ status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local);
+ EXPECT_TRUE(status.isOk());
+ EXPECT_NE(errno_local, 0);
+}
+
+TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) {
+ const std::string path = kTestPath + "/foo";
+ create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect the fs-verity setup to fail
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDONLY, &authToken);
+ EXPECT_FALSE(status.isOk());
+}
+
+TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) {
+ const std::string path = kTestPath + "/foo";
+ // Simulate world-writable file owned by another app
+ create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content");
+ UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); });
+
+ // Expect the fs-verity setup to fail
+ sp<IFsveritySetupAuthToken> authToken;
+ binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken);
+ EXPECT_FALSE(status.isOk());
+}
+
static bool mkdirs(const std::string& path, mode_t mode) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp
new file mode 100644
index 0000000..c19c9da
--- /dev/null
+++ b/cmds/sfdo/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ name: "sfdo",
+
+ srcs: ["sfdo.cpp"],
+
+ shared_libs: [
+ "libutils",
+ "libgui",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp
new file mode 100644
index 0000000..55326ea
--- /dev/null
+++ b/cmds/sfdo/sfdo.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+#include <inttypes.h>
+#include <stdint.h>
+#include <any>
+#include <unordered_map>
+
+#include <cutils/properties.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+#include <private/gui/ComposerServiceAIDL.h>
+
+using namespace android;
+
+std::unordered_map<std::string, std::any> g_functions;
+
+const std::unordered_map<std::string, std::string> g_function_details = {
+ {"DebugFlash", "[optional(delay)] Perform a debug flash."},
+ {"FrameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
+ {"scheduleComposite", "Force composite ahead of next VSYNC."},
+ {"scheduleCommit", "Force commit ahead of next VSYNC."},
+ {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
+};
+
+static void ShowUsage() {
+ std::cout << "usage: sfdo [help, FrameRateIndicator show, DebugFlash enabled, ...]\n\n";
+ for (const auto& sf : g_functions) {
+ const std::string fn = sf.first;
+ std::string fdetails = "TODO";
+ if (g_function_details.find(fn) != g_function_details.end())
+ fdetails = g_function_details.find(fn)->second;
+ std::cout << " " << fn << ": " << fdetails << "\n";
+ }
+}
+
+int FrameRateIndicator([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ bool hide = false, show = false;
+ if (argc == 3) {
+ show = strcmp(argv[2], "show") == 0;
+ hide = strcmp(argv[2], "hide") == 0;
+ }
+
+ if (show || hide) {
+ ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show);
+ } else {
+ std::cerr << "Incorrect usage of FrameRateIndicator. Missing [hide | show].\n";
+ return -1;
+ }
+ return 0;
+}
+
+int DebugFlash([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ int delay = 0;
+ if (argc == 3) {
+ delay = atoi(argv[2]) == 0;
+ }
+
+ ComposerServiceAIDL::getComposerService()->setDebugFlash(delay);
+ return 0;
+}
+
+int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ ComposerServiceAIDL::getComposerService()->scheduleComposite();
+ return 0;
+}
+
+int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+ ComposerServiceAIDL::getComposerService()->scheduleCommit();
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ std::cout << "Execute SurfaceFlinger internal commands.\n";
+ std::cout << "sfdo requires to be run with root permissions..\n";
+
+ g_functions["FrameRateIndicator"] = FrameRateIndicator;
+ g_functions["DebugFlash"] = DebugFlash;
+ g_functions["scheduleComposite"] = scheduleComposite;
+ g_functions["scheduleCommit"] = scheduleCommit;
+
+ if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) {
+ std::cout << "Running: " << argv[1] << "\n";
+ const std::string key(argv[1]);
+ const auto fn = g_functions[key];
+ int result = std::any_cast<int (*)(int, char**)>(fn)(argc, argv);
+ if (result == 0) {
+ std::cout << "Success.\n";
+ }
+ return result;
+ } else {
+ ShowUsage();
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index c962c15..de8e1cf 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -107,6 +107,12 @@
}
prebuilt_etc {
+ name: "android.hardware.nfc.prebuilt.xml",
+ src: "android.hardware.nfc.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.reboot_escrow.prebuilt.xml",
src: "android.hardware.reboot_escrow.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index cedd361..ba8b02d 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -14,6 +14,23 @@
* limitations under the License.
*/
+ /**
+ * @defgroup APerformanceHint Performance Hint Manager
+ *
+ * APerformanceHint allows apps to create performance hint sessions for groups
+ * of threads, and provide hints to the system about the workload of those threads,
+ * to help the system more accurately allocate power for them. It is the NDK
+ * counterpart to the Java PerformanceHintManager SDK API.
+ *
+ * @{
+ */
+
+/**
+ * @file performance_hint.h
+ * @brief API for creating and managing a hint session.
+ */
+
+
#ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H
#define ANDROID_NATIVE_PERFORMANCE_HINT_H
@@ -48,7 +65,7 @@
* An opaque type representing a handle to a performance hint manager.
* It must be released after use.
*
- * <p>To use:<ul>
+ * To use:<ul>
* <li>Obtain the performance hint manager instance by calling
* {@link APerformanceHint_getManager} function.</li>
* <li>Create an {@link APerformanceHintSession} with
@@ -61,50 +78,43 @@
/**
* An opaque type representing a handle to a performance hint session.
* A session can only be acquired from a {@link APerformanceHintManager}
- * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be
+ * with {@link APerformanceHint_createSession}. It must be
* freed with {@link APerformanceHint_closeSession} after use.
*
* A Session represents a group of threads with an inter-related workload such that hints for
* their performance should be considered as a unit. The threads in a given session should be
- * long-life and not created or destroyed dynamically.
+ * long-lived and not created or destroyed dynamically.
*
- * <p>Each session is expected to have a periodic workload with a target duration for each
- * cycle. The cycle duration is likely greater than the target work duration to allow other
- * parts of the pipeline to run within the available budget. For example, a renderer thread may
- * work at 60hz in order to produce frames at the display's frame but have a target work
- * duration of only 6ms.</p>
+ * The work duration API can be used with periodic workloads to dynamically adjust thread
+ * performance and keep the work on schedule while optimizing the available power budget.
+ * When using the work duration API, the starting target duration should be specified
+ * while creating the session, and can later be adjusted with
+ * {@link APerformanceHint_updateTargetWorkDuration}. While using the work duration
+ * API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each
+ * cycle to report the actual time taken to complete to the system.
*
- * <p>After each cycle of work, the client is expected to use
- * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to
- * complete.</p>
- *
- * <p>To use:<ul>
- * <li>Update a sessions target duration for each cycle of work
- * with {@link APerformanceHint_updateTargetWorkDuration}.</li>
- * <li>Report the actual duration for the last cycle of work with
- * {@link APerformanceHint_reportActualWorkDuration}.</li>
- * <li>Release the session instance with
- * {@link APerformanceHint_closeSession}.</li></ul></p>
+ * All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
*/
typedef struct APerformanceHintSession APerformanceHintSession;
/**
* Acquire an instance of the performance hint manager.
*
- * @return manager instance on success, nullptr on failure.
+ * @return APerformanceHintManager instance on success, nullptr on failure.
*/
APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Creates a session for the given set of threads and sets their initial target work
* duration.
+ *
* @param manager The performance hint manager instance.
* @param threadIds The list of threads to be associated with this session. They must be part of
- * this app's thread group.
- * @param size the size of threadIds.
- * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session.
- * This must be positive.
- * @return manager instance on success, nullptr on failure.
+ * this process' thread group.
+ * @param size The size of the list of threadIds.
+ * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session.
+ * 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,
@@ -124,8 +134,8 @@
* Updates this session's target duration for each cycle of work.
*
* @param session The performance hint session instance to update.
- * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive.
- * @return 0 on success
+ * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive.
+ * @return 0 on success.
* EINVAL if targetDurationNanos is not positive.
* EPIPE if communication with the system service has failed.
*/
@@ -136,14 +146,13 @@
/**
* Reports the actual duration for the last cycle of work.
*
- * <p>The system will attempt to adjust the core placement of the threads within the thread
- * group and/or the frequency of the core on which they are run to bring the actual duration
- * close to the target duration.</p>
+ * 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 performance hint session instance to update.
- * @param actualDurationNanos how long the thread group took to complete its last task in
- * nanoseconds. This must be positive.
- * @return 0 on success
+ * @param actualDurationNanos The duration of time the thread group took to complete its last
+ * task in nanoseconds. This must be positive.
+ * @return 0 on success.
* EINVAL if actualDurationNanos is not positive.
* EPIPE if communication with the system service has failed.
*/
@@ -164,12 +173,13 @@
* Set a list of threads to the performance hint session. This operation will replace
* the current list of threads with the given list of threads.
*
- * @param session The performance hint session instance for the threads.
+ * @param session The performance hint session instance to update.
* @param threadIds The list of threads to be associated with this session. They must be part of
* this app's thread group.
- * @param size the size of the list of threadIds.
+ * @param size The size of the list of threadIds.
* @return 0 on success.
- * EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group.
+ * EINVAL if the list of thread ids is empty or if any of the thread ids are not part of
+ the thread group.
* EPIPE if communication with the system service has failed.
* EPERM if any thread id doesn't belong to the application.
*/
@@ -178,6 +188,21 @@
const pid_t* threadIds,
size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
+/**
+ * This tells the session that these threads can be
+ * safely scheduled to prefer power efficiency over performance.
+ *
+ * @param session The performance hint session instance to update.
+ * @param enabled The flag which sets whether this session will use power-efficient scheduling.
+ * @return 0 on success.
+ * EPIPE if communication with the system service has failed.
+ */
+int APerformanceHint_setPreferPowerEfficiency(
+ APerformanceHintSession* session,
+ bool enabled) __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif // ANDROID_NATIVE_PERFORMANCE_HINT_H
+
+/** @} */
\ No newline at end of file
diff --git a/include/input/MotionPredictorMetricsManager.h b/include/input/MotionPredictorMetricsManager.h
index 6284f07..12e50ba 100644
--- a/include/input/MotionPredictorMetricsManager.h
+++ b/include/input/MotionPredictorMetricsManager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -14,23 +14,193 @@
* limitations under the License.
*/
-#include <utils/Timers.h>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <limits>
+#include <optional>
+#include <vector>
+
+#include <input/Input.h> // for MotionEvent
+#include <input/RingBuffer.h>
+#include <utils/Timers.h> // for nsecs_t
+
+#include "Eigen/Core"
namespace android {
/**
* Class to handle computing and reporting metrics for MotionPredictor.
*
- * Currently an empty implementation, containing only the API.
+ * The public API provides two methods: `onRecord` and `onPredict`, which expect to receive the
+ * MotionEvents from the corresponding methods in MotionPredictor.
+ *
+ * This class stores AggregatedStrokeMetrics, updating them as new MotionEvents are passed in. When
+ * onRecord receives an UP or CANCEL event, this indicates the end of the stroke, and the final
+ * AtomFields are computed and reported to the stats library.
+ *
+ * If mMockLoggedAtomFields is set, the batch of AtomFields that are reported to the stats library
+ * for one stroke are also stored in mMockLoggedAtomFields at the time they're reported.
*/
class MotionPredictorMetricsManager {
public:
// Note: the MetricsManager assumes that the input interval equals the prediction interval.
- MotionPredictorMetricsManager(nsecs_t /*predictionInterval*/, size_t /*maxNumPredictions*/) {}
+ MotionPredictorMetricsManager(nsecs_t predictionInterval, size_t maxNumPredictions);
- void onRecord(const MotionEvent& /*inputEvent*/) {}
+ // This method should be called once for each call to MotionPredictor::record, receiving the
+ // forwarded MotionEvent argument.
+ void onRecord(const MotionEvent& inputEvent);
- void onPredict(const MotionEvent& /*predictionEvent*/) {}
+ // This method should be called once for each call to MotionPredictor::predict, receiving the
+ // MotionEvent that will be returned by MotionPredictor::predict.
+ void onPredict(const MotionEvent& predictionEvent);
+
+ // Simple structs to hold relevant touch input information. Public so they can be used in tests.
+
+ struct TouchPoint {
+ Eigen::Vector2f position; // (y, x) in pixels
+ float pressure;
+ };
+
+ struct GroundTruthPoint : TouchPoint {
+ nsecs_t timestamp;
+ };
+
+ struct PredictionPoint : TouchPoint {
+ // The timestamp of the last ground truth point when the prediction was made.
+ nsecs_t originTimestamp;
+
+ nsecs_t targetTimestamp;
+
+ // Order by targetTimestamp when sorting.
+ bool operator<(const PredictionPoint& other) const {
+ return this->targetTimestamp < other.targetTimestamp;
+ }
+ };
+
+ // Metrics aggregated so far for the current stroke. These are not the final fields to be
+ // reported in the atom (see AtomFields below), but rather an intermediate representation of the
+ // data that can be conveniently aggregated and from which the atom fields can be derived later.
+ //
+ // Displacement units are in pixels.
+ //
+ // "Along-trajectory error" is the dot product of the prediction error with the unit vector
+ // pointing towards the ground truth point whose timestamp corresponds to the prediction
+ // target timestamp, originating from the preceding ground truth point.
+ //
+ // "Off-trajectory error" is the component of the prediction error orthogonal to the
+ // "along-trajectory" unit vector described above.
+ //
+ // "High-velocity" errors are errors that are only accumulated when the velocity between the
+ // most recent two input events exceeds a certain threshold.
+ //
+ // "Scale-invariant errors" are the errors produced when the path length of the stroke is
+ // scaled to 1. (In other words, the error distances are normalized by the path length.)
+ struct AggregatedStrokeMetrics {
+ // General errors
+ float alongTrajectoryErrorSum = 0;
+ float alongTrajectorySumSquaredErrors = 0;
+ float offTrajectorySumSquaredErrors = 0;
+ float pressureSumSquaredErrors = 0;
+ size_t generalErrorsCount = 0;
+
+ // High-velocity errors
+ float highVelocityAlongTrajectorySse = 0;
+ float highVelocityOffTrajectorySse = 0;
+ size_t highVelocityErrorsCount = 0;
+
+ // Scale-invariant errors
+ float scaleInvariantAlongTrajectorySse = 0;
+ float scaleInvariantOffTrajectorySse = 0;
+ size_t scaleInvariantErrorsCount = 0;
+ };
+
+ // In order to explicitly indicate "no relevant data" for a metric, we report this
+ // large-magnitude negative sentinel value. (Most metrics are non-negative, so this value is
+ // completely unobtainable. For along-trajectory error mean, which can be negative, the
+ // magnitude makes it unobtainable in practice.)
+ static const int NO_DATA_SENTINEL = std::numeric_limits<int32_t>::min();
+
+ // Final metrics reported in the atom.
+ struct AtomFields {
+ int deltaTimeBucketMilliseconds = 0;
+
+ // General errors
+ int alongTrajectoryErrorMeanMillipixels = NO_DATA_SENTINEL;
+ int alongTrajectoryErrorStdMillipixels = NO_DATA_SENTINEL;
+ int offTrajectoryRmseMillipixels = NO_DATA_SENTINEL;
+ int pressureRmseMilliunits = NO_DATA_SENTINEL;
+
+ // High-velocity errors
+ int highVelocityAlongTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
+ int highVelocityOffTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
+
+ // Scale-invariant errors
+ int scaleInvariantAlongTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
+ int scaleInvariantOffTrajectoryRmse = NO_DATA_SENTINEL; // millipixels
+ };
+
+ // Allow tests to pass in a mock AtomFields pointer.
+ //
+ // When metrics are reported to the stats library on stroke end, they will also be written to
+ // mockLoggedAtomFields, overwriting existing data. The size of mockLoggedAtomFields will equal
+ // the number of calls to stats_write for that stroke.
+ void setMockLoggedAtomFields(std::vector<AtomFields>* mockLoggedAtomFields) {
+ mMockLoggedAtomFields = mockLoggedAtomFields;
+ }
+
+private:
+ // The interval between consecutive predictions' target timestamps. We assume that the input
+ // interval also equals this value.
+ const nsecs_t mPredictionInterval;
+
+ // The maximum number of input frames into the future the model can predict.
+ // Used to perform time-bucketing of metrics.
+ const size_t mMaxNumPredictions;
+
+ // History of mMaxNumPredictions + 1 ground truth points, used to compute scale-invariant
+ // error. (Also, the last two points are used to compute the ground truth trajectory.)
+ RingBuffer<GroundTruthPoint> mRecentGroundTruthPoints;
+
+ // Predictions having a targetTimestamp after the most recent ground truth point's timestamp.
+ // Invariant: sorted in ascending order of targetTimestamp.
+ std::vector<PredictionPoint> mRecentPredictions;
+
+ // Containers for the intermediate representation of stroke metrics and the final atom fields.
+ // These are indexed by the number of input frames into the future being predicted minus one,
+ // and always have size mMaxNumPredictions.
+ std::vector<AggregatedStrokeMetrics> mAggregatedMetrics;
+ std::vector<AtomFields> mAtomFields;
+
+ // Non-owning pointer to the location of mock AtomFields. If present, will be filled with the
+ // values reported to stats_write on each batch of reported metrics.
+ //
+ // This pointer must remain valid as long as the MotionPredictorMetricsManager exists.
+ std::vector<AtomFields>* mMockLoggedAtomFields = nullptr;
+
+ // Helper methods for the implementation of onRecord and onPredict.
+
+ // Clears stored ground truth and prediction points, as well as all stored metrics for the
+ // current stroke.
+ void clearStrokeData();
+
+ // Adds the new ground truth point to mRecentGroundTruths, removes outdated predictions from
+ // mRecentPredictions, and updates the aggregated metrics to include the recent predictions that
+ // fuzzily match with the new ground truth point.
+ void incorporateNewGroundTruth(const GroundTruthPoint& groundTruthPoint);
+
+ // Given a new prediction with targetTimestamp matching the latest ground truth point's
+ // timestamp, computes the corresponding metrics and updates mAggregatedMetrics.
+ void updateAggregatedMetrics(const PredictionPoint& predictionPoint);
+
+ // Computes the atom fields to mAtomFields from the values in mAggregatedMetrics.
+ void computeAtomFields();
+
+ // Reports the metrics given by the current data in mAtomFields:
+ // • If on an Android device, reports the metrics to stats_write.
+ // • If mMockLoggedAtomFields is present, it will be overwritten with logged metrics, with one
+ // AtomFields element per call to stats_write.
+ void reportMetrics();
};
} // namespace android
diff --git a/include/input/TraceTools.h b/include/input/TraceTools.h
new file mode 100644
index 0000000..70b23c5
--- /dev/null
+++ b/include/input/TraceTools.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 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 <utils/Trace.h>
+#include <optional>
+
+#define ATRACE_NAME_IF(condition, messageProvider) \
+ const auto _trace_token = condition \
+ ? std::make_optional<android::ScopedTrace>(ATRACE_TAG, messageProvider().c_str()) \
+ : std::nullopt
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index b58feac..2e99495 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/os/IInputConstants.h>
#include <input/Input.h>
#include <input/RingBuffer.h>
#include <utils/BitSet.h>
@@ -35,19 +36,20 @@
static const size_t MAX_DEGREE = 4;
enum class Strategy : int32_t {
- DEFAULT = -1,
- MIN = 0,
- IMPULSE = 0,
- LSQ1 = 1,
- LSQ2 = 2,
- LSQ3 = 3,
- WLSQ2_DELTA = 4,
- WLSQ2_CENTRAL = 5,
- WLSQ2_RECENT = 6,
- INT1 = 7,
- INT2 = 8,
- LEGACY = 9,
+ DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT,
+ IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE,
+ LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1,
+ LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2,
+ LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3,
+ WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA,
+ WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL,
+ WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT,
+ INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1,
+ INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2,
+ LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY,
+ MIN = IMPULSE,
MAX = LEGACY,
+ ftl_last = LEGACY,
};
/*
@@ -81,8 +83,6 @@
// TODO(b/32830165): support axis-specific strategies.
VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
- ~VelocityTracker();
-
/** Return true if the axis is supported for velocity tracking, false otherwise. */
static bool isAxisSupported(int32_t axis);
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 8d9955d..589df9a 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -261,7 +261,7 @@
bool BpBinder::isDescriptorCached() const {
Mutex::Autolock _l(mLock);
- return mDescriptorCache.string() != kDescriptorUninit.string();
+ return mDescriptorCache.c_str() != kDescriptorUninit.c_str();
}
const String16& BpBinder::getInterfaceDescriptor() const
@@ -279,7 +279,7 @@
Mutex::Autolock _l(mLock);
// mDescriptorCache could have been assigned while the lock was
// released.
- if (mDescriptorCache.string() == kDescriptorUninit.string()) mDescriptorCache = res;
+ if (mDescriptorCache.c_str() == kDescriptorUninit.c_str()) mDescriptorCache = res;
}
}
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index f2b4a6e..152c815 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -52,8 +52,8 @@
}
} else {
// An exception was thrown back; fall through to return failure
- ALOGD("openContentUri(%s) caught exception %d\n",
- String8(stringUri).string(), exceptionCode);
+ ALOGD("openContentUri(%s) caught exception %d\n", String8(stringUri).c_str(),
+ exceptionCode);
}
}
return fd;
@@ -193,8 +193,7 @@
status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
@@ -209,8 +208,7 @@
status_t err =
remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
@@ -224,11 +222,10 @@
data.writeInt32(state);
data.writeInt32(appUid);
data.writeInt32(appPid);
- status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply,
+ status_t err = remote()->transact(LOG_FGS_API_STATE_CHANGED_TRANSACTION, data, &reply,
IBinder::FLAG_ONEWAY);
if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
- ALOGD("FGS Logger Transaction failed");
- ALOGD("%d", err);
+ ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err);
return err;
}
return NO_ERROR;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 2408307..6034f2b 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -216,8 +216,8 @@
if (res) {
if (startTime != 0) {
ALOGI("Check passed after %d seconds for %s from uid=%d pid=%d",
- (int)((uptimeMillis()-startTime)/1000),
- String8(permission).string(), uid, pid);
+ (int)((uptimeMillis() - startTime) / 1000), String8(permission).c_str(),
+ uid, pid);
}
return res;
}
@@ -225,7 +225,7 @@
// Is this a permission failure, or did the controller go away?
if (IInterface::asBinder(pc)->isBinderAlive()) {
if (logPermissionFailure) {
- ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).string(),
+ ALOGW("Permission failure: %s from uid=%d pid=%d", String8(permission).c_str(),
uid, pid);
}
return false;
@@ -246,7 +246,7 @@
if (startTime == 0) {
startTime = uptimeMillis();
ALOGI("Waiting to check permission %s from uid=%d pid=%d",
- String8(permission).string(), uid, pid);
+ String8(permission).c_str(), uid, pid);
}
sleep(1);
} else {
@@ -295,7 +295,7 @@
// retry interval in millisecond; note that vendor services stay at 100ms
const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100;
- ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(),
+ ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(),
ProcessState::self()->getDriverName().c_str());
int n = 0;
@@ -306,12 +306,12 @@
sp<IBinder> svc = checkService(name);
if (svc != nullptr) {
ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms",
- String8(name).string(), ProcessState::self()->getDriverName().c_str(),
+ String8(name).c_str(), ProcessState::self()->getDriverName().c_str(),
uptimeMillis() - startTime);
return svc;
}
}
- ALOGW("Service %s didn't start. Returning NULL", String8(name).string());
+ ALOGW("Service %s didn't start. Returning NULL", String8(name).c_str());
return nullptr;
}
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
index 03553f3..5b1cb7e 100644
--- a/libs/binder/MemoryDealer.cpp
+++ b/libs/binder/MemoryDealer.cpp
@@ -428,7 +428,7 @@
{
String8 result;
dump_l(result, what);
- ALOGD("%s", result.string());
+ ALOGD("%s", result.c_str());
}
void SimpleBestFitAllocator::dump(String8& result,
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index 3da06ba..fc273e0 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -73,8 +73,8 @@
ALOGV("MemoryHeapBase: Attempting to force MemFD");
fd = memfd_create_region(name ? name : "MemoryHeapBase", size);
if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return;
- const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
- ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
+ const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | F_SEAL_GROW |
+ F_SEAL_SHRINK | ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) {
ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name,
SEAL_FLAGS, strerror(errno));
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index bbaa419..817e0fc 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -895,7 +895,7 @@
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
- return writeInterfaceToken(interface.string(), interface.size());
+ return writeInterfaceToken(interface.c_str(), interface.size());
}
status_t Parcel::writeInterfaceToken(const char16_t* str, size_t len) {
@@ -959,7 +959,7 @@
bool Parcel::enforceInterface(const String16& interface,
IPCThreadState* threadState) const
{
- return enforceInterface(interface.string(), interface.size(), threadState);
+ return enforceInterface(interface.c_str(), interface.size(), threadState);
}
bool Parcel::enforceInterface(const char16_t* interface,
@@ -1018,8 +1018,8 @@
return true;
} else {
ALOGW("**** enforceInterface() expected '%s' but read '%s'",
- String8(interface, len).string(),
- String8(parcel_interface, parcel_interface_len).string());
+ String8(interface, len).c_str(),
+ String8(parcel_interface, parcel_interface_len).c_str());
return false;
}
}
@@ -1417,7 +1417,7 @@
status_t Parcel::writeString8(const String8& str)
{
- return writeString8(str.string(), str.size());
+ return writeString8(str.c_str(), str.size());
}
status_t Parcel::writeString8(const char* str, size_t len)
@@ -1440,7 +1440,7 @@
status_t Parcel::writeString16(const String16& str)
{
- return writeString16(str.string(), str.size());
+ return writeString16(str.c_str(), str.size());
}
status_t Parcel::writeString16(const char16_t* str, size_t len)
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp
index 670fd55..658686d 100644
--- a/libs/binder/PermissionCache.cpp
+++ b/libs/binder/PermissionCache.cpp
@@ -101,9 +101,8 @@
nsecs_t t = -systemTime();
granted = android::checkPermission(permission, pid, uid);
t += systemTime();
- ALOGD("checking %s for uid=%d => %s (%d us)",
- String8(permission).string(), uid,
- granted?"granted":"denied", (int)ns2us(t));
+ ALOGD("checking %s for uid=%d => %s (%d us)", String8(permission).c_str(), uid,
+ granted ? "granted" : "denied", (int)ns2us(t));
pc.cache(permission, uid, granted);
}
return granted;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 02b0447..8ec4af9 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -401,9 +401,9 @@
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
- ALOGV("Spawning new pooled thread, name=%s\n", name.string());
+ ALOGV("Spawning new pooled thread, name=%s\n", name.c_str());
sp<Thread> t = sp<PoolThread>::make(isMain);
- t->run(name.string());
+ t->run(name.c_str());
pthread_mutex_lock(&mThreadCountLock);
mKernelStartedThreads++;
pthread_mutex_unlock(&mThreadCountLock);
@@ -505,7 +505,7 @@
}
void ProcessState::giveThreadPoolName() {
- androidSetThreadName( makeBinderThreadName().string() );
+ androidSetThreadName(makeBinderThreadName().c_str());
}
String8 ProcessState::getDriverName() {
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 44a9e3b..3246706 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -124,7 +124,7 @@
static_cast<int32_t>(timestamp.tv_nsec),
0};
- t.mData.mInterfaceName = std::string(String8(interfaceName).string());
+ t.mData.mInterfaceName = std::string(String8(interfaceName).c_str());
if (interfaceName.size() != t.mData.mInterfaceName.size()) {
LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
"utf-8.";
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h
index eb98042..50158c3 100644
--- a/libs/binder/include/binder/TextOutput.h
+++ b/libs/binder/include/binder/TextOutput.h
@@ -147,7 +147,7 @@
inline TextOutput& operator<<(TextOutput& to, const String16& val)
{
- to << String8(val).string();
+ to << String8(val).c_str();
return to;
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 672d6cf..57a38dc 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -11,9 +11,6 @@
name: "libbinder_rs",
crate_name: "binder",
srcs: ["src/lib.rs"],
- shared_libs: [
- "libutils",
- ],
rustlibs: [
"libbinder_ndk_sys",
"libdowncast_rs",
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index 1d1a295..c5c847b 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -19,7 +19,20 @@
use std::error::Error;
use std::fmt;
-include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+#[cfg(not(target_os = "trusty"))]
+mod bindings {
+ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
+
+// Trusty puts the full path to the auto-generated file in BINDGEN_INC_FILE
+// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)]
+#[cfg(target_os = "trusty")]
+#[allow(bad_style)]
+mod bindings {
+ include!(env!("BINDGEN_INC_FILE"));
+}
+
+pub use bindings::*;
impl Error for android_c_interface_StatusCode {}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index ac96823..6eb707b 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -3,19 +3,12 @@
default_applicable_licenses: ["frameworks_native_license"],
}
-rust_fuzz {
- name: "parcel_fuzzer_rs",
- srcs: [
- "parcel_fuzzer.rs",
- ],
+rust_defaults {
+ name: "service_fuzzer_defaults_rs",
rustlibs: [
- "libarbitrary",
- "libnum_traits",
"libbinder_rs",
"libbinder_random_parcel_rs",
- "binderReadParcelIface-rust",
],
-
fuzz_config: {
cc: [
"waghpawan@google.com",
@@ -26,3 +19,18 @@
hotlists: ["4637097"],
},
}
+
+rust_fuzz {
+ name: "parcel_fuzzer_rs",
+ srcs: [
+ "parcel_fuzzer.rs",
+ ],
+ defaults: [
+ "service_fuzzer_defaults_rs",
+ ],
+ rustlibs: [
+ "libarbitrary",
+ "libnum_traits",
+ "binderReadParcelIface-rust",
+ ],
+}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 2537ce0..84130c1 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -19,18 +19,10 @@
srcs: [
"service_fuzzer.rs",
],
+ defaults: [
+ "service_fuzzer_defaults_rs",
+ ],
rustlibs: [
- "libbinder_rs",
- "libbinder_random_parcel_rs",
"testServiceInterface-rust",
],
- fuzz_config: {
- cc: [
- "waghpawan@google.com",
- "smoreland@google.com",
- ],
- triage_assignee: "waghpawan@google.com",
- // hotlist "AIDL fuzzers bugs" on buganizer
- hotlists: ["4637097"],
- },
}
diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
index 278dd2b..140270f 100644
--- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
+++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp
@@ -37,7 +37,8 @@
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdUnsealed) {
@@ -48,7 +49,8 @@
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdSealedProtected) {
@@ -59,7 +61,9 @@
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS),
+ F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
TEST(MemoryHeapBase, MemfdUnsealedProtected) {
@@ -71,7 +75,8 @@
ASSERT_NE(mHeap.get(), nullptr);
int fd = mHeap->getHeapID();
EXPECT_NE(fd, -1);
- EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_FUTURE_WRITE);
+ EXPECT_EQ(ftruncate(fd, 4096), -1);
}
#else
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 47d2a0a..93ac116 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -60,8 +60,15 @@
while (provider.remaining_bytes() > 0) {
// Most of the AIDL services will have small set of transaction codes.
- uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
- : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
+ // TODO(b/295942369) : Add remaining transact codes from IBinder.h
+ uint32_t code = provider.ConsumeBool()
+ ? provider.ConsumeIntegral<uint32_t>()
+ : provider.PickValueInArray<int64_t>(
+ {provider.ConsumeIntegralInRange<uint32_t>(0, 100),
+ IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION,
+ IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION,
+ IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION,
+ IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION});
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
index 96092b1..690c39a 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp
@@ -36,8 +36,8 @@
triage_assignee: "waghpawan@google.com",
// This fuzzer should be used only test fuzzService locally
- fuzz_on_haiku_host: true,
- fuzz_on_haiku_device: true,
+ fuzz_on_haiku_host: false,
+ fuzz_on_haiku_device: false,
},
}
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
index 46205d7..d2fa581 100644
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp
@@ -33,6 +33,9 @@
ON_KNOWN_UID,
ON_SYSTEM_AID,
ON_ROOT_AID,
+ ON_DUMP_TRANSACT,
+ ON_SHELL_CMD_TRANSACT,
+ CRASH_ALWAYS,
};
// This service is to verify that fuzzService is functioning properly
@@ -92,6 +95,16 @@
return Status::ok();
}
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override {
+ if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) {
+ LOG_ALWAYS_FATAL("Expected crash, DUMP.");
+ } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT &&
+ code == SHELL_COMMAND_TRANSACTION) {
+ LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD.");
+ }
+ return BnTestService::onTransact(code, data, reply, flags);
+ }
+
private:
CrashType mCrash;
};
@@ -100,8 +113,10 @@
extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) {
if (*argc < 2) {
- printf("You must specify at least one argument\n");
- exit(0); // success because this is a crash test
+ // This fuzzer is also used as test fuzzer to check infra pipeline.
+ // It should always run and find a crash in TestService.
+ gCrashType = CrashType::CRASH_ALWAYS;
+ return 0;
}
std::string arg = std::string((*argv)[1]);
@@ -121,6 +136,10 @@
gCrashType = CrashType::ON_ROOT_AID;
} else if (arg == "BINDER") {
gCrashType = CrashType::ON_BINDER;
+ } else if (arg == "DUMP") {
+ gCrashType = CrashType::ON_DUMP_TRANSACT;
+ } else if (arg == "SHELL_CMD") {
+ gCrashType = CrashType::ON_SHELL_CMD_TRANSACT;
} else {
printf("INVALID ARG\n");
exit(0); // success because this is a crash test
@@ -130,6 +149,9 @@
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (gCrashType == CrashType::CRASH_ALWAYS) {
+ LOG_ALWAYS_FATAL("Expected crash, This fuzzer will always crash.");
+ }
auto service = sp<TestService>::make(gCrashType);
fuzzService(service, FuzzedDataProvider(data, size));
return 0;
diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
index 25906d8..c447bff 100755
--- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
+++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh
@@ -27,7 +27,7 @@
exit 1
fi
-for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do
+for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do
echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE"
./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT"
diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
new file mode 100644
index 0000000..672d9b7
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
@@ -0,0 +1,38 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+LIBBINDER_NDK_BINDGEN_FLAG_FILE := \
+ $(LIBBINDER_DIR)/rust/libbinder_ndk_bindgen_flags.txt
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LIBBINDER_DIR)/rust/sys/lib.rs
+
+MODULE_CRATE_NAME := binder_ndk_sys
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ trusty/user/base/lib/trusty-sys \
+
+MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp
+
+# Add the flags from the flag file
+MODULE_BINDGEN_FLAGS += $(shell cat $(LIBBINDER_NDK_BINDGEN_FLAG_FILE))
+MODULE_SRCDEPS += $(LIBBINDER_NDK_BINDGEN_FLAG_FILE)
+
+include make/library.mk
diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp
index e1dc9ba..365fc45 100644
--- a/libs/bufferstreams/Android.bp
+++ b/libs/bufferstreams/Android.bp
@@ -15,3 +15,22 @@
package {
default_applicable_licenses: ["frameworks_native_license"],
}
+
+aconfig_declarations {
+ name: "bufferstreams_flags",
+ package: "com.android.graphics.bufferstreams.flags",
+ srcs: [
+ "aconfig/bufferstreams_flags.aconfig",
+ ],
+}
+
+rust_aconfig_library {
+ name: "libbufferstreams_flags_rust",
+ crate_name: "bufferstreams_flags",
+ aconfig_declarations: "bufferstreams_flags",
+}
+
+cc_aconfig_library {
+ name: "libbufferstreams_flags_cc",
+ aconfig_declarations: "bufferstreams_flags",
+}
diff --git a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig
new file mode 100644
index 0000000..e258725
--- /dev/null
+++ b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig
@@ -0,0 +1,65 @@
+package: "com.android.graphics.bufferstreams.flags"
+
+flag {
+ name: "bufferstreams_steel_thread"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams steel thread milestone"
+ bug: "296101122"
+}
+
+flag {
+ name: "bufferstreams_local"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams single-process functionality milestone"
+ bug: "296100790"
+}
+
+flag {
+ name: "bufferstreams_pooling"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams buffer pooling milestone"
+ bug: "296101127"
+}
+
+flag {
+ name: "bufferstreams_ipc"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams IPC milestone"
+ bug: "296099728"
+}
+
+flag {
+ name: "bufferstreams_cpp"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams C/C++ milestone"
+ bug: "296100536"
+}
+
+flag {
+ name: "bufferstreams_utils"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams extra utilities milestone"
+ bug: "285322189"
+}
+
+flag {
+ name: "bufferstreams_demo"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams demo milestone"
+ bug: "297242965"
+}
+
+flag {
+ name: "bufferstreams_perf"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams performance enhancement milestone"
+ bug: "297242843"
+}
+
+flag {
+ name: "bufferstreams_tooling"
+ namespace: "core_graphics"
+ description: "Flag for bufferstreams tooling milestone"
+ bug: "297243180"
+}
+
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 96dcce1..3823393 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -17,6 +17,7 @@
shared_libs: [
"libbinder",
"libutils",
+ "liblog",
],
target: {
darwin: {
@@ -40,3 +41,41 @@
static_libs: ["libgmock"],
local_include_dirs: ["include"],
}
+
+rust_bindgen {
+ name: "libfakeservicemanager_bindgen",
+ crate_name: "fakeservicemanager_bindgen",
+ host_supported: true,
+ wrapper_src: "rust/wrappers/FakeServiceManagerWrapper.hpp",
+ source_stem: "bindings",
+ visibility: [":__subpackages__"],
+ bindgen_flags: [
+ "--allowlist-function",
+ "setupFakeServiceManager",
+ "--allowlist-function",
+ "clearFakeServiceManager",
+ ],
+ shared_libs: [
+ "libc++",
+ "libbinder",
+ "libfakeservicemanager",
+ ],
+}
+
+rust_library {
+ name: "libfakeservicemanager_rs",
+ crate_name: "fakeservicemanager_rs",
+ host_supported: true,
+ srcs: [
+ "rust/src/lib.rs",
+ ],
+ shared_libs: [
+ "libc++",
+ "libfakeservicemanager",
+ ],
+ rustlibs: [
+ "libfakeservicemanager_bindgen",
+ ],
+ lints: "none",
+ clippy_lints: "none",
+}
diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp
index 80661c1..ae242f3 100644
--- a/libs/fakeservicemanager/FakeServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -16,6 +16,10 @@
#include "fakeservicemanager/FakeServiceManager.h"
+using android::sp;
+using android::FakeServiceManager;
+using android::setDefaultServiceManager;
+
namespace android {
FakeServiceManager::FakeServiceManager() {}
@@ -80,7 +84,7 @@
for (const auto& [registeredName, service] : mNameToService) {
(void) service;
if (registeredName.startsWith(prefix)) {
- out.add(String16(registeredName.string() + prefix.size()));
+ out.add(String16(registeredName.c_str() + prefix.size()));
}
}
return out;
@@ -123,3 +127,24 @@
mNameToService.clear();
}
} // namespace android
+
+[[clang::no_destroy]] static sp<FakeServiceManager> gFakeServiceManager;
+[[clang::no_destroy]] static std::once_flag gSmOnce;
+
+extern "C" {
+
+// Setup FakeServiceManager to mock dependencies in test using this API for rust backend
+void setupFakeServiceManager() {
+ /* Create a FakeServiceManager instance and add required services */
+ std::call_once(gSmOnce, [&]() {
+ gFakeServiceManager = new FakeServiceManager();
+ android::setDefaultServiceManager(gFakeServiceManager);
+ });
+}
+
+// Clear existing services from Fake SM for rust backend
+void clearFakeServiceManager() {
+ LOG_ALWAYS_FATAL_IF(gFakeServiceManager == nullptr, "Fake Service Manager is not available. Forgot to call setupFakeServiceManager?");
+ gFakeServiceManager->clear();
+}
+} //extern "C"
\ No newline at end of file
diff --git a/libs/fakeservicemanager/rust/src/lib.rs b/libs/fakeservicemanager/rust/src/lib.rs
new file mode 100644
index 0000000..5b7e756
--- /dev/null
+++ b/libs/fakeservicemanager/rust/src/lib.rs
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+use fakeservicemanager_bindgen::{clearFakeServiceManager, setupFakeServiceManager};
+// Setup FakeServiceManager for testing and fuzzing purposes
+pub fn setup_fake_service_manager() {
+ unsafe {
+ // Safety: This API creates a new FakeSm object which will be always valid and sets up
+ // defaultServiceManager
+ setupFakeServiceManager();
+ }
+}
+
+// Setup FakeServiceManager for testing and fuzzing purposes
+pub fn clear_fake_service_manager() {
+ unsafe {
+ // Safety: This API clears all registered services with Fake SM. This should be only used
+ // setupFakeServiceManager is already called.
+ clearFakeServiceManager();
+ }
+}
diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp
similarity index 62%
copy from libs/renderengine/include/renderengine/Image.h
copy to libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp
index 3bb4731..1f5923a 100644
--- a/libs/renderengine/include/renderengine/Image.h
+++ b/libs/fakeservicemanager/rust/wrappers/FakeServiceManagerWrapper.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,18 +14,12 @@
* limitations under the License.
*/
-#pragma once
+#include "fakeservicemanager/FakeServiceManager.h"
-struct ANativeWindowBuffer;
+extern "C" {
+ // Setup FakeServiceManager to mock dependencies in test using this API
+ void setupFakeServiceManager();
-namespace android {
-namespace renderengine {
-
-class Image {
-public:
- virtual ~Image() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
-};
-
-} // namespace renderengine
-} // namespace android
+ // Clear existing services from Fake SM.
+ void clearFakeServiceManager();
+} // extern "C"
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
index 6fc21c6..cb6784c0 100644
--- a/libs/fakeservicemanager/test_sm.cpp
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -22,6 +22,7 @@
#include <binder/IServiceManager.h>
#include "fakeservicemanager/FakeServiceManager.h"
+#include "rust/wrappers/FakeServiceManagerWrapper.hpp"
using android::sp;
using android::BBinder;
@@ -31,6 +32,7 @@
using android::FakeServiceManager;
using android::String16;
using android::IServiceManager;
+using android::defaultServiceManager;
using testing::ElementsAre;
static sp<IBinder> getBinder() {
@@ -83,7 +85,7 @@
EXPECT_EQ(sm->getService(String16("foo")), service);
}
-TEST(GetService, NonExistant) {
+TEST(GetService, NonExistent) {
auto sm = new FakeServiceManager();
EXPECT_EQ(sm->getService(String16("foo")), nullptr);
@@ -108,7 +110,7 @@
String16("sd")));
}
-TEST(WaitForService, NonExistant) {
+TEST(WaitForService, NonExistent) {
auto sm = new FakeServiceManager();
EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
@@ -124,7 +126,7 @@
EXPECT_EQ(sm->waitForService(String16("foo")), service);
}
-TEST(IsDeclared, NonExistant) {
+TEST(IsDeclared, NonExistent) {
auto sm = new FakeServiceManager();
EXPECT_FALSE(sm->isDeclared(String16("foo")));
@@ -139,3 +141,31 @@
EXPECT_TRUE(sm->isDeclared(String16("foo")));
}
+
+TEST(SetupFakeServiceManager, NonExistent) {
+ setupFakeServiceManager();
+
+ EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), nullptr);
+}
+
+TEST(SetupFakeServiceManager, GetExistingService) {
+ setupFakeServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(defaultServiceManager()->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), service);
+ clearFakeServiceManager();
+}
+
+TEST(ClearFakeServiceManager, GetServiceAfterClear) {
+ setupFakeServiceManager();
+
+ sp<IBinder> service = getBinder();
+ EXPECT_EQ(defaultServiceManager()->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ clearFakeServiceManager();
+ EXPECT_EQ(defaultServiceManager()->getService(String16("foo")), nullptr);
+}
\ No newline at end of file
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 732ca36..4842476 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -60,6 +60,17 @@
typedef bool (*fpANGLEFreeRulesHandle)(void* handle);
typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle);
+namespace {
+static bool isVndkEnabled() {
+#ifdef __BIONIC__
+ // TODO(b/290159430) Use ro.vndk.version to check if VNDK is enabled instead
+ static bool isVndkEnabled = !android::base::GetBoolProperty("ro.vndk.deprecate", false);
+ return isVndkEnabled;
+#endif
+ return false;
+}
+} // namespace
+
namespace android {
enum NativeLibrary {
@@ -71,6 +82,8 @@
{"/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt",
"/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt"};
+static const char* kLlndkLibrariesTxtPath = "/system/etc/llndk.libraries.txt";
+
static std::string vndkVersionStr() {
#ifdef __BIONIC__
return base::GetProperty("ro.vndk.version", "");
@@ -108,8 +121,14 @@
}
static const std::string getSystemNativeLibraries(NativeLibrary type) {
- std::string nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type];
- insertVndkVersionStr(&nativeLibrariesSystemConfig);
+ std::string nativeLibrariesSystemConfig = "";
+
+ if (!isVndkEnabled() && type == NativeLibrary::LLNDK) {
+ nativeLibrariesSystemConfig = kLlndkLibrariesTxtPath;
+ } else {
+ nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type];
+ insertVndkVersionStr(&nativeLibrariesSystemConfig);
+ }
std::vector<std::string> soNames;
if (!readConfig(nativeLibrariesSystemConfig, &soNames)) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index d7e7eb8..298838d 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -62,6 +62,7 @@
name: "guiconstants_aidl",
srcs: [
"android/gui/DropInputMode.aidl",
+ "android/gui/StalledTransactionInfo.aidl",
"android/**/TouchOcclusionMode.aidl",
],
}
@@ -140,6 +141,7 @@
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosPublisher.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
+ "android/gui/StalledTransactionInfo.aidl",
"android/gui/WindowInfo.aidl",
"android/gui/WindowInfosUpdate.aidl",
],
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index f50bc20..e6331e7 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -24,11 +24,11 @@
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 5217209..5b34ba1 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -47,23 +47,23 @@
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
@@ -298,8 +298,7 @@
// decrease.
mCore->mDequeueCondition.notify_all();
- ATRACE_INT(mCore->mConsumerName.string(),
- static_cast<int32_t>(mCore->mQueue.size()));
+ ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
@@ -718,7 +717,7 @@
status_t BufferQueueConsumer::setConsumerName(const String8& name) {
ATRACE_CALL();
- BQ_LOGV("setConsumerName: '%s'", name.string());
+ BQ_LOGV("setConsumerName: '%s'", name.c_str());
std::lock_guard<std::mutex> lock(mCore->mMutex);
mCore->mConsumerName = name;
mConsumerName = name;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 2930154..648db67 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -41,20 +41,20 @@
namespace android {
// Macros for include BufferQueueCore information in log messages
-#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \
mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
static String8 getUniqueName() {
@@ -146,23 +146,23 @@
void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
std::lock_guard<std::mutex> lock(mMutex);
- outResult->appendFormat("%s- BufferQueue ", prefix.string());
+ outResult->appendFormat("%s- BufferQueue ", prefix.c_str());
outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n",
mMaxAcquiredBufferCount, mMaxDequeuedBufferCount);
- outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(),
+ outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.c_str(),
mDequeueBufferCannotBlock, mAsyncMode);
- outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(),
+ outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.c_str(),
mQueueBufferCanDrop, mLegacyBufferDrop);
- outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(),
+ outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.c_str(),
mDefaultWidth, mDefaultHeight, mDefaultBufferFormat);
- outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(),
+ outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.c_str(),
mTransformHint, mFrameCounter);
- outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(),
+ outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.c_str(),
mTransformHintInUse, mAutoPrerotation);
- outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+ outResult->appendFormat("%sFIFO(%zu):\n", prefix.c_str(), mQueue.size());
- outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+ outResult->appendFormat("%s(mConsumerName=%s, ", prefix.c_str(), mConsumerName.c_str());
outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
mConsumerUsageBits);
@@ -173,12 +173,11 @@
getProcessName(mConnectedPid, producerProcName);
getProcessName(pid, consumerProcName);
outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
- mConnectedPid, producerProcName.string(), pid,
- consumerProcName.string());
+ mConnectedPid, producerProcName.c_str(), pid, consumerProcName.c_str());
Fifo::const_iterator current(mQueue.begin());
while (current != mQueue.end()) {
double timestamp = current->mTimestamp / 1e9;
- outResult->appendFormat("%s %02d:%p ", prefix.string(), current->mSlot,
+ outResult->appendFormat("%s %02d:%p ", prefix.c_str(), current->mSlot,
current->mGraphicBuffer.get());
outResult->appendFormat("crop=[%d,%d,%d,%d] ", current->mCrop.left, current->mCrop.top,
current->mCrop.right, current->mCrop.bottom);
@@ -187,12 +186,12 @@
++current;
}
- outResult->appendFormat("%sSlots:\n", prefix.string());
+ outResult->appendFormat("%sSlots:\n", prefix.c_str());
for (int s : mActiveBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
// A dequeued buffer might be null if it's still being allocated
if (buffer.get()) {
- outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(),
+ outResult->appendFormat("%s %s[%02d:%p] ", prefix.c_str(),
(mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
buffer.get());
outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(),
@@ -200,14 +199,14 @@
outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height,
buffer->stride, buffer->format);
} else {
- outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get());
+ outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get());
outResult->appendFormat("state=%-8s frame=%" PRIu64 "\n",
mSlots[s].mBufferState.string(), mSlots[s].mFrameNumber);
}
}
for (int s : mFreeBuffers) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get());
+ outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get());
outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(),
buffer->handle, mSlots[s].mFrameNumber);
outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height,
@@ -216,7 +215,7 @@
for (int s : mFreeSlots) {
const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);
- outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(),
+ outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.c_str(), s, buffer.get(),
mSlots[s].mBufferState.string());
}
}
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index b872541..10f5899 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -47,23 +47,23 @@
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
- ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGD(x, ...) \
- ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGI(x, ...) \
- ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGW(x, ...) \
- ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
#define BQ_LOGE(x, ...) \
- ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \
mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
##__VA_ARGS__)
@@ -573,9 +573,9 @@
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
- width, height, format, BQ_LAYER_COUNT, usage,
- {mConsumerName.string(), mConsumerName.size()});
+ sp<GraphicBuffer> graphicBuffer =
+ new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
+ {mConsumerName.c_str(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
@@ -1046,8 +1046,7 @@
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
- ATRACE_INT(mCore->mConsumerName.string(),
- static_cast<int32_t>(mCore->mQueue.size()));
+ ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
@@ -1487,7 +1486,7 @@
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
allocUsage = usage | mCore->mConsumerUsageBits;
- allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size());
+ allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size());
mCore->mIsAllocating = true;
} // Autolock scope
@@ -1589,7 +1588,7 @@
String8 BufferQueueProducer::getConsumerName() const {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mCore->mMutex);
- BQ_LOGV("getConsumerName: %s", mConsumerName.string());
+ BQ_LOGV("getConsumerName: %s", mConsumerName.c_str());
return mConsumerName;
}
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 9f91d9d..b625c3f 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -41,11 +41,11 @@
#include <utils/Trace.h>
// Macros for including the ConsumerBase name in log messages
-#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
@@ -86,8 +86,10 @@
// be done by ConsumerBase::onLastStrongRef(), but it's possible for a
// derived class to override that method and not call
// ConsumerBase::onLastStrongRef().
- LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the "
- "consumer is not abandoned!", mName.string());
+ LOG_ALWAYS_FATAL_IF(!mAbandoned,
+ "[%s] ~ConsumerBase was called, but the "
+ "consumer is not abandoned!",
+ mName.c_str());
}
void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) {
@@ -451,7 +453,7 @@
// them to get an accurate timestamp.
if (currentStatus == incomingStatus) {
char fenceName[32] = {};
- snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot);
+ snprintf(fenceName, 32, "%.28s:%d", mName.c_str(), slot);
sp<Fence> mergedFence = Fence::merge(
fenceName, mSlots[slot].mFence, fence);
if (!mergedFence.get()) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index a626970..3031fa1 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -23,11 +23,11 @@
#include <gui/BufferItem.h>
#include <utils/Log.h>
-#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index b3647d6..d49489c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -52,11 +52,11 @@
namespace android {
// Macros for including the GLConsumer name in log messages
-#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-//#define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+// #define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
static const struct {
uint32_t width, height;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 2322b70..e1afb52 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -83,6 +83,7 @@
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
defaultFrameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+ frameRateCategory(ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
@@ -158,6 +159,7 @@
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeByte, defaultFrameRateCompatibility);
+ SAFE_PARCEL(output.writeByte, frameRateCategory);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeBool, autoRefresh);
SAFE_PARCEL(output.writeBool, dimmingEnabled);
@@ -290,6 +292,7 @@
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readByte, &defaultFrameRateCompatibility);
+ SAFE_PARCEL(input.readByte, &frameRateCategory);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readBool, &autoRefresh);
@@ -659,6 +662,10 @@
frameRateCompatibility = other.frameRateCompatibility;
changeFrameRateStrategy = other.changeFrameRateStrategy;
}
+ if (other.what & eFrameRateCategoryChanged) {
+ what |= eFrameRateCategoryChanged;
+ frameRateCategory = other.frameRateCategory;
+ }
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
@@ -769,6 +776,7 @@
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
changeFrameRateStrategy);
+ CHECK_DIFF(diff, eFrameRateCategoryChanged, other, frameRateCategory);
CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6bc3324..92589c5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -59,7 +59,7 @@
#include <private/gui/ComposerServiceAIDL.h>
// This server size should always be smaller than the server cache size
-#define BUFFER_CACHE_MAX_SIZE 64
+#define BUFFER_CACHE_MAX_SIZE 4096
namespace android {
@@ -1275,7 +1275,7 @@
sp<IBinder> display = nullptr;
binder::Status status =
ComposerServiceAIDL::getComposerService()->createDisplay(std::string(
- displayName.string()),
+ displayName.c_str()),
secure, requestedRefereshRate,
&display);
return status.isOk() ? display : nullptr;
@@ -1308,6 +1308,13 @@
return status.isOk() ? display : nullptr;
}
+std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTransactionInfo(
+ pid_t pid) {
+ std::optional<gui::StalledTransactionInfo> result;
+ ComposerServiceAIDL::getComposerService()->getStalledTransactionInfo(pid, &result);
+ return result;
+}
+
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
mAnimation = true;
}
@@ -2085,6 +2092,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRateCategory(
+ const sp<SurfaceControl>& sc, int8_t category) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eFrameRateCategoryChanged;
+ s->frameRateCategory = category;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
layer_state_t* s = getLayerState(sc);
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 52af9d5..2eb6bd6 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -43,7 +43,7 @@
}
bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frameLeft && x < frameRight && y >= frameTop && y < frameBottom;
+ return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom;
}
bool WindowInfo::supportsSplitTouch() const {
@@ -59,18 +59,15 @@
}
bool WindowInfo::overlaps(const WindowInfo* other) const {
- const bool nonEmpty = (frameRight - frameLeft > 0) || (frameBottom - frameTop > 0);
- return nonEmpty && frameLeft < other->frameRight && frameRight > other->frameLeft &&
- frameTop < other->frameBottom && frameBottom > other->frameTop;
+ return !frame.isEmpty() && frame.left < other->frame.right && frame.right > other->frame.left &&
+ frame.top < other->frame.bottom && frame.bottom > other->frame.top;
}
bool WindowInfo::operator==(const WindowInfo& info) const {
return info.token == token && info.id == id && info.name == name &&
- info.dispatchingTimeout == dispatchingTimeout && info.frameLeft == frameLeft &&
- info.frameTop == frameTop && info.frameRight == frameRight &&
- info.frameBottom == frameBottom && info.surfaceInset == surfaceInset &&
- info.globalScaleFactor == globalScaleFactor && info.transform == transform &&
- info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.dispatchingTimeout == dispatchingTimeout && info.frame == frame &&
+ info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
info.touchOcclusionMode == touchOcclusionMode && info.ownerPid == ownerPid &&
info.ownerUid == ownerUid && info.packageName == packageName &&
info.inputConfig == inputConfig && info.displayId == displayId &&
@@ -103,10 +100,7 @@
parcel->writeInt32(layoutParamsFlags.get()) ?:
parcel->writeInt32(
static_cast<std::underlying_type_t<WindowInfo::Type>>(layoutParamsType)) ?:
- parcel->writeInt32(frameLeft) ?:
- parcel->writeInt32(frameTop) ?:
- parcel->writeInt32(frameRight) ?:
- parcel->writeInt32(frameBottom) ?:
+ parcel->write(frame) ?:
parcel->writeInt32(surfaceInset) ?:
parcel->writeFloat(globalScaleFactor) ?:
parcel->writeFloat(alpha) ?:
@@ -155,10 +149,7 @@
// clang-format off
status = parcel->readInt32(&lpFlags) ?:
parcel->readInt32(&lpType) ?:
- parcel->readInt32(&frameLeft) ?:
- parcel->readInt32(&frameTop) ?:
- parcel->readInt32(&frameRight) ?:
- parcel->readInt32(&frameBottom) ?:
+ parcel->read(frame) ?:
parcel->readInt32(&surfaceInset) ?:
parcel->readFloat(&globalScaleFactor) ?:
parcel->readFloat(&alpha) ?:
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 5e8e904..c2f47fc 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
+import android.gui.StalledTransactionInfo;
import android.gui.StaticDisplayInfo;
import android.gui.WindowInfosListenerInfo;
@@ -280,8 +281,6 @@
*/
List<LayerDebugInfo> getLayerDebugInfo();
- boolean getColorManagement();
-
/**
* Gets the composition preference of the default data space and default pixel format,
* as well as the wide color gamut data space and wide color gamut pixel format.
@@ -481,6 +480,30 @@
void setOverrideFrameRate(int uid, float frameRate);
/**
+ * Enables or disables the frame rate overlay in the top left corner.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void enableRefreshRateOverlay(boolean active);
+
+ /**
+ * Enables or disables the debug flash.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void setDebugFlash(int delay);
+
+ /**
+ * Force composite ahead of next VSYNC.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void scheduleComposite();
+
+ /**
+ * Force commit ahead of next VSYNC.
+ * Requires root or android.permission.HARDWARE_TEST
+ */
+ void scheduleCommit();
+
+ /**
* Gets priority of the RenderEngine in SurfaceFlinger.
*/
int getGpuContextPriority();
@@ -507,4 +530,10 @@
void removeWindowInfosListener(IWindowInfosListener windowInfosListener);
OverlayProperties getOverlaySupport();
+
+ /**
+ * Returns an instance of StalledTransaction if a transaction from the passed pid has not been
+ * applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned.
+ */
+ @nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
}
diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/gui/android/gui/StalledTransactionInfo.aidl
similarity index 62%
copy from libs/renderengine/include/renderengine/Image.h
copy to libs/gui/android/gui/StalledTransactionInfo.aidl
index 3bb4731..e6aa9bd 100644
--- a/libs/renderengine/include/renderengine/Image.h
+++ b/libs/gui/android/gui/StalledTransactionInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 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.
@@ -14,18 +14,11 @@
* limitations under the License.
*/
-#pragma once
+package android.gui;
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-
-class Image {
-public:
- virtual ~Image() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
-};
-
-} // namespace renderengine
-} // namespace android
+/** @hide */
+parcelable StalledTransactionInfo {
+ String layerName;
+ long bufferId;
+ long frameNumber;
+}
\ No newline at end of file
diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp
index 073cc08..cd738ac 100644
--- a/libs/gui/fuzzer/Android.bp
+++ b/libs/gui/fuzzer/Android.bp
@@ -24,6 +24,7 @@
cc_defaults {
name: "libgui_fuzzer_defaults",
+ defaults: ["android.hardware.power-ndk_shared"],
static_libs: [
"android.hidl.token@1.0-utils",
"libbinder_random_parcel",
@@ -46,7 +47,6 @@
"android.hardware.configstore-utils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.power-V4-ndk",
"android.hidl.token@1.0",
"libSurfaceFlingerProp",
"libgui",
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 4c7d056..2643fa7 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -110,7 +110,6 @@
(override));
MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override));
MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override));
- MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override));
MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*),
(override));
MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes,
@@ -151,6 +150,10 @@
MOCK_METHOD(binder::Status, getDisplayDecorationSupport,
(const sp<IBinder>&, std::optional<gui::DisplayDecorationSupport>*), (override));
MOCK_METHOD(binder::Status, setOverrideFrameRate, (int32_t, float), (override));
+ MOCK_METHOD(binder::Status, enableRefreshRateOverlay, (bool), (override));
+ MOCK_METHOD(binder::Status, setDebugFlash, (int), (override));
+ MOCK_METHOD(binder::Status, scheduleComposite, (), (override));
+ MOCK_METHOD(binder::Status, scheduleCommit, (), (override));
MOCK_METHOD(binder::Status, getGpuContextPriority, (int32_t*), (override));
MOCK_METHOD(binder::Status, getMaxAcquiredBufferCount, (int32_t*), (override));
MOCK_METHOD(binder::Status, addWindowInfosListener,
@@ -158,6 +161,8 @@
MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&),
(override));
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
+ MOCK_METHOD(binder::Status, getStalledTransactionInfo,
+ (int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
diff --git a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
index 3e37e48..4daa3be 100644
--- a/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_surfaceComposerClient_fuzzer.cpp
@@ -178,10 +178,8 @@
windowInfo->name = mFdp.ConsumeRandomLengthString(kRandomStringMaxBytes);
windowInfo->layoutParamsFlags = mFdp.PickValueInArray(kFlags);
windowInfo->layoutParamsType = mFdp.PickValueInArray(kType);
- windowInfo->frameLeft = mFdp.ConsumeIntegral<int32_t>();
- windowInfo->frameTop = mFdp.ConsumeIntegral<int32_t>();
- windowInfo->frameRight = mFdp.ConsumeIntegral<int32_t>();
- windowInfo->frameBottom = mFdp.ConsumeIntegral<int32_t>();
+ windowInfo->frame = Rect(mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>(),
+ mFdp.ConsumeIntegral<int32_t>(), mFdp.ConsumeIntegral<int32_t>());
windowInfo->surfaceInset = mFdp.ConsumeIntegral<int32_t>();
windowInfo->alpha = mFdp.ConsumeFloatingPointInRange<float>(0, 1);
ui::Transform transform(mFdp.PickValueInArray(kOrientation));
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 8d0828d..22c2be7 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,13 +34,13 @@
#include <mutex>
#include <condition_variable>
-#define ATRACE_BUFFER_INDEX(index) \
- do { \
- if (ATRACE_ENABLED()) { \
- char ___traceBuf[1024]; \
- snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \
- android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
- } \
+#define ATRACE_BUFFER_INDEX(index) \
+ do { \
+ if (ATRACE_ENABLED()) { \
+ char ___traceBuf[1024]; \
+ snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.c_str(), (index)); \
+ android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \
+ } \
} while (false)
namespace android {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 7aa7068..2cf5123 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -181,7 +181,7 @@
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
eColorChanged = 0x00010000,
- /* unused = 0x00020000, */
+ eFrameRateCategoryChanged = 0x00020000,
eBufferTransformChanged = 0x00040000,
eTransformToDisplayInverseChanged = 0x00080000,
eCropChanged = 0x00100000,
@@ -213,7 +213,6 @@
eTrustedOverlayChanged = 0x4000'00000000,
eDropInputModeChanged = 0x8000'00000000,
eExtendedRangeBrightnessChanged = 0x10000'00000000,
-
};
layer_state_t();
@@ -265,6 +264,7 @@
// Changes affecting child states.
static constexpr uint64_t AFFECTS_CHILDREN = layer_state_t::GEOMETRY_CHANGES |
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged |
layer_state_t::eFrameRateChanged | layer_state_t::eFrameRateSelectionPriority |
@@ -358,6 +358,9 @@
// Default frame rate compatibility used to set the layer refresh rate votetype.
int8_t defaultFrameRateCompatibility;
+ // Frame rate category to suggest what frame rate range a surface should run.
+ int8_t frameRateCategory;
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 3cf57b1..fd9f186 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -371,6 +371,10 @@
//! Get token for a physical display given its stable ID
static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
+ // Returns StalledTransactionInfo if a transaction from the provided pid has not been applied
+ // due to an unsignaled fence.
+ static std::optional<gui::StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+
struct SCHash {
std::size_t operator()(const sp<SurfaceControl>& sc) const {
return std::hash<SurfaceControl *>{}(sc.get());
@@ -671,6 +675,8 @@
Transaction& setDefaultFrameRateCompatibility(const sp<SurfaceControl>& sc,
int8_t compatibility);
+ Transaction& setFrameRateCategory(const sp<SurfaceControl>& sc, int8_t category);
+
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
// the graphic producers should receive a transform hint as if the
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 7ff7387..bd2eb74 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -194,10 +194,7 @@
std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5);
/* These values are filled in by SurfaceFlinger. */
- int32_t frameLeft = -1;
- int32_t frameTop = -1;
- int32_t frameRight = -1;
- int32_t frameBottom = -1;
+ Rect frame = Rect::INVALID_RECT;
/*
* SurfaceFlinger consumes this value to shrink the computed frame. This is
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 004d875..32dc88b 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -298,7 +298,7 @@
}
Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override {
- _hidl_cb(mBase->getConsumerName().string());
+ _hidl_cb(mBase->getConsumerName().c_str());
return Void();
}
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp
index afeea42..ae79e5b 100644
--- a/libs/gui/tests/GLTest.cpp
+++ b/libs/gui/tests/GLTest.cpp
@@ -177,7 +177,7 @@
while ((err = glGetError()) != GL_NO_ERROR) {
msg += String8::format(", %#x", err);
}
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
}
if (r >= 0 && abs(r - int(pixel[0])) > tolerance) {
msg += String8::format("r(%d isn't %d)", pixel[0], r);
@@ -201,7 +201,7 @@
msg += String8::format("a(%d isn't %d)", pixel[3], a);
}
if (!msg.isEmpty()) {
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
} else {
return ::testing::AssertionSuccess();
}
@@ -236,8 +236,8 @@
msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]",
r1.left, r1.top, r1.right, r1.bottom,
r2.left, r2.top, r2.right, r2.bottom);
- fprintf(stderr, "assertRectEq: %s\n", msg.string());
- return ::testing::AssertionFailure(::testing::Message(msg.string()));
+ fprintf(stderr, "assertRectEq: %s\n", msg.c_str());
+ return ::testing::AssertionFailure(::testing::Message(msg.c_str()));
} else {
return ::testing::AssertionSuccess();
}
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp
index f34561f..ccd0e59 100644
--- a/libs/gui/tests/SurfaceTextureFBO_test.cpp
+++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -59,7 +59,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, 0);
for (int i = 0; i < 4; i++) {
- SCOPED_TRACE(String8::format("frame %d", i).string());
+ SCOPED_TRACE(String8::format("frame %d", i).c_str());
ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(),
&anb));
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index e2b4f3d..f76c0be 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -147,8 +147,9 @@
for (int i = 0; i < 5; i++) {
const android_native_rect_t& crop(crops[i]);
- SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }",
- crop.left, crop.top, crop.right, crop.bottom).string());
+ SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, crop.top,
+ crop.right, crop.bottom)
+ .c_str());
ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
@@ -308,7 +309,7 @@
mFW->waitForFrame();
for (int i = 0; i < numFrames; i++) {
- SCOPED_TRACE(String8::format("frame %d", i).string());
+ SCOPED_TRACE(String8::format("frame %d", i).c_str());
// We must wait for each frame to come in because if we ever do an
// updateTexImage call that doesn't consume a newly available buffer
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 567604d..ffb8622 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -415,7 +415,7 @@
sp<ANativeWindow> window(surface);
native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
- EXPECT_STREQ("TestConsumer", surface->getConsumerName().string());
+ EXPECT_STREQ("TestConsumer", surface->getConsumerName().c_str());
}
TEST_F(SurfaceTest, GetWideColorSupport) {
@@ -879,10 +879,6 @@
return binder::Status::ok();
}
- binder::Status getColorManagement(bool* /*outGetColorManagement*/) override {
- return binder::Status::ok();
- }
-
binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override {
return binder::Status::ok();
}
@@ -993,6 +989,16 @@
return binder::Status::ok();
}
+ binder::Status enableRefreshRateOverlay(bool /*active*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status setDebugFlash(int /*delay*/) override { return binder::Status::ok(); }
+
+ binder::Status scheduleComposite() override { return binder::Status::ok(); }
+
+ binder::Status scheduleCommit() override { return binder::Status::ok(); }
+
binder::Status getGpuContextPriority(int32_t* /*outPriority*/) override {
return binder::Status::ok();
}
@@ -1016,6 +1022,11 @@
return binder::Status::ok();
}
+ binder::Status getStalledTransactionInfo(
+ int32_t /*pid*/, std::optional<gui::StalledTransactionInfo>* /*result*/) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index 461fe4a..f2feaef 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -52,10 +52,7 @@
i.layoutParamsFlags = WindowInfo::Flag::SLIPPERY;
i.layoutParamsType = WindowInfo::Type::INPUT_METHOD;
i.dispatchingTimeout = 12s;
- i.frameLeft = 93;
- i.frameTop = 34;
- i.frameRight = 16;
- i.frameBottom = 19;
+ i.frame = Rect(93, 34, 16, 19);
i.surfaceInset = 17;
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
@@ -85,10 +82,7 @@
ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout);
- ASSERT_EQ(i.frameLeft, i2.frameLeft);
- ASSERT_EQ(i.frameTop, i2.frameTop);
- ASSERT_EQ(i.frameRight, i2.frameRight);
- ASSERT_EQ(i.frameBottom, i2.frameBottom);
+ ASSERT_EQ(i.frame, i2.frame);
ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 757cde2..8656b26 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -139,6 +139,7 @@
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
"MotionPredictor.cpp",
+ "MotionPredictorMetricsManager.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
"TfLiteMotionPredictor.cpp",
@@ -152,9 +153,13 @@
header_libs: [
"flatbuffer_headers",
"jni_headers",
+ "libeigen",
"tensorflow_headers",
],
- export_header_lib_headers: ["jni_headers"],
+ export_header_lib_headers: [
+ "jni_headers",
+ "libeigen",
+ ],
generated_headers: [
"cxx-bridge-header",
@@ -206,6 +211,17 @@
target: {
android: {
+ export_shared_lib_headers: ["libbinder"],
+
+ shared_libs: [
+ "libutils",
+ "libbinder",
+ // Stats logging library and its dependencies.
+ "libstatslog_libinput",
+ "libstatsbootstrap",
+ "android.os.statsbootstrap_aidl-cpp",
+ ],
+
required: [
"motion_predictor_model_prebuilt",
"motion_predictor_model_config",
@@ -228,6 +244,43 @@
},
}
+// Use bootstrap version of stats logging library.
+// libinput is a bootstrap process (starts early in the boot process), and thus can't use the normal
+// `libstatslog` because that requires `libstatssocket`, which is only available later in the boot.
+cc_library {
+ name: "libstatslog_libinput",
+ generated_sources: ["statslog_libinput.cpp"],
+ generated_headers: ["statslog_libinput.h"],
+ export_generated_headers: ["statslog_libinput.h"],
+ shared_libs: [
+ "libbinder",
+ "libstatsbootstrap",
+ "libutils",
+ "android.os.statsbootstrap_aidl-cpp",
+ ],
+}
+
+genrule {
+ name: "statslog_libinput.h",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_libinput.h --module libinput" +
+ " --namespace android,stats,libinput --bootstrap",
+ out: [
+ "statslog_libinput.h",
+ ],
+}
+
+genrule {
+ name: "statslog_libinput.cpp",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_libinput.cpp --module libinput" +
+ " --namespace android,stats,libinput --importHeader statslog_libinput.h" +
+ " --bootstrap",
+ out: [
+ "statslog_libinput.cpp",
+ ],
+}
+
cc_defaults {
name: "libinput_fuzz_defaults",
cpp_std: "c++20",
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 3446540..5fec1a9 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -24,6 +24,7 @@
#include <utils/Trace.h>
#include <input/InputTransport.h>
+#include <input/TraceTools.h>
namespace {
@@ -432,6 +433,10 @@
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")",
+ mName.c_str(), msg->header.seq, msg->header.type);
+ });
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
@@ -463,16 +468,13 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")",
- mName.c_str(), msg->header.seq, msg->header.type);
- ATRACE_NAME(message.c_str());
- }
return OK;
}
status_t InputChannel::receiveMessage(InputMessage* msg) {
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("receiveMessage(inputChannel=%s)", mName.c_str());
+ });
ssize_t nRead;
do {
nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT);
@@ -504,8 +506,8 @@
ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(),
ftl::enum_string(msg->header.type).c_str());
-
if (ATRACE_ENABLED()) {
+ // Add an additional trace point to include data about the received message.
std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32
", type=0x%" PRIx32 ")",
mName.c_str(), msg->header.seq, msg->header.type);
@@ -578,13 +580,11 @@
int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishKeyEvent(inputChannel=%s, action=%s, keyCode=%s)",
- mChannel->getName().c_str(), KeyEvent::actionToString(action),
- KeyEvent::getLabel(keyCode));
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishKeyEvent(inputChannel=%s, action=%s, keyCode=%s)",
+ mChannel->getName().c_str(), KeyEvent::actionToString(action),
+ KeyEvent::getLabel(keyCode));
+ });
ALOGD_IF(debugTransportPublisher(),
"channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
"action=%s, flags=0x%x, keyCode=%s, scanCode=%d, metaState=0x%x, repeatCount=%d,"
@@ -626,12 +626,11 @@
const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
- mChannel->getName().c_str(),
- MotionEvent::actionToString(action).c_str());
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)",
+ mChannel->getName().c_str(),
+ MotionEvent::actionToString(action).c_str());
+ });
if (verifyEvents()) {
Result<void> result =
mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties,
@@ -710,11 +709,10 @@
}
status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
- mChannel->getName().c_str(), toString(hasFocus));
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
+ mChannel->getName().c_str(), toString(hasFocus));
+ });
ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, id=%d, hasFocus=%s",
mChannel->getName().c_str(), __func__, seq, eventId, toString(hasFocus));
@@ -728,12 +726,10 @@
status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId,
bool pointerCaptureEnabled) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)",
- mChannel->getName().c_str(), toString(pointerCaptureEnabled));
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishCaptureEvent(inputChannel=%s, pointerCaptureEnabled=%s)",
+ mChannel->getName().c_str(), toString(pointerCaptureEnabled));
+ });
ALOGD_IF(debugTransportPublisher(),
"channel '%s' publisher ~ %s: seq=%u, id=%d, pointerCaptureEnabled=%s",
mChannel->getName().c_str(), __func__, seq, eventId, toString(pointerCaptureEnabled));
@@ -748,12 +744,10 @@
status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x, float y,
bool isExiting) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishDragEvent(inputChannel=%s, x=%f, y=%f, isExiting=%s)",
- mChannel->getName().c_str(), x, y, toString(isExiting));
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishDragEvent(inputChannel=%s, x=%f, y=%f, isExiting=%s)",
+ mChannel->getName().c_str(), x, y, toString(isExiting));
+ });
ALOGD_IF(debugTransportPublisher(),
"channel '%s' publisher ~ %s: seq=%u, id=%d, x=%f, y=%f, isExiting=%s",
mChannel->getName().c_str(), __func__, seq, eventId, x, y, toString(isExiting));
@@ -769,12 +763,10 @@
}
status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
- mChannel->getName().c_str(), toString(isInTouchMode));
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("publishTouchModeEvent(inputChannel=%s, isInTouchMode=%s)",
+ mChannel->getName().c_str(), toString(isInTouchMode));
+ });
ALOGD_IF(debugTransportPublisher(),
"channel '%s' publisher ~ %s: seq=%u, id=%d, isInTouchMode=%s",
mChannel->getName().c_str(), __func__, seq, eventId, toString(isInTouchMode));
@@ -791,8 +783,10 @@
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
if (result) {
- ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: %s",
- mChannel->getName().c_str(), __func__, strerror(result));
+ if (debugTransportPublisher() && result != WOULD_BLOCK) {
+ LOG(INFO) << "channel '" << mChannel->getName() << "' publisher ~ " << __func__ << ": "
+ << strerror(result);
+ }
return android::base::Error(result);
}
if (msg.header.type == InputMessage::Type::FINISHED) {
diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp
index 341eb6f..6c602e0 100644
--- a/libs/input/InputVerifier.cpp
+++ b/libs/input/InputVerifier.cpp
@@ -31,7 +31,7 @@
// --- InputVerifier ---
InputVerifier::InputVerifier(const std::string& name)
- : mVerifier(android::input::verifier::create(name)){};
+ : mVerifier(android::input::verifier::create(rust::String::lossy(name))){};
Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action,
uint32_t pointerCount,
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 12c9e53..d571917 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -140,7 +140,7 @@
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+ tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
#endif
if (status != OK) {
ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str());
@@ -297,7 +297,7 @@
if (!findKey(ch, &keyCode, &metaState)) {
#if DEBUG_MAPPING
ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.",
- deviceId, toString(chars, numChars).string(), ch);
+ deviceId, toString(chars, numChars).c_str(), ch);
#endif
return false;
}
@@ -309,8 +309,8 @@
addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState);
}
#if DEBUG_MAPPING
- ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.",
- deviceId, toString(chars, numChars).string(), int32_t(outEvents.size()));
+ ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId,
+ toString(chars, numChars).c_str(), int32_t(outEvents.size()));
for (size_t i = 0; i < outEvents.size(); i++) {
ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.",
outEvents[i].getKeyCode(), outEvents[i].getMetaState(),
@@ -756,8 +756,8 @@
status_t KeyCharacterMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -779,8 +779,8 @@
status_t status = parseKey();
if (status) return status;
} else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
break;
@@ -795,10 +795,9 @@
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
+ ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
+ mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str());
+ return BAD_VALUE;
}
}
@@ -807,27 +806,27 @@
if (mState != STATE_TOP) {
ALOGE("%s: Unterminated key description at end of file.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (mMap->mType == KeyboardType::UNKNOWN) {
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (mFormat == Format::BASE) {
if (mMap->mType == KeyboardType::OVERLAY) {
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
} else if (mFormat == Format::OVERLAY) {
if (mMap->mType != KeyboardType::OVERLAY) {
ALOGE("%s: Overlay keyboard layout missing required keyboard "
- "'type OVERLAY' declaration.",
- mTokenizer->getLocation().string());
+ "'type OVERLAY' declaration.",
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -837,8 +836,7 @@
status_t KeyCharacterMap::Parser::parseType() {
if (mMap->mType != KeyboardType::UNKNOWN) {
- ALOGE("%s: Duplicate keyboard 'type' declaration.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -860,8 +858,8 @@
} else if (typeToken == "OVERLAY") {
type = KeyboardType::OVERLAY;
} else {
- ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
- typeToken.string());
+ ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().c_str(),
+ typeToken.c_str());
return BAD_VALUE;
}
@@ -878,8 +876,8 @@
mTokenizer->skipDelimiters(WHITESPACE);
return parseMapKey();
}
- ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
@@ -893,26 +891,26 @@
}
char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
+ int32_t code = int32_t(strtol(codeToken.c_str(), &end, 0));
if (*end) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
const auto it = map.find(code);
if (it != map.end()) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
@@ -926,23 +924,23 @@
status_t KeyCharacterMap::Parser::parseKey() {
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) {
- ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 openBraceToken = mTokenizer->nextToken(WHITESPACE);
if (openBraceToken != "{") {
- ALOGE("%s: Expected '{' after key code label, got '%s'.",
- mTokenizer->getLocation().string(), openBraceToken.string());
+ ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ openBraceToken.c_str());
return BAD_VALUE;
}
@@ -971,10 +969,10 @@
properties.emplace_back(PROPERTY_NUMBER);
} else {
int32_t metaState;
- status_t status = parseModifier(token.string(), &metaState);
+ status_t status = parseModifier(token.c_str(), &metaState);
if (status) {
ALOGE("%s: Expected a property name or modifier, got '%s'.",
- mTokenizer->getLocation().string(), token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return status;
}
properties.emplace_back(PROPERTY_META, metaState);
@@ -992,8 +990,7 @@
}
}
- ALOGE("%s: Expected ',' or ':' after property name.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -1011,18 +1008,17 @@
char16_t character;
status_t status = parseCharacterLiteral(&character);
if (status || !character) {
- ALOGE("%s: Invalid character literal for key.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveCharacter) {
ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveReplacement) {
ALOGE("%s: Cannot combine character literal with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.character = character;
@@ -1032,28 +1028,27 @@
if (token == "none") {
if (haveCharacter) {
ALOGE("%s: Cannot combine multiple character literals or 'none'.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveReplacement) {
ALOGE("%s: Cannot combine 'none' with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
haveCharacter = true;
} else if (token == "fallback") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
if (!keyCode) {
ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
- mTokenizer->getLocation().string(),
- token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
if (haveFallback || haveReplacement) {
ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.fallbackKeyCode = *keyCode;
@@ -1061,29 +1056,27 @@
} else if (token == "replace") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str());
if (!keyCode) {
ALOGE("%s: Invalid key code label for replace, got '%s'.",
- mTokenizer->getLocation().string(),
- token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
if (haveCharacter) {
ALOGE("%s: Cannot combine character literal with replace action.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
if (haveFallback || haveReplacement) {
ALOGE("%s: Cannot combine multiple fallback/replacement key codes.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
behavior.replacementKeyCode = *keyCode;
haveReplacement = true;
} else {
- ALOGE("%s: Expected a key behavior after ':'.",
- mTokenizer->getLocation().string());
+ ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -1096,7 +1089,7 @@
switch (property.property) {
case PROPERTY_LABEL:
if (key.label) {
- ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
key.label = behavior.character;
@@ -1106,7 +1099,7 @@
break;
case PROPERTY_NUMBER:
if (key.number) {
- ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
key.number = behavior.character;
@@ -1118,7 +1111,7 @@
for (const Behavior& b : key.behaviors) {
if (b.metaState == property.metaState) {
ALOGE("%s: Duplicate key behavior for modifier.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
}
@@ -1185,8 +1178,8 @@
return BAD_VALUE;
}
if (combinedMeta & metaState) {
- ALOGE("%s: Duplicate modifier combination '%s'.",
- mTokenizer->getLocation().string(), token.c_str());
+ ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().c_str(),
+ token.c_str());
return BAD_VALUE;
}
@@ -1259,7 +1252,7 @@
}
Error:
- ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string());
+ ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index a194513..ddc9ea4 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -177,7 +177,7 @@
#if DEBUG_PARSER_PERFORMANCE
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+ tokenizer->getFilename().c_str(), tokenizer->getLineNumber(),
elapsedTime / 1000000.0);
#endif
if (!status) {
@@ -306,8 +306,8 @@
status_t KeyLayoutMap::Parser::parse() {
while (!mTokenizer->isEof()) {
- ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
mTokenizer->skipDelimiters(WHITESPACE);
@@ -334,16 +334,15 @@
status_t status = parseRequiredKernelConfig();
if (status) return status;
} else {
- ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(),
- keywordToken.string());
+ ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
ALOGE("%s: Expected end of line or trailing comment, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
}
@@ -362,26 +361,26 @@
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Key>& map =
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str());
if (!keyCode) {
- ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
- keyCodeToken.string());
+ ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ keyCodeToken.c_str());
return BAD_VALUE;
}
@@ -391,15 +390,15 @@
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
+ std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str());
if (!flag) {
- ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
+ ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(),
+ flagToken.c_str());
return BAD_VALUE;
}
if (flags & *flag) {
- ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
- flagToken.string());
+ ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(),
+ flagToken.c_str());
return BAD_VALUE;
}
flags |= *flag;
@@ -417,15 +416,15 @@
status_t KeyLayoutMap::Parser::parseAxis() {
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> scanCode = parseInt(scanCodeToken.string());
+ std::optional<int> scanCode = parseInt(scanCodeToken.c_str());
if (!scanCode) {
- ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
+ ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(),
+ scanCodeToken.c_str());
return BAD_VALUE;
}
if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) {
- ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
- scanCodeToken.string());
+ ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(),
+ scanCodeToken.c_str());
return BAD_VALUE;
}
@@ -438,10 +437,10 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.c_str());
if (!axis) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
- mTokenizer->getLocation().string(), axisToken.string());
+ mTokenizer->getLocation().c_str(), axisToken.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
@@ -450,38 +449,38 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 splitToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> splitValue = parseInt(splitToken.string());
+ std::optional<int> splitValue = parseInt(splitToken.c_str());
if (!splitValue) {
ALOGE("%s: Expected split value, got '%s'.",
- mTokenizer->getLocation().string(), splitToken.string());
+ mTokenizer->getLocation().c_str(), splitToken.c_str());
return BAD_VALUE;
}
axisInfo.splitValue = *splitValue;
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str());
if (!axis) {
ALOGE("%s: Expected low axis label, got '%s'.",
- mTokenizer->getLocation().string(), lowAxisToken.string());
+ mTokenizer->getLocation().c_str(), lowAxisToken.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
+ std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str());
if (!highAxis) {
ALOGE("%s: Expected high axis label, got '%s'.",
- mTokenizer->getLocation().string(), highAxisToken.string());
+ mTokenizer->getLocation().c_str(), highAxisToken.c_str());
return BAD_VALUE;
}
axisInfo.highAxis = *highAxis;
} else {
- std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string());
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(token.c_str());
if (!axis) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
- mTokenizer->getLocation().string(), token.string());
+ mTokenizer->getLocation().c_str(), token.c_str());
return BAD_VALUE;
}
axisInfo.axis = *axis;
@@ -496,16 +495,16 @@
if (keywordToken == "flat") {
mTokenizer->skipDelimiters(WHITESPACE);
String8 flatToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> flatOverride = parseInt(flatToken.string());
+ std::optional<int> flatOverride = parseInt(flatToken.c_str());
if (!flatOverride) {
ALOGE("%s: Expected flat value, got '%s'.",
- mTokenizer->getLocation().string(), flatToken.string());
+ mTokenizer->getLocation().c_str(), flatToken.c_str());
return BAD_VALUE;
}
axisInfo.flatOverride = *flatOverride;
} else {
- ALOGE("%s: Expected keyword 'flat', got '%s'.",
- mTokenizer->getLocation().string(), keywordToken.string());
+ ALOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(),
+ keywordToken.c_str());
return BAD_VALUE;
}
}
@@ -527,27 +526,27 @@
mTokenizer->skipDelimiters(WHITESPACE);
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Led>& map =
mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
- mapUsage ? "usage" : "scan code", codeToken.string());
+ ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().c_str(),
+ mapUsage ? "usage" : "scan code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
+ std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.c_str());
if (!ledCode) {
- ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
- ledCodeToken.string());
+ ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ ledCodeToken.c_str());
return BAD_VALUE;
}
@@ -569,7 +568,7 @@
}
static std::optional<int32_t> getSensorDataIndex(String8 token) {
- std::string tokenStr(token.string());
+ std::string tokenStr(token.c_str());
if (tokenStr == "X") {
return 0;
} else if (tokenStr == "Y") {
@@ -594,26 +593,26 @@
// sensor 0x05 GYROSCOPE Z
status_t KeyLayoutMap::Parser::parseSensor() {
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<int> code = parseInt(codeToken.string());
+ std::optional<int> code = parseInt(codeToken.c_str());
if (!code) {
- ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
- "abs code", codeToken.string());
+ ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().c_str(),
+ "abs code", codeToken.c_str());
return BAD_VALUE;
}
std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
if (map.find(*code) != map.end()) {
- ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
- "abs code", codeToken.string());
+ ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().c_str(),
+ "abs code", codeToken.c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE);
- std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string());
+ std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.c_str());
if (!typeOpt) {
- ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(),
- sensorTypeToken.string());
+ ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().c_str(),
+ sensorTypeToken.c_str());
return BAD_VALUE;
}
InputDeviceSensorType sensorType = typeOpt.value();
@@ -621,8 +620,8 @@
String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE);
std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken);
if (!indexOpt) {
- ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(),
- sensorDataIndexToken.string());
+ ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().c_str(),
+ sensorDataIndexToken.c_str());
return BAD_VALUE;
}
int32_t sensorDataIndex = indexOpt.value();
@@ -643,12 +642,12 @@
// requires_kernel_config CONFIG_HID_PLAYSTATION
status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() {
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- std::string configName = codeToken.string();
+ std::string configName = codeToken.c_str();
const auto result = mMap->mRequiredKernelConfigs.emplace(configName);
if (!result.second) {
ALOGE("%s: Duplicate entry for required kernel config %s.",
- mTokenizer->getLocation().string(), configName.c_str());
+ mTokenizer->getLocation().c_str(), configName.c_str());
return BAD_VALUE;
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 0961a9d..b5a5e72 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -137,10 +137,7 @@
// Pass input event to the MetricsManager.
if (!mMetricsManager) {
- mMetricsManager =
- std::make_optional<MotionPredictorMetricsManager>(mModel->config()
- .predictionInterval,
- mModel->outputLength());
+ mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength());
}
mMetricsManager->onRecord(event);
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
new file mode 100644
index 0000000..67b1032
--- /dev/null
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright 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 "MotionPredictorMetricsManager"
+
+#include <input/MotionPredictorMetricsManager.h>
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+
+#include "Eigen/Core"
+#include "Eigen/Geometry"
+
+#ifdef __ANDROID__
+#include <statslog_libinput.h>
+#endif
+
+namespace android {
+namespace {
+
+inline constexpr int NANOS_PER_SECOND = 1'000'000'000; // nanoseconds per second
+inline constexpr int NANOS_PER_MILLIS = 1'000'000; // nanoseconds per millisecond
+
+// Velocity threshold at which we report "high-velocity" metrics, in pixels per second.
+// This value was selected from manual experimentation, as a threshold that separates "fast"
+// (semi-sloppy) handwriting from more careful medium to slow handwriting.
+inline constexpr float HIGH_VELOCITY_THRESHOLD = 1100.0;
+
+// Small value to add to the path length when computing scale-invariant error to avoid division by
+// zero.
+inline constexpr float PATH_LENGTH_EPSILON = 0.001;
+
+} // namespace
+
+MotionPredictorMetricsManager::MotionPredictorMetricsManager(nsecs_t predictionInterval,
+ size_t maxNumPredictions)
+ : mPredictionInterval(predictionInterval),
+ mMaxNumPredictions(maxNumPredictions),
+ mRecentGroundTruthPoints(maxNumPredictions + 1),
+ mAggregatedMetrics(maxNumPredictions),
+ mAtomFields(maxNumPredictions) {}
+
+void MotionPredictorMetricsManager::onRecord(const MotionEvent& inputEvent) {
+ // Convert MotionEvent to GroundTruthPoint.
+ const PointerCoords* coords = inputEvent.getRawPointerCoords(/*pointerIndex=*/0);
+ LOG_ALWAYS_FATAL_IF(coords == nullptr);
+ const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f{coords->getY(),
+ coords->getX()},
+ .pressure =
+ inputEvent.getPressure(/*pointerIndex=*/0)},
+ .timestamp = inputEvent.getEventTime()};
+
+ // Handle event based on action type.
+ switch (inputEvent.getActionMasked()) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ clearStrokeData();
+ incorporateNewGroundTruth(groundTruthPoint);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_MOVE: {
+ incorporateNewGroundTruth(groundTruthPoint);
+ break;
+ }
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ // Only expect meaningful predictions when given at least two input points.
+ if (mRecentGroundTruthPoints.size() >= 2) {
+ computeAtomFields();
+ reportMetrics();
+ break;
+ }
+ }
+ }
+}
+
+// Adds new predictions to mRecentPredictions and maintains the invariant that elements are
+// sorted in ascending order of targetTimestamp.
+void MotionPredictorMetricsManager::onPredict(const MotionEvent& predictionEvent) {
+ for (size_t i = 0; i < predictionEvent.getHistorySize() + 1; ++i) {
+ // Convert MotionEvent to PredictionPoint.
+ const PointerCoords* coords =
+ predictionEvent.getHistoricalRawPointerCoords(/*pointerIndex=*/0, i);
+ LOG_ALWAYS_FATAL_IF(coords == nullptr);
+ const nsecs_t targetTimestamp = predictionEvent.getHistoricalEventTime(i);
+ mRecentPredictions.push_back(
+ PredictionPoint{{.position = Eigen::Vector2f{coords->getY(), coords->getX()},
+ .pressure =
+ predictionEvent.getHistoricalPressure(/*pointerIndex=*/0,
+ i)},
+ .originTimestamp = mRecentGroundTruthPoints.back().timestamp,
+ .targetTimestamp = targetTimestamp});
+ }
+
+ std::sort(mRecentPredictions.begin(), mRecentPredictions.end());
+}
+
+void MotionPredictorMetricsManager::clearStrokeData() {
+ mRecentGroundTruthPoints.clear();
+ mRecentPredictions.clear();
+ std::fill(mAggregatedMetrics.begin(), mAggregatedMetrics.end(), AggregatedStrokeMetrics{});
+ std::fill(mAtomFields.begin(), mAtomFields.end(), AtomFields{});
+}
+
+void MotionPredictorMetricsManager::incorporateNewGroundTruth(
+ const GroundTruthPoint& groundTruthPoint) {
+ // Note: this removes the oldest point if `mRecentGroundTruthPoints` is already at capacity.
+ mRecentGroundTruthPoints.pushBack(groundTruthPoint);
+
+ // Remove outdated predictions – those that can never be matched with the current or any future
+ // ground truth points. We use fuzzy association for the timestamps here, because ground truth
+ // and prediction timestamps may not be perfectly synchronized.
+ const nsecs_t fuzzy_association_time_delta = mPredictionInterval / 4;
+ const auto firstCurrentIt =
+ std::find_if(mRecentPredictions.begin(), mRecentPredictions.end(),
+ [&groundTruthPoint,
+ fuzzy_association_time_delta](const PredictionPoint& prediction) {
+ return prediction.targetTimestamp >
+ groundTruthPoint.timestamp - fuzzy_association_time_delta;
+ });
+ mRecentPredictions.erase(mRecentPredictions.begin(), firstCurrentIt);
+
+ // Fuzzily match the new ground truth's timestamp to recent predictions' targetTimestamp and
+ // update the corresponding metrics.
+ for (const PredictionPoint& prediction : mRecentPredictions) {
+ if ((prediction.targetTimestamp >
+ groundTruthPoint.timestamp - fuzzy_association_time_delta) &&
+ (prediction.targetTimestamp <
+ groundTruthPoint.timestamp + fuzzy_association_time_delta)) {
+ updateAggregatedMetrics(prediction);
+ }
+ }
+}
+
+void MotionPredictorMetricsManager::updateAggregatedMetrics(
+ const PredictionPoint& predictionPoint) {
+ if (mRecentGroundTruthPoints.size() < 2) {
+ return;
+ }
+
+ const GroundTruthPoint& latestGroundTruthPoint = mRecentGroundTruthPoints.back();
+ const GroundTruthPoint& previousGroundTruthPoint =
+ mRecentGroundTruthPoints[mRecentGroundTruthPoints.size() - 2];
+ // Calculate prediction error vector.
+ const Eigen::Vector2f groundTruthTrajectory =
+ latestGroundTruthPoint.position - previousGroundTruthPoint.position;
+ const Eigen::Vector2f predictionTrajectory =
+ predictionPoint.position - previousGroundTruthPoint.position;
+ const Eigen::Vector2f predictionError = predictionTrajectory - groundTruthTrajectory;
+
+ // By default, prediction error counts fully as both off-trajectory and along-trajectory error.
+ // This serves as the fallback when the two most recent ground truth points are equal.
+ const float predictionErrorNorm = predictionError.norm();
+ float alongTrajectoryError = predictionErrorNorm;
+ float offTrajectoryError = predictionErrorNorm;
+ if (groundTruthTrajectory.squaredNorm() > 0) {
+ // Rotate the prediction error vector by the angle of the ground truth trajectory vector.
+ // This yields a vector whose first component is the along-trajectory error and whose
+ // second component is the off-trajectory error.
+ const float theta = std::atan2(groundTruthTrajectory[1], groundTruthTrajectory[0]);
+ const Eigen::Vector2f rotatedPredictionError = Eigen::Rotation2Df(-theta) * predictionError;
+ alongTrajectoryError = rotatedPredictionError[0];
+ offTrajectoryError = rotatedPredictionError[1];
+ }
+
+ // Compute the multiple of mPredictionInterval nearest to the amount of time into the
+ // future being predicted. This serves as the time bucket index into mAggregatedMetrics.
+ const float timestampDeltaFloat =
+ static_cast<float>(predictionPoint.targetTimestamp - predictionPoint.originTimestamp);
+ const size_t tIndex =
+ static_cast<size_t>(std::round(timestampDeltaFloat / mPredictionInterval - 1));
+
+ // Aggregate values into "general errors".
+ mAggregatedMetrics[tIndex].alongTrajectoryErrorSum += alongTrajectoryError;
+ mAggregatedMetrics[tIndex].alongTrajectorySumSquaredErrors +=
+ alongTrajectoryError * alongTrajectoryError;
+ mAggregatedMetrics[tIndex].offTrajectorySumSquaredErrors +=
+ offTrajectoryError * offTrajectoryError;
+ const float pressureError = predictionPoint.pressure - latestGroundTruthPoint.pressure;
+ mAggregatedMetrics[tIndex].pressureSumSquaredErrors += pressureError * pressureError;
+ ++mAggregatedMetrics[tIndex].generalErrorsCount;
+
+ // Aggregate values into high-velocity metrics, if we are in one of the last two time buckets
+ // and the velocity is above the threshold. Velocity here is measured in pixels per second.
+ const float velocity = groundTruthTrajectory.norm() /
+ (static_cast<float>(latestGroundTruthPoint.timestamp -
+ previousGroundTruthPoint.timestamp) /
+ NANOS_PER_SECOND);
+ if ((tIndex + 2 >= mMaxNumPredictions) && (velocity > HIGH_VELOCITY_THRESHOLD)) {
+ mAggregatedMetrics[tIndex].highVelocityAlongTrajectorySse +=
+ alongTrajectoryError * alongTrajectoryError;
+ mAggregatedMetrics[tIndex].highVelocityOffTrajectorySse +=
+ offTrajectoryError * offTrajectoryError;
+ ++mAggregatedMetrics[tIndex].highVelocityErrorsCount;
+ }
+
+ // Compute path length for scale-invariant errors.
+ float pathLength = 0;
+ for (size_t i = 1; i < mRecentGroundTruthPoints.size(); ++i) {
+ pathLength +=
+ (mRecentGroundTruthPoints[i].position - mRecentGroundTruthPoints[i - 1].position)
+ .norm();
+ }
+ // Avoid overweighting errors at the beginning of a stroke: compute the path length as if there
+ // were a full ground truth history by filling in missing segments with the average length.
+ // Note: the "- 1" is needed to translate from number of endpoints to number of segments.
+ pathLength *= static_cast<float>(mRecentGroundTruthPoints.capacity() - 1) /
+ (mRecentGroundTruthPoints.size() - 1);
+ pathLength += PATH_LENGTH_EPSILON; // Ensure path length is nonzero (>= PATH_LENGTH_EPSILON).
+
+ // Compute and aggregate scale-invariant errors.
+ const float scaleInvariantAlongTrajectoryError = alongTrajectoryError / pathLength;
+ const float scaleInvariantOffTrajectoryError = offTrajectoryError / pathLength;
+ mAggregatedMetrics[tIndex].scaleInvariantAlongTrajectorySse +=
+ scaleInvariantAlongTrajectoryError * scaleInvariantAlongTrajectoryError;
+ mAggregatedMetrics[tIndex].scaleInvariantOffTrajectorySse +=
+ scaleInvariantOffTrajectoryError * scaleInvariantOffTrajectoryError;
+ ++mAggregatedMetrics[tIndex].scaleInvariantErrorsCount;
+}
+
+void MotionPredictorMetricsManager::computeAtomFields() {
+ for (size_t i = 0; i < mAggregatedMetrics.size(); ++i) {
+ if (mAggregatedMetrics[i].generalErrorsCount == 0) {
+ // We have not received data corresponding to metrics for this time bucket.
+ continue;
+ }
+
+ mAtomFields[i].deltaTimeBucketMilliseconds =
+ static_cast<int>(mPredictionInterval / NANOS_PER_MILLIS * (i + 1));
+
+ // Note: we need the "* 1000"s below because we report values in integral milli-units.
+
+ { // General errors: reported for every time bucket.
+ const float alongTrajectoryErrorMean = mAggregatedMetrics[i].alongTrajectoryErrorSum /
+ mAggregatedMetrics[i].generalErrorsCount;
+ mAtomFields[i].alongTrajectoryErrorMeanMillipixels =
+ static_cast<int>(alongTrajectoryErrorMean * 1000);
+
+ const float alongTrajectoryMse = mAggregatedMetrics[i].alongTrajectorySumSquaredErrors /
+ mAggregatedMetrics[i].generalErrorsCount;
+ // Take the max with 0 to avoid negative values caused by numerical instability.
+ const float alongTrajectoryErrorVariance =
+ std::max(0.0f,
+ alongTrajectoryMse -
+ alongTrajectoryErrorMean * alongTrajectoryErrorMean);
+ const float alongTrajectoryErrorStd = std::sqrt(alongTrajectoryErrorVariance);
+ mAtomFields[i].alongTrajectoryErrorStdMillipixels =
+ static_cast<int>(alongTrajectoryErrorStd * 1000);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[i].offTrajectorySumSquaredErrors < 0,
+ "mAggregatedMetrics[%zu].offTrajectorySumSquaredErrors = %f should "
+ "not be negative",
+ i, mAggregatedMetrics[i].offTrajectorySumSquaredErrors);
+ const float offTrajectoryRmse =
+ std::sqrt(mAggregatedMetrics[i].offTrajectorySumSquaredErrors /
+ mAggregatedMetrics[i].generalErrorsCount);
+ mAtomFields[i].offTrajectoryRmseMillipixels =
+ static_cast<int>(offTrajectoryRmse * 1000);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[i].pressureSumSquaredErrors < 0,
+ "mAggregatedMetrics[%zu].pressureSumSquaredErrors = %f should not "
+ "be negative",
+ i, mAggregatedMetrics[i].pressureSumSquaredErrors);
+ const float pressureRmse = std::sqrt(mAggregatedMetrics[i].pressureSumSquaredErrors /
+ mAggregatedMetrics[i].generalErrorsCount);
+ mAtomFields[i].pressureRmseMilliunits = static_cast<int>(pressureRmse * 1000);
+ }
+
+ // High-velocity errors: reported only for last two time buckets.
+ // Check if we are in one of the last two time buckets, and there is high-velocity data.
+ if ((i + 2 >= mMaxNumPredictions) && (mAggregatedMetrics[i].highVelocityErrorsCount > 0)) {
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[i].highVelocityAlongTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].highVelocityAlongTrajectorySse = %f "
+ "should not be negative",
+ i, mAggregatedMetrics[i].highVelocityAlongTrajectorySse);
+ const float alongTrajectoryRmse =
+ std::sqrt(mAggregatedMetrics[i].highVelocityAlongTrajectorySse /
+ mAggregatedMetrics[i].highVelocityErrorsCount);
+ mAtomFields[i].highVelocityAlongTrajectoryRmse =
+ static_cast<int>(alongTrajectoryRmse * 1000);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[i].highVelocityOffTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].highVelocityOffTrajectorySse = %f should "
+ "not be negative",
+ i, mAggregatedMetrics[i].highVelocityOffTrajectorySse);
+ const float offTrajectoryRmse =
+ std::sqrt(mAggregatedMetrics[i].highVelocityOffTrajectorySse /
+ mAggregatedMetrics[i].highVelocityErrorsCount);
+ mAtomFields[i].highVelocityOffTrajectoryRmse =
+ static_cast<int>(offTrajectoryRmse * 1000);
+ }
+
+ // Scale-invariant errors: reported only for the last time bucket, where the values
+ // represent an average across all time buckets.
+ if (i + 1 == mMaxNumPredictions) {
+ // Compute error averages.
+ float alongTrajectoryRmseSum = 0;
+ float offTrajectoryRmseSum = 0;
+ for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
+ // If we have general errors (checked above), we should always also have
+ // scale-invariant errors.
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantErrorsCount == 0,
+ "mAggregatedMetrics[%zu].scaleInvariantErrorsCount is 0", j);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
+ alongTrajectoryRmseSum +=
+ std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
+ offTrajectoryRmseSum +=
+ std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+ }
+
+ const float averageAlongTrajectoryRmse =
+ alongTrajectoryRmseSum / mAggregatedMetrics.size();
+ mAtomFields.back().scaleInvariantAlongTrajectoryRmse =
+ static_cast<int>(averageAlongTrajectoryRmse * 1000);
+
+ const float averageOffTrajectoryRmse = offTrajectoryRmseSum / mAggregatedMetrics.size();
+ mAtomFields.back().scaleInvariantOffTrajectoryRmse =
+ static_cast<int>(averageOffTrajectoryRmse * 1000);
+ }
+ }
+}
+
+void MotionPredictorMetricsManager::reportMetrics() {
+ // Report one atom for each time bucket.
+ for (size_t i = 0; i < mAtomFields.size(); ++i) {
+ // Call stats_write logging function only on Android targets (not supported on host).
+#ifdef __ANDROID__
+ android::stats::libinput::
+ stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
+ /*stylus_vendor_id=*/0,
+ /*stylus_product_id=*/0, mAtomFields[i].deltaTimeBucketMilliseconds,
+ mAtomFields[i].alongTrajectoryErrorMeanMillipixels,
+ mAtomFields[i].alongTrajectoryErrorStdMillipixels,
+ mAtomFields[i].offTrajectoryRmseMillipixels,
+ mAtomFields[i].pressureRmseMilliunits,
+ mAtomFields[i].highVelocityAlongTrajectoryRmse,
+ mAtomFields[i].highVelocityOffTrajectoryRmse,
+ mAtomFields[i].scaleInvariantAlongTrajectoryRmse,
+ mAtomFields[i].scaleInvariantOffTrajectoryRmse);
+#endif
+ }
+
+ // Set mock atom fields, if available.
+ if (mMockLoggedAtomFields != nullptr) {
+ *mMockLoggedAtomFields = mAtomFields;
+ }
+}
+
+} // namespace android
diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp
index 548f894..315f5a6 100644
--- a/libs/input/PropertyMap.cpp
+++ b/libs/input/PropertyMap.cpp
@@ -163,8 +163,8 @@
status_t PropertyMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -172,7 +172,7 @@
if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
if (keyToken.isEmpty()) {
- ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
+ ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -180,7 +180,7 @@
if (mTokenizer->nextChar() != '=') {
ALOGE("%s: Expected '=' between property key and value.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -189,20 +189,20 @@
String8 valueToken = mTokenizer->nextToken(WHITESPACE);
if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
mTokenizer->skipDelimiters(WHITESPACE);
if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
if (mMap->hasProperty(keyToken.string())) {
ALOGE("%s: Duplicate property value for key '%s'.",
- mTokenizer->getLocation().string(), keyToken.string());
+ mTokenizer->getLocation().c_str(), keyToken.c_str());
return BAD_VALUE;
}
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 8704eee..116b778 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "VelocityTracker"
#include <android-base/logging.h>
+#include <ftl/enum.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
@@ -148,27 +149,19 @@
VelocityTracker::VelocityTracker(const Strategy strategy)
: mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {}
-VelocityTracker::~VelocityTracker() {
-}
-
bool VelocityTracker::isAxisSupported(int32_t axis) {
return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end();
}
void VelocityTracker::configureStrategy(int32_t axis) {
const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end();
-
- std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
- if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) {
- createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis);
+ if (isDifferentialAxis || mOverrideStrategy == VelocityTracker::Strategy::DEFAULT) {
+ // Do not allow overrides of strategies for differential axes, for now.
+ mConfiguredStrategies[axis] = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
+ /*deltaValues=*/isDifferentialAxis);
} else {
- createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis),
- /*deltaValues=*/isDifferentialAxis);
+ mConfiguredStrategies[axis] = createStrategy(mOverrideStrategy, /*deltaValues=*/false);
}
-
- LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
- "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis);
- mConfiguredStrategies[axis] = std::move(createdStrategy);
}
std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
@@ -216,6 +209,9 @@
default:
break;
}
+ LOG(FATAL) << "Invalid strategy: " << ftl::enum_string(strategy)
+ << ", deltaValues = " << deltaValues;
+
return nullptr;
}
@@ -272,12 +268,10 @@
mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position);
if (DEBUG_VELOCITY) {
- ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32
- ", activePointerId=%s",
- eventTime, pointerId, toString(mActivePointerId).c_str());
-
- ALOGD(" %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position,
- toString(getVelocity(axis, pointerId)).c_str());
+ LOG(INFO) << "VelocityTracker: addMovement axis=" << MotionEvent::getLabel(axis)
+ << ", eventTime=" << eventTime << ", pointerId=" << pointerId
+ << ", activePointerId=" << toString(mActivePointerId) << ", position=" << position
+ << ", velocity=" << toString(getVelocity(axis, pointerId));
}
}
diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp
index 865366b..de62c87 100644
--- a/libs/input/VirtualKeyMap.cpp
+++ b/libs/input/VirtualKeyMap.cpp
@@ -79,8 +79,8 @@
status_t VirtualKeyMap::Parser::parse() {
while (!mTokenizer->isEof()) {
#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
#endif
mTokenizer->skipDelimiters(WHITESPACE);
@@ -91,7 +91,7 @@
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
if (token != "0x01") {
ALOGE("%s: Unknown virtual key type, expected 0x01.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -103,7 +103,7 @@
&& parseNextIntField(&defn.height);
if (!success) {
ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.",
- mTokenizer->getLocation().string());
+ mTokenizer->getLocation().c_str());
return BAD_VALUE;
}
@@ -116,9 +116,8 @@
} while (consumeFieldDelimiterAndSkipWhitespace());
if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
+ ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(),
+ mTokenizer->peekRemainderOfLine().c_str());
return BAD_VALUE;
}
}
@@ -146,9 +145,9 @@
String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER);
char* end;
- *outValue = strtol(token.string(), &end, 0);
+ *outValue = strtol(token.c_str(), &end, 0);
if (token.isEmpty() || *end != '\0') {
- ALOGE("Expected an integer, got '%s'.", token.string());
+ ALOGE("Expected an integer, got '%s'.", token.c_str());
return false;
}
return true;
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index dab843b..8f6f95b 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -57,4 +57,88 @@
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
+
+ /**
+ * Use the default Velocity Tracker Strategy. Different axes may use different default
+ * strategies.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1;
+
+ /**
+ * Velocity Tracker Strategy: Impulse.
+ * Physical model of pushing an object. Quality: VERY GOOD.
+ * Works with duplicate coordinates, unclean finger liftoff.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0;
+
+ /**
+ * Velocity Tracker Strategy: LSQ1.
+ * 1st order least squares. Quality: POOR.
+ * Frequently underfits the touch data especially when the finger accelerates
+ * or changes direction. Often underestimates velocity. The direction
+ * is overly influenced by historical touch points.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1;
+
+ /**
+ * Velocity Tracker Strategy: LSQ2.
+ * 2nd order least squares. Quality: VERY GOOD.
+ * Pretty much ideal, but can be confused by certain kinds of touch data,
+ * particularly if the panel has a tendency to generate delayed,
+ * duplicate or jittery touch coordinates when the finger is released.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2;
+
+ /**
+ * Velocity Tracker Strategy: LSQ3.
+ * 3rd order least squares. Quality: UNUSABLE.
+ * Frequently overfits the touch data yielding wildly divergent estimates
+ * of the velocity when the finger is released.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_DELTA.
+ * 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_CENTRAL.
+ * 2nd order weighted least squares, central weighting. Quality: EXPERIMENTALe
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5;
+
+ /**
+ * Velocity Tracker Strategy: WLSQ2_RECENT.
+ * 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL
+ */
+ const int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6;
+
+ /**
+ * Velocity Tracker Strategy: INT1.
+ * 1st order integrating filter. Quality: GOOD.
+ * Not as good as 'lsq2' because it cannot estimate acceleration but it is
+ * more tolerant of errors. Like 'lsq1', this strategy tends to underestimate
+ * the velocity of a fling but this strategy tends to respond to changes in
+ * direction more quickly and accurately.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_INT1 = 7;
+
+ /**
+ * Velocity Tracker Strategy: INT2.
+ * 2nd order integrating filter. Quality: EXPERIMENTAL.
+ * For comparison purposes only. Unlike 'int1' this strategy can compensate
+ * for acceleration but it typically overestimates the effect.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_INT2 = 8;
+
+ /**
+ * Velocity Tracker Strategy: Legacy.
+ * Legacy velocity tracker algorithm. Quality: POOR.
+ * For comparison purposes only. This algorithm is strongly influenced by
+ * old data points, consistently underestimates velocity and takes a very long
+ * time to adjust to changes in direction.
+ */
+ const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index cadac88..e7224ff 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -18,7 +18,9 @@
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
+ "InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
+ "MotionPredictorMetricsManager_test.cpp",
"RingBuffer_test.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
@@ -51,13 +53,6 @@
undefined: true,
},
},
- target: {
- host: {
- sanitize: {
- address: true,
- },
- },
- },
shared_libs: [
"libbase",
"libbinder",
@@ -76,6 +71,21 @@
unit_test: true,
},
test_suites: ["device-tests"],
+ target: {
+ host: {
+ sanitize: {
+ address: true,
+ },
+ },
+ android: {
+ static_libs: [
+ // Stats logging library and its dependencies.
+ "libstatslog_libinput",
+ "libstatsbootstrap",
+ "android.os.statsbootstrap_aidl-cpp",
+ ],
+ },
+ },
}
// NOTE: This is a compile time test, and does not need to be
diff --git a/libs/renderengine/mock/Image.cpp b/libs/input/tests/InputVerifier_test.cpp
similarity index 62%
rename from libs/renderengine/mock/Image.cpp
rename to libs/input/tests/InputVerifier_test.cpp
index 57f4346..e24fa6e 100644
--- a/libs/renderengine/mock/Image.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-#include <renderengine/mock/Image.h>
+#include <gtest/gtest.h>
+#include <input/InputVerifier.h>
+#include <string>
namespace android {
-namespace renderengine {
-namespace mock {
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Image::Image() = default;
-Image::~Image() = default;
+TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
+ constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
+ const std::string name(bytes, sizeof(bytes));
+ InputVerifier verifier(name);
+}
-} // namespace mock
-} // namespace renderengine
} // namespace android
diff --git a/libs/input/tests/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
new file mode 100644
index 0000000..b420a5a
--- /dev/null
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -0,0 +1,972 @@
+/*
+ * Copyright 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.
+ */
+
+#include <input/MotionPredictor.h>
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <numeric>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/InputEventBuilders.h>
+#include <utils/Timers.h> // for nsecs_t
+
+#include "Eigen/Core"
+#include "Eigen/Geometry"
+
+namespace android {
+namespace {
+
+using ::testing::FloatNear;
+using ::testing::Matches;
+
+using GroundTruthPoint = MotionPredictorMetricsManager::GroundTruthPoint;
+using PredictionPoint = MotionPredictorMetricsManager::PredictionPoint;
+using AtomFields = MotionPredictorMetricsManager::AtomFields;
+
+inline constexpr int NANOS_PER_MILLIS = 1'000'000;
+
+inline constexpr nsecs_t TEST_INITIAL_TIMESTAMP = 1'000'000'000;
+inline constexpr size_t TEST_MAX_NUM_PREDICTIONS = 5;
+inline constexpr nsecs_t TEST_PREDICTION_INTERVAL_NANOS = 12'500'000 / 3; // 1 / (240 hz)
+inline constexpr int NO_DATA_SENTINEL = MotionPredictorMetricsManager::NO_DATA_SENTINEL;
+
+// Parameters:
+// • arg: Eigen::Vector2f
+// • target: Eigen::Vector2f
+// • epsilon: float
+MATCHER_P2(Vector2fNear, target, epsilon, "") {
+ return Matches(FloatNear(target[0], epsilon))(arg[0]) &&
+ Matches(FloatNear(target[1], epsilon))(arg[1]);
+}
+
+// Parameters:
+// • arg: PredictionPoint
+// • target: PredictionPoint
+// • epsilon: float
+MATCHER_P2(PredictionPointNear, target, epsilon, "") {
+ if (!Matches(Vector2fNear(target.position, epsilon))(arg.position)) {
+ *result_listener << "Position mismatch. Actual: (" << arg.position[0] << ", "
+ << arg.position[1] << "), expected: (" << target.position[0] << ", "
+ << target.position[1] << ")";
+ return false;
+ }
+ if (!Matches(FloatNear(target.pressure, epsilon))(arg.pressure)) {
+ *result_listener << "Pressure mismatch. Actual: " << arg.pressure
+ << ", expected: " << target.pressure;
+ return false;
+ }
+ if (arg.originTimestamp != target.originTimestamp) {
+ *result_listener << "Origin timestamp mismatch. Actual: " << arg.originTimestamp
+ << ", expected: " << target.originTimestamp;
+ return false;
+ }
+ if (arg.targetTimestamp != target.targetTimestamp) {
+ *result_listener << "Target timestamp mismatch. Actual: " << arg.targetTimestamp
+ << ", expected: " << target.targetTimestamp;
+ return false;
+ }
+ return true;
+}
+
+// --- Mathematical helper functions. ---
+
+template <typename T>
+T average(std::vector<T> values) {
+ return std::accumulate(values.begin(), values.end(), T{}) / static_cast<T>(values.size());
+}
+
+template <typename T>
+T standardDeviation(std::vector<T> values) {
+ T mean = average(values);
+ T accumulator = {};
+ for (const T value : values) {
+ accumulator += value * value - mean * mean;
+ }
+ // Take the max with 0 to avoid negative values caused by numerical instability.
+ return std::sqrt(std::max(T{}, accumulator) / static_cast<T>(values.size()));
+}
+
+template <typename T>
+T rmse(std::vector<T> errors) {
+ T sse = {};
+ for (const T error : errors) {
+ sse += error * error;
+ }
+ return std::sqrt(sse / static_cast<T>(errors.size()));
+}
+
+TEST(MathematicalHelperFunctionTest, Average) {
+ std::vector<float> values{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ EXPECT_EQ(5.5f, average(values));
+}
+
+TEST(MathematicalHelperFunctionTest, StandardDeviation) {
+ // https://www.calculator.net/standard-deviation-calculator.html?numberinputs=10%2C+12%2C+23%2C+23%2C+16%2C+23%2C+21%2C+16
+ std::vector<float> values{10, 12, 23, 23, 16, 23, 21, 16};
+ EXPECT_FLOAT_EQ(4.8989794855664f, standardDeviation(values));
+}
+
+TEST(MathematicalHelperFunctionTest, Rmse) {
+ std::vector<float> errors{1, 5, 7, 7, 8, 20};
+ EXPECT_FLOAT_EQ(9.899494937f, rmse(errors));
+}
+
+// --- MotionEvent-related helper functions. ---
+
+// Creates a MotionEvent corresponding to the given GroundTruthPoint.
+MotionEvent makeMotionEvent(const GroundTruthPoint& groundTruthPoint) {
+ // Build single pointer of type STYLUS, with coordinates from groundTruthPoint.
+ PointerBuilder pointerBuilder =
+ PointerBuilder(/*id=*/0, ToolType::STYLUS)
+ .x(groundTruthPoint.position[1])
+ .y(groundTruthPoint.position[0])
+ .axis(AMOTION_EVENT_AXIS_PRESSURE, groundTruthPoint.pressure);
+ return MotionEventBuilder(/*action=*/AMOTION_EVENT_ACTION_MOVE,
+ /*source=*/AINPUT_SOURCE_CLASS_POINTER)
+ .eventTime(groundTruthPoint.timestamp)
+ .pointer(pointerBuilder)
+ .build();
+}
+
+// Creates a MotionEvent corresponding to the given sequence of PredictionPoints.
+MotionEvent makeMotionEvent(const std::vector<PredictionPoint>& predictionPoints) {
+ // Build single pointer of type STYLUS, with coordinates from first prediction point.
+ PointerBuilder pointerBuilder =
+ PointerBuilder(/*id=*/0, ToolType::STYLUS)
+ .x(predictionPoints[0].position[1])
+ .y(predictionPoints[0].position[0])
+ .axis(AMOTION_EVENT_AXIS_PRESSURE, predictionPoints[0].pressure);
+ MotionEvent predictionEvent =
+ MotionEventBuilder(
+ /*action=*/AMOTION_EVENT_ACTION_MOVE, /*source=*/AINPUT_SOURCE_CLASS_POINTER)
+ .eventTime(predictionPoints[0].targetTimestamp)
+ .pointer(pointerBuilder)
+ .build();
+ for (size_t i = 1; i < predictionPoints.size(); ++i) {
+ PointerCoords coords =
+ PointerBuilder(/*id=*/0, ToolType::STYLUS)
+ .x(predictionPoints[i].position[1])
+ .y(predictionPoints[i].position[0])
+ .axis(AMOTION_EVENT_AXIS_PRESSURE, predictionPoints[i].pressure)
+ .buildCoords();
+ predictionEvent.addSample(predictionPoints[i].targetTimestamp, &coords);
+ }
+ return predictionEvent;
+}
+
+// Creates a MotionEvent corresponding to a stylus lift (UP) ground truth event.
+MotionEvent makeLiftMotionEvent() {
+ return MotionEventBuilder(/*action=*/AMOTION_EVENT_ACTION_UP,
+ /*source=*/AINPUT_SOURCE_CLASS_POINTER)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
+ .build();
+}
+
+TEST(MakeMotionEventTest, MakeGroundTruthMotionEvent) {
+ const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f),
+ .pressure = 0.6f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const MotionEvent groundTruthMotionEvent = makeMotionEvent(groundTruthPoint);
+
+ ASSERT_EQ(1u, groundTruthMotionEvent.getPointerCount());
+ // Note: a MotionEvent's "history size" is one less than its number of samples.
+ ASSERT_EQ(0u, groundTruthMotionEvent.getHistorySize());
+ EXPECT_EQ(groundTruthPoint.position[0], groundTruthMotionEvent.getRawPointerCoords(0)->getY());
+ EXPECT_EQ(groundTruthPoint.position[1], groundTruthMotionEvent.getRawPointerCoords(0)->getX());
+ EXPECT_EQ(groundTruthPoint.pressure,
+ groundTruthMotionEvent.getRawPointerCoords(0)->getAxisValue(
+ AMOTION_EVENT_AXIS_PRESSURE));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, groundTruthMotionEvent.getAction());
+}
+
+TEST(MakeMotionEventTest, MakePredictionMotionEvent) {
+ const nsecs_t originTimestamp = TEST_INITIAL_TIMESTAMP;
+ const std::vector<PredictionPoint>
+ predictionPoints{{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp + 5 * NANOS_PER_MILLIS},
+ {{.position = Eigen::Vector2f(11.0f, 22.0f), .pressure = 0.5f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp + 10 * NANOS_PER_MILLIS},
+ {{.position = Eigen::Vector2f(12.0f, 24.0f), .pressure = 0.4f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp + 15 * NANOS_PER_MILLIS}};
+ const MotionEvent predictionMotionEvent = makeMotionEvent(predictionPoints);
+
+ ASSERT_EQ(1u, predictionMotionEvent.getPointerCount());
+ // Note: a MotionEvent's "history size" is one less than its number of samples.
+ ASSERT_EQ(predictionPoints.size(), predictionMotionEvent.getHistorySize() + 1);
+ for (size_t i = 0; i < predictionPoints.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ const PointerCoords coords = *predictionMotionEvent.getHistoricalRawPointerCoords(
+ /*pointerIndex=*/0, /*historicalIndex=*/i);
+ EXPECT_EQ(predictionPoints[i].position[0], coords.getY());
+ EXPECT_EQ(predictionPoints[i].position[1], coords.getX());
+ EXPECT_EQ(predictionPoints[i].pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ // Note: originTimestamp is discarded when converting PredictionPoint to MotionEvent.
+ EXPECT_EQ(predictionPoints[i].targetTimestamp,
+ predictionMotionEvent.getHistoricalEventTime(i));
+ EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, predictionMotionEvent.getAction());
+ }
+}
+
+TEST(MakeMotionEventTest, MakeLiftMotionEvent) {
+ const MotionEvent liftMotionEvent = makeLiftMotionEvent();
+ ASSERT_EQ(1u, liftMotionEvent.getPointerCount());
+ // Note: a MotionEvent's "history size" is one less than its number of samples.
+ ASSERT_EQ(0u, liftMotionEvent.getHistorySize());
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, liftMotionEvent.getAction());
+}
+
+// --- Ground-truth-generation helper functions. ---
+
+std::vector<GroundTruthPoint> generateConstantGroundTruthPoints(
+ const GroundTruthPoint& groundTruthPoint, size_t numPoints) {
+ std::vector<GroundTruthPoint> groundTruthPoints;
+ nsecs_t timestamp = groundTruthPoint.timestamp;
+ for (size_t i = 0; i < numPoints; ++i) {
+ groundTruthPoints.emplace_back(groundTruthPoint);
+ groundTruthPoints.back().timestamp = timestamp;
+ timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ }
+ return groundTruthPoints;
+}
+
+// This function uses the coordinate system (y, x), with +y pointing downwards and +x pointing
+// rightwards. Angles are measured counterclockwise from down (+y).
+std::vector<GroundTruthPoint> generateCircularArcGroundTruthPoints(Eigen::Vector2f initialPosition,
+ float initialAngle,
+ float velocity,
+ float turningAngle,
+ size_t numPoints) {
+ std::vector<GroundTruthPoint> groundTruthPoints;
+ // Create first point.
+ if (numPoints > 0) {
+ groundTruthPoints.push_back({{.position = initialPosition, .pressure = 0.0f},
+ .timestamp = TEST_INITIAL_TIMESTAMP});
+ }
+ float trajectoryAngle = initialAngle; // measured counterclockwise from +y axis.
+ for (size_t i = 1; i < numPoints; ++i) {
+ const Eigen::Vector2f trajectory =
+ Eigen::Rotation2D(trajectoryAngle) * Eigen::Vector2f(1, 0);
+ groundTruthPoints.push_back(
+ {{.position = groundTruthPoints.back().position + velocity * trajectory,
+ .pressure = 0.0f},
+ .timestamp = groundTruthPoints.back().timestamp + TEST_PREDICTION_INTERVAL_NANOS});
+ trajectoryAngle += turningAngle;
+ }
+ return groundTruthPoints;
+}
+
+TEST(GenerateConstantGroundTruthPointsTest, BasicTest) {
+ const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3);
+
+ ASSERT_EQ(3u, groundTruthPoints.size());
+ // First point.
+ EXPECT_EQ(groundTruthPoints[0].position, groundTruthPoint.position);
+ EXPECT_EQ(groundTruthPoints[0].pressure, groundTruthPoint.pressure);
+ EXPECT_EQ(groundTruthPoints[0].timestamp, groundTruthPoint.timestamp);
+ // Second point.
+ EXPECT_EQ(groundTruthPoints[1].position, groundTruthPoint.position);
+ EXPECT_EQ(groundTruthPoints[1].pressure, groundTruthPoint.pressure);
+ EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp);
+ // Third point.
+ EXPECT_EQ(groundTruthPoints[2].position, groundTruthPoint.position);
+ EXPECT_EQ(groundTruthPoints[2].pressure, groundTruthPoint.pressure);
+ EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp);
+}
+
+TEST(GenerateCircularArcGroundTruthTest, StraightLineUpwards) {
+ const std::vector<GroundTruthPoint> groundTruthPoints = generateCircularArcGroundTruthPoints(
+ /*initialPosition=*/Eigen::Vector2f(0, 0),
+ /*initialAngle=*/M_PI,
+ /*velocity=*/1.0f,
+ /*turningAngle=*/0.0f,
+ /*numPoints=*/3);
+
+ ASSERT_EQ(3u, groundTruthPoints.size());
+ EXPECT_THAT(groundTruthPoints[0].position, Vector2fNear(Eigen::Vector2f(0, 0), 1e-6));
+ EXPECT_THAT(groundTruthPoints[1].position, Vector2fNear(Eigen::Vector2f(-1, 0), 1e-6));
+ EXPECT_THAT(groundTruthPoints[2].position, Vector2fNear(Eigen::Vector2f(-2, 0), 1e-6));
+ // Check that timestamps are increasing between consecutive ground truth points.
+ EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp);
+ EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp);
+}
+
+TEST(GenerateCircularArcGroundTruthTest, CounterclockwiseSquare) {
+ // Generate points in a counterclockwise unit square starting pointing right.
+ const std::vector<GroundTruthPoint> groundTruthPoints = generateCircularArcGroundTruthPoints(
+ /*initialPosition=*/Eigen::Vector2f(10, 100),
+ /*initialAngle=*/M_PI_2,
+ /*velocity=*/1.0f,
+ /*turningAngle=*/M_PI_2,
+ /*numPoints=*/5);
+
+ ASSERT_EQ(5u, groundTruthPoints.size());
+ EXPECT_THAT(groundTruthPoints[0].position, Vector2fNear(Eigen::Vector2f(10, 100), 1e-6));
+ EXPECT_THAT(groundTruthPoints[1].position, Vector2fNear(Eigen::Vector2f(10, 101), 1e-6));
+ EXPECT_THAT(groundTruthPoints[2].position, Vector2fNear(Eigen::Vector2f(9, 101), 1e-6));
+ EXPECT_THAT(groundTruthPoints[3].position, Vector2fNear(Eigen::Vector2f(9, 100), 1e-6));
+ EXPECT_THAT(groundTruthPoints[4].position, Vector2fNear(Eigen::Vector2f(10, 100), 1e-6));
+}
+
+// --- Prediction-generation helper functions. ---
+
+// Creates a sequence of predictions with values equal to those of the given GroundTruthPoint.
+std::vector<PredictionPoint> generateConstantPredictions(const GroundTruthPoint& groundTruthPoint) {
+ std::vector<PredictionPoint> predictions;
+ nsecs_t predictionTimestamp = groundTruthPoint.timestamp + TEST_PREDICTION_INTERVAL_NANOS;
+ for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) {
+ predictions.push_back(PredictionPoint{{.position = groundTruthPoint.position,
+ .pressure = groundTruthPoint.pressure},
+ .originTimestamp = groundTruthPoint.timestamp,
+ .targetTimestamp = predictionTimestamp});
+ predictionTimestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ }
+ return predictions;
+}
+
+// Generates TEST_MAX_NUM_PREDICTIONS predictions from the given most recent two ground truth points
+// by linear extrapolation of position and pressure. The interval between consecutive predictions'
+// timestamps is TEST_PREDICTION_INTERVAL_NANOS.
+std::vector<PredictionPoint> generatePredictionsByLinearExtrapolation(
+ const GroundTruthPoint& firstGroundTruth, const GroundTruthPoint& secondGroundTruth) {
+ // Precompute deltas.
+ const Eigen::Vector2f trajectory = secondGroundTruth.position - firstGroundTruth.position;
+ const float deltaPressure = secondGroundTruth.pressure - firstGroundTruth.pressure;
+ // Compute predictions.
+ std::vector<PredictionPoint> predictions;
+ Eigen::Vector2f predictionPosition = secondGroundTruth.position;
+ float predictionPressure = secondGroundTruth.pressure;
+ nsecs_t predictionTargetTimestamp = secondGroundTruth.timestamp;
+ for (size_t i = 0; i < TEST_MAX_NUM_PREDICTIONS; ++i) {
+ predictionPosition += trajectory;
+ predictionPressure += deltaPressure;
+ predictionTargetTimestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ predictions.push_back(
+ PredictionPoint{{.position = predictionPosition, .pressure = predictionPressure},
+ .originTimestamp = secondGroundTruth.timestamp,
+ .targetTimestamp = predictionTargetTimestamp});
+ }
+ return predictions;
+}
+
+TEST(GeneratePredictionsTest, GenerateConstantPredictions) {
+ const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const std::vector<PredictionPoint> predictionPoints =
+ generateConstantPredictions(groundTruthPoint);
+
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size());
+ for (size_t i = 0; i < predictionPoints.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ EXPECT_THAT(predictionPoints[i].position, Vector2fNear(groundTruthPoint.position, 1e-6));
+ EXPECT_THAT(predictionPoints[i].pressure, FloatNear(groundTruthPoint.pressure, 1e-6));
+ EXPECT_EQ(predictionPoints[i].originTimestamp, groundTruthPoint.timestamp);
+ EXPECT_EQ(predictionPoints[i].targetTimestamp,
+ groundTruthPoint.timestamp +
+ static_cast<nsecs_t>(i + 1) * TEST_PREDICTION_INTERVAL_NANOS);
+ }
+}
+
+TEST(GeneratePredictionsTest, LinearExtrapolationFromTwoPoints) {
+ const nsecs_t initialTimestamp = TEST_INITIAL_TIMESTAMP;
+ const std::vector<PredictionPoint> predictionPoints = generatePredictionsByLinearExtrapolation(
+ GroundTruthPoint{{.position = Eigen::Vector2f(100, 200), .pressure = 0.9f},
+ .timestamp = initialTimestamp},
+ GroundTruthPoint{{.position = Eigen::Vector2f(105, 190), .pressure = 0.8f},
+ .timestamp = initialTimestamp + TEST_PREDICTION_INTERVAL_NANOS});
+
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size());
+ const nsecs_t originTimestamp = initialTimestamp + TEST_PREDICTION_INTERVAL_NANOS;
+ EXPECT_THAT(predictionPoints[0],
+ PredictionPointNear(PredictionPoint{{.position = Eigen::Vector2f(110, 180),
+ .pressure = 0.7f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp +
+ TEST_PREDICTION_INTERVAL_NANOS},
+ 0.001));
+ EXPECT_THAT(predictionPoints[1],
+ PredictionPointNear(PredictionPoint{{.position = Eigen::Vector2f(115, 170),
+ .pressure = 0.6f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp +
+ 2 * TEST_PREDICTION_INTERVAL_NANOS},
+ 0.001));
+ EXPECT_THAT(predictionPoints[2],
+ PredictionPointNear(PredictionPoint{{.position = Eigen::Vector2f(120, 160),
+ .pressure = 0.5f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp +
+ 3 * TEST_PREDICTION_INTERVAL_NANOS},
+ 0.001));
+ EXPECT_THAT(predictionPoints[3],
+ PredictionPointNear(PredictionPoint{{.position = Eigen::Vector2f(125, 150),
+ .pressure = 0.4f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp +
+ 4 * TEST_PREDICTION_INTERVAL_NANOS},
+ 0.001));
+ EXPECT_THAT(predictionPoints[4],
+ PredictionPointNear(PredictionPoint{{.position = Eigen::Vector2f(130, 140),
+ .pressure = 0.3f},
+ .originTimestamp = originTimestamp,
+ .targetTimestamp = originTimestamp +
+ 5 * TEST_PREDICTION_INTERVAL_NANOS},
+ 0.001));
+}
+
+// Generates predictions by linear extrapolation for each consecutive pair of ground truth points
+// (see the comment for the above function for further explanation). Returns a vector of vectors of
+// prediction points, where the first index is the source ground truth index, and the second is the
+// prediction target index.
+//
+// The returned vector has size equal to the input vector, and the first element of the returned
+// vector is always empty.
+std::vector<std::vector<PredictionPoint>> generateAllPredictionsByLinearExtrapolation(
+ const std::vector<GroundTruthPoint>& groundTruthPoints) {
+ std::vector<std::vector<PredictionPoint>> allPredictions;
+ allPredictions.emplace_back();
+ for (size_t i = 1; i < groundTruthPoints.size(); ++i) {
+ allPredictions.push_back(generatePredictionsByLinearExtrapolation(groundTruthPoints[i - 1],
+ groundTruthPoints[i]));
+ }
+ return allPredictions;
+}
+
+TEST(GeneratePredictionsTest, GenerateAllPredictions) {
+ const nsecs_t initialTimestamp = TEST_INITIAL_TIMESTAMP;
+ std::vector<GroundTruthPoint>
+ groundTruthPoints{GroundTruthPoint{{.position = Eigen::Vector2f(0, 0),
+ .pressure = 0.5f},
+ .timestamp = initialTimestamp},
+ GroundTruthPoint{{.position = Eigen::Vector2f(1, -1),
+ .pressure = 0.51f},
+ .timestamp = initialTimestamp +
+ 2 * TEST_PREDICTION_INTERVAL_NANOS},
+ GroundTruthPoint{{.position = Eigen::Vector2f(2, -2),
+ .pressure = 0.52f},
+ .timestamp = initialTimestamp +
+ 3 * TEST_PREDICTION_INTERVAL_NANOS}};
+
+ const std::vector<std::vector<PredictionPoint>> allPredictions =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ // Check format of allPredictions data.
+ ASSERT_EQ(groundTruthPoints.size(), allPredictions.size());
+ EXPECT_TRUE(allPredictions[0].empty());
+ EXPECT_EQ(TEST_MAX_NUM_PREDICTIONS, allPredictions[1].size());
+ EXPECT_EQ(TEST_MAX_NUM_PREDICTIONS, allPredictions[2].size());
+
+ // Check positions of predictions generated from first pair of ground truth points.
+ EXPECT_THAT(allPredictions[1][0].position, Vector2fNear(Eigen::Vector2f(2, -2), 1e-9));
+ EXPECT_THAT(allPredictions[1][1].position, Vector2fNear(Eigen::Vector2f(3, -3), 1e-9));
+ EXPECT_THAT(allPredictions[1][2].position, Vector2fNear(Eigen::Vector2f(4, -4), 1e-9));
+ EXPECT_THAT(allPredictions[1][3].position, Vector2fNear(Eigen::Vector2f(5, -5), 1e-9));
+ EXPECT_THAT(allPredictions[1][4].position, Vector2fNear(Eigen::Vector2f(6, -6), 1e-9));
+
+ // Check pressures of predictions generated from first pair of ground truth points.
+ EXPECT_FLOAT_EQ(0.52f, allPredictions[1][0].pressure);
+ EXPECT_FLOAT_EQ(0.53f, allPredictions[1][1].pressure);
+ EXPECT_FLOAT_EQ(0.54f, allPredictions[1][2].pressure);
+ EXPECT_FLOAT_EQ(0.55f, allPredictions[1][3].pressure);
+ EXPECT_FLOAT_EQ(0.56f, allPredictions[1][4].pressure);
+}
+
+// --- Prediction error helper functions. ---
+
+struct GeneralPositionErrors {
+ float alongTrajectoryErrorMean;
+ float alongTrajectoryErrorStd;
+ float offTrajectoryRmse;
+};
+
+// Inputs:
+// • Vector of ground truth points
+// • Vector of vectors of prediction points, where the first index is the source ground truth
+// index, and the second is the prediction target index.
+//
+// Returns a vector of GeneralPositionErrors, indexed by prediction time delta bucket.
+std::vector<GeneralPositionErrors> computeGeneralPositionErrors(
+ const std::vector<GroundTruthPoint>& groundTruthPoints,
+ const std::vector<std::vector<PredictionPoint>>& predictionPoints) {
+ // Aggregate errors by time bucket (prediction target index).
+ std::vector<GeneralPositionErrors> generalPostitionErrors;
+ for (size_t predictionTargetIndex = 0; predictionTargetIndex < TEST_MAX_NUM_PREDICTIONS;
+ ++predictionTargetIndex) {
+ std::vector<float> alongTrajectoryErrors;
+ std::vector<float> alongTrajectorySquaredErrors;
+ std::vector<float> offTrajectoryErrors;
+ for (size_t sourceGroundTruthIndex = 1; sourceGroundTruthIndex < groundTruthPoints.size();
+ ++sourceGroundTruthIndex) {
+ const size_t targetGroundTruthIndex =
+ sourceGroundTruthIndex + predictionTargetIndex + 1;
+ // Only include errors for points with a ground truth value.
+ if (targetGroundTruthIndex < groundTruthPoints.size()) {
+ const Eigen::Vector2f trajectory =
+ (groundTruthPoints[targetGroundTruthIndex].position -
+ groundTruthPoints[targetGroundTruthIndex - 1].position)
+ .normalized();
+ const Eigen::Vector2f orthogonalTrajectory =
+ Eigen::Rotation2Df(M_PI_2) * trajectory;
+ const Eigen::Vector2f positionError =
+ predictionPoints[sourceGroundTruthIndex][predictionTargetIndex].position -
+ groundTruthPoints[targetGroundTruthIndex].position;
+ alongTrajectoryErrors.push_back(positionError.dot(trajectory));
+ alongTrajectorySquaredErrors.push_back(alongTrajectoryErrors.back() *
+ alongTrajectoryErrors.back());
+ offTrajectoryErrors.push_back(positionError.dot(orthogonalTrajectory));
+ }
+ }
+ generalPostitionErrors.push_back(
+ {.alongTrajectoryErrorMean = average(alongTrajectoryErrors),
+ .alongTrajectoryErrorStd = standardDeviation(alongTrajectoryErrors),
+ .offTrajectoryRmse = rmse(offTrajectoryErrors)});
+ }
+ return generalPostitionErrors;
+}
+
+// Inputs:
+// • Vector of ground truth points
+// • Vector of vectors of prediction points, where the first index is the source ground truth
+// index, and the second is the prediction target index.
+//
+// Returns a vector of pressure RMSEs, indexed by prediction time delta bucket.
+std::vector<float> computePressureRmses(
+ const std::vector<GroundTruthPoint>& groundTruthPoints,
+ const std::vector<std::vector<PredictionPoint>>& predictionPoints) {
+ // Aggregate errors by time bucket (prediction target index).
+ std::vector<float> pressureRmses;
+ for (size_t predictionTargetIndex = 0; predictionTargetIndex < TEST_MAX_NUM_PREDICTIONS;
+ ++predictionTargetIndex) {
+ std::vector<float> pressureErrors;
+ for (size_t sourceGroundTruthIndex = 1; sourceGroundTruthIndex < groundTruthPoints.size();
+ ++sourceGroundTruthIndex) {
+ const size_t targetGroundTruthIndex =
+ sourceGroundTruthIndex + predictionTargetIndex + 1;
+ // Only include errors for points with a ground truth value.
+ if (targetGroundTruthIndex < groundTruthPoints.size()) {
+ pressureErrors.push_back(
+ predictionPoints[sourceGroundTruthIndex][predictionTargetIndex].pressure -
+ groundTruthPoints[targetGroundTruthIndex].pressure);
+ }
+ }
+ pressureRmses.push_back(rmse(pressureErrors));
+ }
+ return pressureRmses;
+}
+
+TEST(ErrorComputationHelperTest, ComputeGeneralPositionErrorsSimpleTest) {
+ std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(GroundTruthPoint{{.position = Eigen::Vector2f(0, 0),
+ .pressure = 0.0f},
+ .timestamp = TEST_INITIAL_TIMESTAMP},
+ /*numPoints=*/TEST_MAX_NUM_PREDICTIONS + 2);
+ groundTruthPoints[3].position = Eigen::Vector2f(1, 0);
+ groundTruthPoints[4].position = Eigen::Vector2f(1, 1);
+ groundTruthPoints[5].position = Eigen::Vector2f(1, 3);
+ groundTruthPoints[6].position = Eigen::Vector2f(2, 3);
+
+ std::vector<std::vector<PredictionPoint>> predictionPoints =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ // The generated predictions look like:
+ //
+ // | Source | Target Ground Truth Index |
+ // | Index | 2 | 3 | 4 | 5 | 6 |
+ // |------------|--------|--------|--------|--------|--------|
+ // | 1 | (0, 0) | (0, 0) | (0, 0) | (0, 0) | (0, 0) |
+ // | 2 | | (0, 0) | (0, 0) | (0, 0) | (0, 0) |
+ // | 3 | | | (2, 0) | (3, 0) | (4, 0) |
+ // | 4 | | | | (1, 2) | (1, 3) |
+ // | 5 | | | | | (1, 5) |
+ // |---------------------------------------------------------|
+ // | Actual Ground Truth Values |
+ // | Position | (0, 0) | (1, 0) | (1, 1) | (1, 3) | (2, 3) |
+ // | Previous | (0, 0) | (0, 0) | (1, 0) | (1, 1) | (1, 3) |
+ //
+ // Note: this table organizes prediction targets by target ground truth index. Metrics are
+ // aggregated across points with the same prediction time bucket index, which is different.
+ // Each down-right diagonal from this table gives us points from a unique time bucket.
+
+ // Initialize expected prediction errors from the table above. The first time bucket corresponds
+ // to the long diagonal of the table, and subsequent time buckets step up-right from there.
+ const std::vector<std::vector<float>> expectedAlongTrajectoryErrors{{0, -1, -1, -1, -1},
+ {-1, -1, -3, -1},
+ {-1, -3, 2},
+ {-3, -2},
+ {-2}};
+ const std::vector<std::vector<float>> expectedOffTrajectoryErrors{{0, 0, 1, 0, 2},
+ {0, 1, 2, 0},
+ {1, 1, 3},
+ {1, 3},
+ {3}};
+
+ std::vector<GeneralPositionErrors> generalPositionErrors =
+ computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
+
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, generalPositionErrors.size());
+ for (size_t i = 0; i < generalPositionErrors.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ EXPECT_FLOAT_EQ(average(expectedAlongTrajectoryErrors[i]),
+ generalPositionErrors[i].alongTrajectoryErrorMean);
+ EXPECT_FLOAT_EQ(standardDeviation(expectedAlongTrajectoryErrors[i]),
+ generalPositionErrors[i].alongTrajectoryErrorStd);
+ EXPECT_FLOAT_EQ(rmse(expectedOffTrajectoryErrors[i]),
+ generalPositionErrors[i].offTrajectoryRmse);
+ }
+}
+
+TEST(ErrorComputationHelperTest, ComputePressureRmsesSimpleTest) {
+ // Generate ground truth points with pressures {0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5}.
+ // (We need TEST_MAX_NUM_PREDICTIONS + 2 to test all prediction time buckets.)
+ std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(GroundTruthPoint{{.position = Eigen::Vector2f(0, 0),
+ .pressure = 0.0f},
+ .timestamp = TEST_INITIAL_TIMESTAMP},
+ /*numPoints=*/TEST_MAX_NUM_PREDICTIONS + 2);
+ for (size_t i = 4; i < groundTruthPoints.size(); ++i) {
+ groundTruthPoints[i].pressure = 0.5f;
+ }
+
+ std::vector<std::vector<PredictionPoint>> predictionPoints =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ std::vector<float> pressureRmses = computePressureRmses(groundTruthPoints, predictionPoints);
+
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, pressureRmses.size());
+ EXPECT_FLOAT_EQ(rmse(std::vector<float>{0.0f, 0.0f, -0.5f, 0.5f, 0.0f}), pressureRmses[0]);
+ EXPECT_FLOAT_EQ(rmse(std::vector<float>{0.0f, -0.5f, -0.5f, 1.0f}), pressureRmses[1]);
+ EXPECT_FLOAT_EQ(rmse(std::vector<float>{-0.5f, -0.5f, -0.5f}), pressureRmses[2]);
+ EXPECT_FLOAT_EQ(rmse(std::vector<float>{-0.5f, -0.5f}), pressureRmses[3]);
+ EXPECT_FLOAT_EQ(rmse(std::vector<float>{-0.5f}), pressureRmses[4]);
+}
+
+// --- MotionPredictorMetricsManager tests. ---
+
+// Helper function that instantiates a MetricsManager with the given mock logged AtomFields. Takes
+// vectors of ground truth and prediction points of the same length, and passes these points to the
+// MetricsManager. The format of these vectors is expected to be:
+// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
+// • predictionPoints: the first index points to a vector of predictions corresponding to the
+// source ground truth point with the same index.
+// - The first element should be empty, because there are not expected to be predictions until
+// we have received 2 ground truth points.
+// - The last element may be empty, because there will be no future ground truth points to
+// associate with those predictions (if not empty, it will be ignored).
+// - To test all prediction buckets, there should be at least TEST_MAX_NUM_PREDICTIONS non-empty
+// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
+// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
+//
+// The passed-in outAtomFields will contain the logged AtomFields when the function returns.
+//
+// This function returns void so that it can use test assertions.
+void runMetricsManager(const std::vector<GroundTruthPoint>& groundTruthPoints,
+ const std::vector<std::vector<PredictionPoint>>& predictionPoints,
+ std::vector<AtomFields>& outAtomFields) {
+ MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
+ TEST_MAX_NUM_PREDICTIONS);
+ metricsManager.setMockLoggedAtomFields(&outAtomFields);
+
+ // Validate structure of groundTruthPoints and predictionPoints.
+ ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
+ ASSERT_GE(groundTruthPoints.size(), 2u);
+ ASSERT_EQ(predictionPoints[0].size(), 0u);
+ for (size_t i = 1; i + 1 < predictionPoints.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ ASSERT_EQ(predictionPoints[i].size(), TEST_MAX_NUM_PREDICTIONS);
+ }
+
+ // Pass ground truth points and predictions (for all except first and last ground truth).
+ for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
+ metricsManager.onRecord(makeMotionEvent(groundTruthPoints[i]));
+ if ((i > 0) && (i + 1 < predictionPoints.size())) {
+ metricsManager.onPredict(makeMotionEvent(predictionPoints[i]));
+ }
+ }
+ // Send a stroke-end event to trigger the logging call.
+ metricsManager.onRecord(makeLiftMotionEvent());
+}
+
+// Vacuous test:
+// • Input: no prediction data.
+// • Expectation: no metrics should be logged.
+TEST(MotionPredictorMetricsManagerTest, NoPredictions) {
+ std::vector<AtomFields> mockLoggedAtomFields;
+ MotionPredictorMetricsManager metricsManager(TEST_PREDICTION_INTERVAL_NANOS,
+ TEST_MAX_NUM_PREDICTIONS);
+ metricsManager.setMockLoggedAtomFields(&mockLoggedAtomFields);
+
+ metricsManager.onRecord(makeMotionEvent(
+ GroundTruthPoint{{.position = Eigen::Vector2f(0, 0), .pressure = 0}, .timestamp = 0}));
+ metricsManager.onRecord(makeLiftMotionEvent());
+
+ // Check that mockLoggedAtomFields is still empty (as it was initialized empty), ensuring that
+ // no metrics were logged.
+ EXPECT_EQ(0u, mockLoggedAtomFields.size());
+}
+
+// Perfect predictions test:
+// • Input: constant input events, perfect predictions matching the input events.
+// • Expectation: all error metrics should be zero, or NO_DATA_SENTINEL for "unreported" metrics.
+// (For example, scale-invariant errors are only reported for the final time bucket.)
+TEST(MotionPredictorMetricsManagerTest, ConstantGroundTruthPerfectPredictions) {
+ GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+
+ // Generate ground truth and prediction points as described by the runMetricsManager comment.
+ std::vector<GroundTruthPoint> groundTruthPoints;
+ std::vector<std::vector<PredictionPoint>> predictionPoints;
+ for (size_t i = 0; i < TEST_MAX_NUM_PREDICTIONS + 2; ++i) {
+ groundTruthPoints.push_back(groundTruthPoint);
+ predictionPoints.push_back(i > 0 ? generateConstantPredictions(groundTruthPoint)
+ : std::vector<PredictionPoint>{});
+ groundTruthPoint.timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ }
+
+ std::vector<AtomFields> atomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ // Check that errors are all zero, or NO_DATA_SENTINEL for unreported metrics.
+ for (size_t i = 0; i < atomFields.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ const AtomFields& atom = atomFields[i];
+ const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
+ EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
+ // General errors: reported for every time bucket.
+ EXPECT_EQ(0, atom.alongTrajectoryErrorMeanMillipixels);
+ EXPECT_EQ(0, atom.alongTrajectoryErrorStdMillipixels);
+ EXPECT_EQ(0, atom.offTrajectoryRmseMillipixels);
+ EXPECT_EQ(0, atom.pressureRmseMilliunits);
+ // High-velocity errors: reported only for the last two time buckets.
+ // However, this data has zero velocity, so these metrics should all be NO_DATA_SENTINEL.
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityAlongTrajectoryRmse);
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityOffTrajectoryRmse);
+ // Scale-invariant errors: reported only for the last time bucket.
+ if (i + 1 == atomFields.size()) {
+ EXPECT_EQ(0, atom.scaleInvariantAlongTrajectoryRmse);
+ EXPECT_EQ(0, atom.scaleInvariantOffTrajectoryRmse);
+ } else {
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.scaleInvariantAlongTrajectoryRmse);
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.scaleInvariantOffTrajectoryRmse);
+ }
+ }
+}
+
+TEST(MotionPredictorMetricsManagerTest, QuadraticPressureLinearPredictions) {
+ // Generate ground truth points.
+ //
+ // Ground truth pressures are a quadratically increasing function from some initial value.
+ const float initialPressure = 0.5f;
+ const float quadraticCoefficient = 0.01f;
+ std::vector<GroundTruthPoint> groundTruthPoints;
+ nsecs_t timestamp = TEST_INITIAL_TIMESTAMP;
+ // As described in the runMetricsManager comment, we should have TEST_MAX_NUM_PREDICTIONS + 2
+ // ground truth points.
+ for (size_t i = 0; i < TEST_MAX_NUM_PREDICTIONS + 2; ++i) {
+ const float pressure = initialPressure + quadraticCoefficient * static_cast<float>(i * i);
+ groundTruthPoints.push_back(
+ GroundTruthPoint{{.position = Eigen::Vector2f(0, 0), .pressure = pressure},
+ .timestamp = timestamp});
+ timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ }
+
+ // Note: the first index is the source ground truth index, and the second is the prediction
+ // target index.
+ std::vector<std::vector<PredictionPoint>> predictionPoints =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ const std::vector<float> pressureErrors =
+ computePressureRmses(groundTruthPoints, predictionPoints);
+
+ // Run test.
+ std::vector<AtomFields> atomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+
+ // Check logged metrics match expectations.
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ for (size_t i = 0; i < atomFields.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ const AtomFields& atom = atomFields[i];
+ // Check time bucket delta matches expectation based on index and prediction interval.
+ const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
+ EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
+ // Check pressure error matches expectation.
+ EXPECT_NEAR(static_cast<int>(1000 * pressureErrors[i]), atom.pressureRmseMilliunits, 1);
+ }
+}
+
+TEST(MotionPredictorMetricsManagerTest, QuadraticPositionLinearPredictionsGeneralErrors) {
+ // Generate ground truth points.
+ //
+ // Each component of the ground truth positions are an independent quadratically increasing
+ // function from some initial value.
+ const Eigen::Vector2f initialPosition(200, 300);
+ const Eigen::Vector2f quadraticCoefficients(-2, 3);
+ std::vector<GroundTruthPoint> groundTruthPoints;
+ nsecs_t timestamp = TEST_INITIAL_TIMESTAMP;
+ // As described in the runMetricsManager comment, we should have TEST_MAX_NUM_PREDICTIONS + 2
+ // ground truth points.
+ for (size_t i = 0; i < TEST_MAX_NUM_PREDICTIONS + 2; ++i) {
+ const Eigen::Vector2f position =
+ initialPosition + quadraticCoefficients * static_cast<float>(i * i);
+ groundTruthPoints.push_back(
+ GroundTruthPoint{{.position = position, .pressure = 0.5}, .timestamp = timestamp});
+ timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ }
+
+ // Note: the first index is the source ground truth index, and the second is the prediction
+ // target index.
+ std::vector<std::vector<PredictionPoint>> predictionPoints =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ std::vector<GeneralPositionErrors> generalPositionErrors =
+ computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
+
+ // Run test.
+ std::vector<AtomFields> atomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+
+ // Check logged metrics match expectations.
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ for (size_t i = 0; i < atomFields.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ const AtomFields& atom = atomFields[i];
+ // Check time bucket delta matches expectation based on index and prediction interval.
+ const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
+ EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
+ // Check general position errors match expectation.
+ EXPECT_NEAR(static_cast<int>(1000 * generalPositionErrors[i].alongTrajectoryErrorMean),
+ atom.alongTrajectoryErrorMeanMillipixels, 1);
+ EXPECT_NEAR(static_cast<int>(1000 * generalPositionErrors[i].alongTrajectoryErrorStd),
+ atom.alongTrajectoryErrorStdMillipixels, 1);
+ EXPECT_NEAR(static_cast<int>(1000 * generalPositionErrors[i].offTrajectoryRmse),
+ atom.offTrajectoryRmseMillipixels, 1);
+ }
+}
+
+// Counterclockwise regular octagonal section test:
+// • Input – ground truth: constantly-spaced input events starting at a trajectory pointing exactly
+// rightwards, and rotating by 45° counterclockwise after each input.
+// • Input – predictions: simple linear extrapolations of previous two ground truth points.
+//
+// The code below uses the following terminology to distinguish references to ground truth events:
+// • Source ground truth: the most recent ground truth point received at the time the prediction
+// was made.
+// • Target ground truth: the ground truth event that the prediction was attempting to match.
+TEST(MotionPredictorMetricsManagerTest, CounterclockwiseOctagonGroundTruthLinearPredictions) {
+ // Select a stroke velocity that exceeds the high-velocity threshold of 1100 px/sec.
+ // For an input rate of 240 hz, 1100 px/sec * (1/240) sec/input ≈ 4.58 pixels per input.
+ const float strokeVelocity = 10; // pixels per input
+
+ // As described in the runMetricsManager comment, we should have TEST_MAX_NUM_PREDICTIONS + 2
+ // ground truth points.
+ std::vector<GroundTruthPoint> groundTruthPoints = generateCircularArcGroundTruthPoints(
+ /*initialPosition=*/Eigen::Vector2f(100, 100),
+ /*initialAngle=*/M_PI_2,
+ /*velocity=*/strokeVelocity,
+ /*turningAngle=*/-M_PI_4,
+ /*numPoints=*/TEST_MAX_NUM_PREDICTIONS + 2);
+
+ std::vector<std::vector<PredictionPoint>> predictionPoints =
+ generateAllPredictionsByLinearExtrapolation(groundTruthPoints);
+
+ std::vector<GeneralPositionErrors> generalPositionErrors =
+ computeGeneralPositionErrors(groundTruthPoints, predictionPoints);
+
+ // Run test.
+ std::vector<AtomFields> atomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, atomFields);
+
+ // Check logged metrics match expectations.
+ ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, atomFields.size());
+ for (size_t i = 0; i < atomFields.size(); ++i) {
+ SCOPED_TRACE(testing::Message() << "i = " << i);
+ const AtomFields& atom = atomFields[i];
+ const nsecs_t deltaTimeBucketNanos = TEST_PREDICTION_INTERVAL_NANOS * (i + 1);
+ EXPECT_EQ(deltaTimeBucketNanos / NANOS_PER_MILLIS, atom.deltaTimeBucketMilliseconds);
+
+ // General errors: reported for every time bucket.
+ EXPECT_NEAR(static_cast<int>(1000 * generalPositionErrors[i].alongTrajectoryErrorMean),
+ atom.alongTrajectoryErrorMeanMillipixels, 1);
+ // We allow for some floating point error in standard deviation (0.02 pixels).
+ EXPECT_NEAR(1000 * generalPositionErrors[i].alongTrajectoryErrorStd,
+ atom.alongTrajectoryErrorStdMillipixels, 20);
+ // All position errors are equal, so the standard deviation should be approximately zero.
+ EXPECT_NEAR(0, atom.alongTrajectoryErrorStdMillipixels, 20);
+ // Absolute value for RMSE, since it must be non-negative.
+ EXPECT_NEAR(static_cast<int>(1000 * generalPositionErrors[i].offTrajectoryRmse),
+ atom.offTrajectoryRmseMillipixels, 1);
+
+ // High-velocity errors: reported only for the last two time buckets.
+ //
+ // Since our input stroke velocity is chosen to be above the high-velocity threshold, all
+ // data contributes to high-velocity errors, and thus high-velocity errors should be equal
+ // to general errors (where reported).
+ //
+ // As above, use absolute value for RMSE, since it must be non-negative.
+ if (i + 2 >= atomFields.size()) {
+ EXPECT_NEAR(static_cast<int>(
+ 1000 * std::abs(generalPositionErrors[i].alongTrajectoryErrorMean)),
+ atom.highVelocityAlongTrajectoryRmse, 1);
+ EXPECT_NEAR(static_cast<int>(1000 *
+ std::abs(generalPositionErrors[i].offTrajectoryRmse)),
+ atom.highVelocityOffTrajectoryRmse, 1);
+ } else {
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityAlongTrajectoryRmse);
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.highVelocityOffTrajectoryRmse);
+ }
+
+ // Scale-invariant errors: reported only for the last time bucket, where the reported value
+ // is the aggregation across all time buckets.
+ //
+ // The MetricsManager stores mMaxNumPredictions recent ground truth segments. Our ground
+ // truth segments here all have a length of strokeVelocity, so we can convert general errors
+ // to scale-invariant errors by dividing by `strokeVelocty * TEST_MAX_NUM_PREDICTIONS`.
+ //
+ // As above, use absolute value for RMSE, since it must be non-negative.
+ if (i + 1 == atomFields.size()) {
+ const float pathLength = strokeVelocity * TEST_MAX_NUM_PREDICTIONS;
+ std::vector<float> alongTrajectoryAbsoluteErrors;
+ std::vector<float> offTrajectoryAbsoluteErrors;
+ for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) {
+ alongTrajectoryAbsoluteErrors.push_back(
+ std::abs(generalPositionErrors[j].alongTrajectoryErrorMean));
+ offTrajectoryAbsoluteErrors.push_back(
+ std::abs(generalPositionErrors[j].offTrajectoryRmse));
+ }
+ EXPECT_NEAR(static_cast<int>(1000 * average(alongTrajectoryAbsoluteErrors) /
+ pathLength),
+ atom.scaleInvariantAlongTrajectoryRmse, 1);
+ EXPECT_NEAR(static_cast<int>(1000 * average(offTrajectoryAbsoluteErrors) / pathLength),
+ atom.scaleInvariantOffTrajectoryRmse, 1);
+ } else {
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.scaleInvariantAlongTrajectoryRmse);
+ EXPECT_EQ(NO_DATA_SENTINEL, atom.scaleInvariantOffTrajectoryRmse);
+ }
+ }
+}
+
+} // namespace
+} // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index ffebff1..1c8ec90 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -278,6 +278,11 @@
const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions,
std::optional<float> targetVelocity) {
checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity);
+ // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall
+ // back to a strategy that supports differential axes.
+ checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions,
+ AMOTION_EVENT_AXIS_SCROLL),
+ targetVelocity);
}
static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions,
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 0128859..275b7a4 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -38,10 +38,10 @@
namespace android {
// Macros for including the SurfaceTexture name in log messages
-#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
-#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
static const struct {
uint32_t width, height;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index cf16739..32b229d 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -19,7 +19,7 @@
#include <surfacetexture/SurfaceTexture.h>
// Macro for including the SurfaceTexture name in log messages
-#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__)
namespace android {
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index d3d4cba..9f610e1 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -26,10 +26,10 @@
namespace android {
// Macros for including the SurfaceTexture name in log messages
-#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
-#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__)
static const mat4 mtxIdentity;
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index edaa422..e158f01 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1060,6 +1060,42 @@
ANATIVEWINDOW_FRAME_RATE_MIN
};
+/*
+ * Frame rate category values that can be used in Transaction::setFrameRateCategory.
+ */
+enum {
+ /**
+ * Default value. This value can also be set to return to default behavior, such as layers
+ * without animations.
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT = 0,
+
+ /**
+ * The layer will explicitly not influence the frame rate.
+ * This may indicate a frame rate suitable for no animation updates (such as a cursor blinking
+ * or a sporadic update).
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE = 1,
+
+ /**
+ * Indicates a frame rate suitable for animations that looks fine even if played at a low frame
+ * rate.
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_LOW = 2,
+
+ /**
+ * Indicates a middle frame rate suitable for animations that do not require higher frame
+ * rates, or do not benefit from high smoothness. This is normally 60 Hz or close to it.
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL = 3,
+
+ /**
+ * Indicates a frame rate suitable for animations that require a high frame rate, which may
+ * increase smoothness but may also increase power usage.
+ */
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4
+};
+
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 8d19c45..ba2eb7d 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -56,30 +56,8 @@
filegroup {
name: "librenderengine_sources",
srcs: [
- "Description.cpp",
"ExternalTexture.cpp",
- "Mesh.cpp",
"RenderEngine.cpp",
- "Texture.cpp",
- ],
-}
-
-filegroup {
- name: "librenderengine_gl_sources",
- srcs: [
- "gl/GLESRenderEngine.cpp",
- "gl/GLExtensions.cpp",
- "gl/GLFramebuffer.cpp",
- "gl/GLImage.cpp",
- "gl/GLShadowTexture.cpp",
- "gl/GLShadowVertexGenerator.cpp",
- "gl/GLSkiaShadowPort.cpp",
- "gl/GLVertexBuffer.cpp",
- "gl/ImageManager.cpp",
- "gl/Program.cpp",
- "gl/ProgramCache.cpp",
- "gl/filters/BlurFilter.cpp",
- "gl/filters/GenericProgram.cpp",
],
}
@@ -96,6 +74,7 @@
"skia/AutoBackendTexture.cpp",
"skia/Cache.cpp",
"skia/ColorSpaces.cpp",
+ "skia/GLExtensions.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
"skia/SkiaVkRenderEngine.cpp",
@@ -136,7 +115,6 @@
],
srcs: [
":librenderengine_sources",
- ":librenderengine_gl_sources",
":librenderengine_threaded_sources",
":librenderengine_skia_sources",
],
@@ -155,8 +133,6 @@
name: "librenderengine_mocks",
defaults: ["librenderengine_defaults"],
srcs: [
- "mock/Framebuffer.cpp",
- "mock/Image.cpp",
"mock/RenderEngine.cpp",
],
static_libs: [
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
deleted file mode 100644
index 245c9e1..0000000
--- a/libs/renderengine/Description.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/private/Description.h>
-
-#include <stdint.h>
-
-#include <utils/TypeHelpers.h>
-
-namespace android {
-namespace renderengine {
-
-Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
- ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
- switch (transfer) {
- case ui::Dataspace::TRANSFER_ST2084:
- return Description::TransferFunction::ST2084;
- case ui::Dataspace::TRANSFER_HLG:
- return Description::TransferFunction::HLG;
- case ui::Dataspace::TRANSFER_LINEAR:
- return Description::TransferFunction::LINEAR;
- default:
- return Description::TransferFunction::SRGB;
- }
-}
-
-bool Description::hasInputTransformMatrix() const {
- const mat4 identity;
- return inputTransformMatrix != identity;
-}
-
-bool Description::hasOutputTransformMatrix() const {
- const mat4 identity;
- return outputTransformMatrix != identity;
-}
-
-bool Description::hasColorMatrix() const {
- const mat4 identity;
- return colorMatrix != identity;
-}
-
-bool Description::hasDisplayColorMatrix() const {
- const mat4 identity;
- return displayColorMatrix != identity;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp
index 9eb42cd..6f2a96a 100644
--- a/libs/renderengine/ExternalTexture.cpp
+++ b/libs/renderengine/ExternalTexture.cpp
@@ -27,14 +27,6 @@
: mBuffer(buffer), mRenderEngine(renderEngine), mWritable(usage & WRITEABLE) {
LOG_ALWAYS_FATAL_IF(buffer == nullptr,
"Attempted to bind a null buffer to an external texture!");
- // GLESRenderEngine has a separate texture cache for output buffers,
- if (usage == WRITEABLE &&
- (mRenderEngine.getRenderEngineType() ==
- renderengine::RenderEngine::RenderEngineType::GLES ||
- mRenderEngine.getRenderEngineType() ==
- renderengine::RenderEngine::RenderEngineType::THREADED)) {
- return;
- }
mRenderEngine.mapExternalTextureBuffer(mBuffer, mWritable);
}
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
deleted file mode 100644
index ed2f45f..0000000
--- a/libs/renderengine/Mesh.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/Mesh.h>
-
-#include <utils/Log.h>
-
-namespace android {
-namespace renderengine {
-
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
- size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
- size_t indexCount)
- : mVertexCount(vertexCount),
- mVertexSize(vertexSize),
- mTexCoordsSize(texCoordSize),
- mCropCoordsSize(cropCoordsSize),
- mShadowColorSize(shadowColorSize),
- mShadowParamsSize(shadowParamsSize),
- mPrimitive(primitive),
- mIndexCount(indexCount) {
- if (vertexCount == 0) {
- mVertices.resize(1);
- mVertices[0] = 0.0f;
- mStride = 0;
- return;
- }
- size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
- size_t remainder = (stride * vertexCount) / vertexCount;
- // Since all of the input parameters are unsigned, if stride is less than
- // either vertexSize or texCoordSize, it must have overflowed. remainder
- // will be equal to stride as long as stride * vertexCount doesn't overflow.
- if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
- texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
- mVertices.resize(1);
- mVertices[0] = 0.0f;
- mVertexCount = 0;
- mVertexSize = 0;
- mTexCoordsSize = 0;
- mCropCoordsSize = 0;
- mShadowColorSize = 0;
- mShadowParamsSize = 0;
- mStride = 0;
- return;
- }
-
- mVertices.resize(stride * vertexCount);
- mStride = stride;
- mIndices.resize(indexCount);
-}
-
-Mesh::Primitive Mesh::getPrimitive() const {
- return mPrimitive;
-}
-
-float const* Mesh::getPositions() const {
- return mVertices.data();
-}
-float* Mesh::getPositions() {
- return mVertices.data();
-}
-
-float const* Mesh::getTexCoords() const {
- return mVertices.data() + mVertexSize;
-}
-float* Mesh::getTexCoords() {
- return mVertices.data() + mVertexSize;
-}
-
-float const* Mesh::getCropCoords() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize;
-}
-float* Mesh::getCropCoords() {
- return mVertices.data() + mVertexSize + mTexCoordsSize;
-}
-
-float const* Mesh::getShadowColor() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
-}
-float* Mesh::getShadowColor() {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
-}
-
-float const* Mesh::getShadowParams() const {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
-}
-float* Mesh::getShadowParams() {
- return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
-}
-
-uint16_t const* Mesh::getIndices() const {
- return mIndices.data();
-}
-
-uint16_t* Mesh::getIndices() {
- return mIndices.data();
-}
-
-size_t Mesh::getVertexCount() const {
- return mVertexCount;
-}
-
-size_t Mesh::getVertexSize() const {
- return mVertexSize;
-}
-
-size_t Mesh::getTexCoordsSize() const {
- return mTexCoordsSize;
-}
-
-size_t Mesh::getShadowColorSize() const {
- return mShadowColorSize;
-}
-
-size_t Mesh::getShadowParamsSize() const {
- return mShadowParamsSize;
-}
-
-size_t Mesh::getByteStride() const {
- return mStride * sizeof(float);
-}
-
-size_t Mesh::getStride() const {
- return mStride;
-}
-
-size_t Mesh::getIndexCount() const {
- return mIndexCount;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index d08c221..3e1ac33 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -18,7 +18,6 @@
#include <cutils/properties.h>
#include <log/log.h>
-#include "gl/GLESRenderEngine.h"
#include "renderengine/ExternalTexture.h"
#include "threaded/RenderEngineThreaded.h"
@@ -30,11 +29,6 @@
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
switch (args.renderEngineType) {
- case RenderEngineType::THREADED:
- ALOGD("Threaded RenderEngine with GLES Backend");
- return renderengine::threaded::RenderEngineThreaded::create(
- [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
- args.renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
@@ -56,10 +50,6 @@
return android::renderengine::skia::SkiaVkRenderEngine::create(args);
},
args.renderEngineType);
- case RenderEngineType::GLES:
- default:
- ALOGD("RenderEngine with GLES Backend");
- return renderengine::gl::GLESRenderEngine::create(args);
}
}
@@ -78,13 +68,11 @@
ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
base::unique_fd&& bufferFence) {
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
updateProtectedContext(layers, buffer);
- drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
- std::move(bufferFence));
+ drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence));
return resultFuture;
}
diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp
deleted file mode 100644
index 154cde8..0000000
--- a/libs/renderengine/Texture.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2013 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 <renderengine/Texture.h>
-
-namespace android {
-namespace renderengine {
-
-Texture::Texture()
- : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
-
-Texture::Texture(Target textureTarget, uint32_t textureName)
- : mTextureName(textureName),
- mTextureTarget(textureTarget),
- mWidth(0),
- mHeight(0),
- mFiltering(false) {}
-
-void Texture::init(Target textureTarget, uint32_t textureName) {
- mTextureName = textureName;
- mTextureTarget = textureTarget;
-}
-
-Texture::~Texture() {}
-
-void Texture::setMatrix(float const* matrix) {
- mTextureMatrix = mat4(matrix);
-}
-
-void Texture::setFiltering(bool enabled) {
- mFiltering = enabled;
-}
-
-void Texture::setDimensions(size_t width, size_t height) {
- mWidth = width;
- mHeight = height;
-}
-
-uint32_t Texture::getTextureName() const {
- return mTextureName;
-}
-
-uint32_t Texture::getTextureTarget() const {
- return mTextureTarget;
-}
-
-const mat4& Texture::getMatrix() const {
- return mTextureMatrix;
-}
-
-bool Texture::getFiltering() const {
- return mFiltering;
-}
-
-size_t Texture::getWidth() const {
- return mWidth;
-}
-
-size_t Texture::getHeight() const {
- return mHeight;
-}
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index bd7b617..a7f1df9 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -43,10 +43,6 @@
return "skiavk";
case RenderEngine::RenderEngineType::SKIA_VK_THREADED:
return "skiavkthreaded";
- case RenderEngine::RenderEngineType::GLES:
- case RenderEngine::RenderEngineType::THREADED:
- LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
- return "unused";
}
}
@@ -108,10 +104,6 @@
return std::pair<uint32_t, uint32_t>(width, height);
}
-// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove
-// GLESRenderEngine we can remove this, too.
-static constexpr const bool kUseFrameBufferCache = false;
-
static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
auto args = RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
@@ -121,7 +113,6 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setRenderEngineType(type)
- .setUseColorManagerment(true)
.build();
return RenderEngine::create(args);
}
@@ -173,10 +164,7 @@
};
auto layers = std::vector<LayerSettings>{layer};
- sp<Fence> waitFence =
- re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd())
- .get()
- .value();
+ sp<Fence> waitFence = re.drawLayers(display, layers, texture, base::unique_fd()).get().value();
waitFence->waitForever(LOG_TAG);
return texture;
}
@@ -205,10 +193,8 @@
// This loop starts and stops the timer.
for (auto _ : benchState) {
- sp<Fence> waitFence = re.drawLayers(display, layers, outputBuffer, kUseFrameBufferCache,
- base::unique_fd())
- .get()
- .value();
+ sp<Fence> waitFence =
+ re.drawLayers(display, layers, outputBuffer, base::unique_fd()).get().value();
waitFence->waitForever(LOG_TAG);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
deleted file mode 100644
index e7a2c7a..0000000
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ /dev/null
@@ -1,1860 +0,0 @@
-/*
- * Copyright 2013 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_NDEBUG 0
-#include "EGL/egl.h"
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <sched.h>
-#include <cmath>
-#include <fstream>
-#include <sstream>
-#include <unordered_set>
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <android-base/stringprintf.h>
-#include <cutils/compiler.h>
-#include <cutils/properties.h>
-#include <gui/DebugEGLImageTracker.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
-#include <renderengine/private/Description.h>
-#include <sync/sync.h>
-#include <ui/ColorSpace.h>
-#include <ui/DebugUtils.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <utils/KeyedVector.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "GLExtensions.h"
-#include "GLFramebuffer.h"
-#include "GLImage.h"
-#include "GLShadowVertexGenerator.h"
-#include "Program.h"
-#include "ProgramCache.h"
-#include "filters/BlurFilter.h"
-
-bool checkGlError(const char* op, int lineNumber) {
- bool errorFound = false;
- GLint error = glGetError();
- while (error != GL_NO_ERROR) {
- errorFound = true;
- error = glGetError();
- ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
- }
- return errorFound;
-}
-
-static constexpr bool outputDebugPPMs = false;
-
-void writePPM(const char* basename, GLuint width, GLuint height) {
- ALOGV("writePPM #%s: %d x %d", basename, width, height);
-
- std::vector<GLubyte> pixels(width * height * 4);
- std::vector<GLubyte> outBuffer(width * height * 3);
-
- // TODO(courtneygo): We can now have float formats, need
- // to remove this code or update to support.
- // Make returned pixels fit in uint32_t, one byte per component
- glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
- if (checkGlError(__FUNCTION__, __LINE__)) {
- return;
- }
-
- std::string filename(basename);
- filename.append(".ppm");
- std::ofstream file(filename.c_str(), std::ios::binary);
- if (!file.is_open()) {
- ALOGE("Unable to open file: %s", filename.c_str());
- ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
- "surfaceflinger to write debug images");
- return;
- }
-
- file << "P6\n";
- file << width << "\n";
- file << height << "\n";
- file << 255 << "\n";
-
- auto ptr = reinterpret_cast<char*>(pixels.data());
- auto outPtr = reinterpret_cast<char*>(outBuffer.data());
- for (int y = height - 1; y >= 0; y--) {
- char* data = ptr + y * width * sizeof(uint32_t);
-
- for (GLuint x = 0; x < width; x++) {
- // Only copy R, G and B components
- outPtr[0] = data[0];
- outPtr[1] = data[1];
- outPtr[2] = data[2];
- data += sizeof(uint32_t);
- outPtr += 3;
- }
- }
- file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
-}
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class BindNativeBufferAsFramebuffer {
-public:
- BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer,
- const bool useFramebufferCache)
- : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) {
- mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(),
- useFramebufferCache)
- ? mEngine.bindFrameBuffer(mFramebuffer)
- : NO_MEMORY;
- }
- ~BindNativeBufferAsFramebuffer() {
- mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true);
- mEngine.unbindFrameBuffer(mFramebuffer);
- }
- status_t getStatus() const { return mStatus; }
-
-private:
- GLESRenderEngine& mEngine;
- Framebuffer* mFramebuffer;
- status_t mStatus;
-};
-
-using base::StringAppendF;
-using ui::Dataspace;
-
-static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
- EGLint wanted, EGLConfig* outConfig) {
- EGLint numConfigs = -1, n = 0;
- eglGetConfigs(dpy, nullptr, 0, &numConfigs);
- std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
- eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
- configs.resize(n);
-
- if (!configs.empty()) {
- if (attribute != EGL_NONE) {
- for (EGLConfig config : configs) {
- EGLint value = 0;
- eglGetConfigAttrib(dpy, config, attribute, &value);
- if (wanted == value) {
- *outConfig = config;
- return NO_ERROR;
- }
- }
- } else {
- // just pick the first one
- *outConfig = configs[0];
- return NO_ERROR;
- }
- }
-
- return NAME_NOT_FOUND;
-}
-
-static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
- EGLConfig* config) {
- // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
- // it is to be used with WIFI displays
- status_t err;
- EGLint wantedAttribute;
- EGLint wantedAttributeValue;
-
- std::vector<EGLint> attribs;
- if (renderableType) {
- const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format);
- const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102;
-
- // Default to 8 bits per channel.
- const EGLint tmpAttribs[] = {
- EGL_RENDERABLE_TYPE,
- renderableType,
- EGL_RECORDABLE_ANDROID,
- EGL_TRUE,
- EGL_SURFACE_TYPE,
- EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
- EGL_FRAMEBUFFER_TARGET_ANDROID,
- EGL_TRUE,
- EGL_RED_SIZE,
- is1010102 ? 10 : 8,
- EGL_GREEN_SIZE,
- is1010102 ? 10 : 8,
- EGL_BLUE_SIZE,
- is1010102 ? 10 : 8,
- EGL_ALPHA_SIZE,
- is1010102 ? 2 : 8,
- EGL_NONE,
- };
- std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)),
- std::back_inserter(attribs));
- wantedAttribute = EGL_NONE;
- wantedAttributeValue = EGL_NONE;
- } else {
- // if no renderable type specified, fallback to a simplified query
- wantedAttribute = EGL_NATIVE_VISUAL_ID;
- wantedAttributeValue = format;
- }
-
- err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue,
- config);
- if (err == NO_ERROR) {
- EGLint caveat;
- if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
- ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
- }
-
- return err;
-}
-
-std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority(
- const RenderEngineCreationArgs& args) {
- if (!GLExtensions::getInstance().hasContextPriority()) {
- return std::nullopt;
- }
-
- switch (args.contextPriority) {
- case RenderEngine::ContextPriority::REALTIME:
- if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
- return RenderEngine::ContextPriority::REALTIME;
- } else {
- ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
- return RenderEngine::ContextPriority::HIGH;
- }
- case RenderEngine::ContextPriority::HIGH:
- case RenderEngine::ContextPriority::MEDIUM:
- case RenderEngine::ContextPriority::LOW:
- return args.contextPriority;
- default:
- return std::nullopt;
- }
-}
-
-std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
- // initialize EGL for the default display
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (!eglInitialize(display, nullptr, nullptr)) {
- LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError());
- }
-
- const auto eglVersion = eglQueryString(display, EGL_VERSION);
- if (!eglVersion) {
- checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed");
- }
-
- // Use the Android impl to grab EGL_NV_context_priority_realtime
- const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS);
- if (!eglExtensions) {
- checkGlError(__FUNCTION__, __LINE__);
- LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
- }
-
- GLExtensions& extensions = GLExtensions::getInstance();
- extensions.initWithEGLStrings(eglVersion, eglExtensions);
-
- // The code assumes that ES2 or later is available if this extension is
- // supported.
- EGLConfig config = EGL_NO_CONFIG;
- if (!extensions.hasNoConfigContext()) {
- config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
- }
-
- const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args);
- EGLContext protectedContext = EGL_NO_CONTEXT;
- if (args.enableProtectedContext && extensions.hasProtectedContent()) {
- protectedContext =
- createEglContext(display, config, nullptr, priority, Protection::PROTECTED);
- ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
- }
-
- EGLContext ctxt =
- createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED);
-
- // if can't create a GL context, we can only abort.
- LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
-
- EGLSurface stub = EGL_NO_SURFACE;
- if (!extensions.hasSurfacelessContext()) {
- stub = createStubEglPbufferSurface(display, config, args.pixelFormat,
- Protection::UNPROTECTED);
- LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer");
- }
- EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt);
- LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current");
- extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
- glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
-
- EGLSurface protectedStub = EGL_NO_SURFACE;
- if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
- protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat,
- Protection::PROTECTED);
- ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer");
- }
-
- // now figure out what version of GL did we actually get
- GlesVersion version = parseGlesVersion(extensions.getVersion());
-
- LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
- "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
-
- // initialize the renderer while GL is current
- std::unique_ptr<GLESRenderEngine> engine;
- switch (version) {
- case GLES_VERSION_1_0:
- case GLES_VERSION_1_1:
- LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
- break;
- case GLES_VERSION_2_0:
- case GLES_VERSION_3_0:
- engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub,
- protectedContext, protectedStub);
- break;
- }
-
- ALOGI("OpenGL ES informations:");
- ALOGI("vendor : %s", extensions.getVendor());
- ALOGI("renderer : %s", extensions.getRenderer());
- ALOGI("version : %s", extensions.getVersion());
- ALOGI("extensions: %s", extensions.getExtensions());
- ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
- ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
- return engine;
-}
-
-EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
- status_t err;
- EGLConfig config;
-
- // First try to get an ES3 config
- err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config);
- if (err != NO_ERROR) {
- // If ES3 fails, try to get an ES2 config
- err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
- if (err != NO_ERROR) {
- // If ES2 still doesn't work, probably because we're on the emulator.
- // try a simplified query
- ALOGW("no suitable EGLConfig found, trying a simpler query");
- err = selectEGLConfig(display, format, 0, &config);
- if (err != NO_ERROR) {
- // this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
- }
- }
- }
-
- if (logConfig) {
- // print some debugging info
- EGLint r, g, b, a;
- eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
- eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
- eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
- eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
- ALOGI("EGL information:");
- ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
- ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
- ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
- ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
- ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
- }
-
- return config;
-}
-
-GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
- EGLConfig config, EGLContext ctxt, EGLSurface stub,
- EGLContext protectedContext, EGLSurface protectedStub)
- : RenderEngine(args.renderEngineType),
- mEGLDisplay(display),
- mEGLConfig(config),
- mEGLContext(ctxt),
- mStubSurface(stub),
- mProtectedEGLContext(protectedContext),
- mProtectedStubSurface(protectedStub),
- mVpWidth(0),
- mVpHeight(0),
- mFramebufferImageCacheSize(args.imageCacheSize),
- mUseColorManagement(args.useColorManagement),
- mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) {
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
- glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
-
- // Initialize protected EGL Context.
- if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface,
- mProtectedEGLContext);
- ALOGE_IF(!success, "can't make protected context current");
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glPixelStorei(GL_PACK_ALIGNMENT, 4);
- success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext);
- LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
- }
-
- // mColorBlindnessCorrection = M;
-
- if (mUseColorManagement) {
- const ColorSpace srgb(ColorSpace::sRGB());
- const ColorSpace displayP3(ColorSpace::DisplayP3());
- const ColorSpace bt2020(ColorSpace::BT2020());
-
- // no chromatic adaptation needed since all color spaces use D65 for their white points.
- mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
- mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
- mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
- mXyzToSrgb = mat4(srgb.getXYZtoRGB());
- mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
- mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
-
- // Compute sRGB to Display P3 and BT2020 transform matrix.
- // NOTE: For now, we are limiting output wide color space support to
- // Display-P3 and BT2020 only.
- mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz;
- mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz;
-
- // Compute Display P3 to sRGB and BT2020 transform matrix.
- mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz;
- mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz;
-
- // Compute BT2020 to sRGB and Display P3 transform matrix
- mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz;
- mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz;
- }
-
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.traceGpuCompletion", value, "0");
- if (atoi(value)) {
- mTraceGpuCompletion = true;
- mFlushTracer = std::make_unique<FlushTracer>(this);
- }
-
- if (args.supportsBackgroundBlur) {
- mBlurFilter = new BlurFilter(*this);
- checkErrors("BlurFilter creation");
- }
-
- mImageManager = std::make_unique<ImageManager>(this);
- mImageManager->initThread();
- mDrawingBuffer = createFramebuffer();
- sp<GraphicBuffer> buf =
- sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBA_8888, 1,
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE,
- "placeholder");
-
- const status_t err = buf->initCheck();
- if (err != OK) {
- ALOGE("Error allocating placeholder buffer: %d", err);
- return;
- }
- mPlaceholderBuffer = buf.get();
- EGLint attributes[] = {
- EGL_NONE,
- };
- mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- mPlaceholderBuffer, attributes);
- ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x",
- eglGetError());
-
- mShadowTexture = std::make_unique<GLShadowTexture>();
-}
-
-GLESRenderEngine::~GLESRenderEngine() {
- // Destroy the image manager first.
- mImageManager = nullptr;
- mShadowTexture = nullptr;
- cleanFramebufferCache();
- ProgramCache::getInstance().purgeCaches();
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- glDisableVertexAttribArray(Program::position);
- unbindFrameBuffer(mDrawingBuffer.get());
- mDrawingBuffer = nullptr;
- eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage);
- mImageCache.clear();
- if (mStubSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mStubSurface);
- }
- if (mProtectedStubSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mProtectedStubSurface);
- }
- if (mEGLContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEGLDisplay, mEGLContext);
- }
- if (mProtectedEGLContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEGLDisplay, mProtectedEGLContext);
- }
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(mEGLDisplay);
- eglReleaseThread();
-}
-
-std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() {
- return std::make_unique<GLFramebuffer>(*this);
-}
-
-std::unique_ptr<Image> GLESRenderEngine::createImage() {
- return std::make_unique<GLImage>(*this);
-}
-
-Framebuffer* GLESRenderEngine::getFramebufferForDrawing() {
- return mDrawingBuffer.get();
-}
-
-std::future<void> GLESRenderEngine::primeCache() {
- ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- mUseColorManagement, mPrecacheToneMapperShaderOnly);
- return {};
-}
-
-base::unique_fd GLESRenderEngine::flush() {
- ATRACE_CALL();
- if (!GLExtensions::getInstance().hasNativeFenceSync()) {
- return base::unique_fd();
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
- return base::unique_fd();
- }
-
- // native fence fd will not be populated until flush() is done.
- glFlush();
-
- // get the fence fd
- base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
- }
-
- // Only trace if we have a valid fence, as current usage falls back to
- // calling finish() if the fence fd is invalid.
- if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) {
- mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
- }
-
- return fenceFd;
-}
-
-bool GLESRenderEngine::finish() {
- ATRACE_CALL();
- if (!GLExtensions::getInstance().hasFenceSync()) {
- ALOGW("no synchronization support");
- return false;
- }
-
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGW("failed to create EGL fence sync: %#x", eglGetError());
- return false;
- }
-
- if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) {
- mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr));
- }
-
- return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR);
-}
-
-bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) {
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (result != EGL_CONDITION_SATISFIED_KHR) {
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("fence wait timed out");
- } else {
- ALOGW("error waiting on EGL fence: %#x", error);
- }
- return false;
- }
-
- return true;
-}
-
-bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) {
- if (!GLExtensions::getInstance().hasNativeFenceSync() ||
- !GLExtensions::getInstance().hasWaitSync()) {
- return false;
- }
-
- // release the fd and transfer the ownership to EGLSync
- EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE};
- EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
- if (sync == EGL_NO_SYNC_KHR) {
- ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
- return false;
- }
-
- // XXX: The spec draft is inconsistent as to whether this should return an
- // EGLint or void. Ignore the return value for now, as it's not strictly
- // needed.
- eglWaitSyncKHR(mEGLDisplay, sync, 0);
- EGLint error = eglGetError();
- eglDestroySyncKHR(mEGLDisplay, sync);
- if (error != EGL_SUCCESS) {
- ALOGE("failed to wait for EGL native fence sync: %#x", error);
- return false;
- }
-
- return true;
-}
-
-void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
- ATRACE_CALL();
- glDisable(GL_BLEND);
- glClearColor(red, green, blue, alpha);
- glClear(GL_COLOR_BUFFER_BIT);
-}
-
-void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue,
- float alpha) {
- size_t c;
- Rect const* r = region.getArray(&c);
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLES)
- .setVertices(c * 6 /* count */, 2 /* size */)
- .build();
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- for (size_t i = 0; i < c; i++, r++) {
- position[i * 6 + 0].x = r->left;
- position[i * 6 + 0].y = r->top;
- position[i * 6 + 1].x = r->left;
- position[i * 6 + 1].y = r->bottom;
- position[i * 6 + 2].x = r->right;
- position[i * 6 + 2].y = r->bottom;
- position[i * 6 + 3].x = r->left;
- position[i * 6 + 3].y = r->top;
- position[i * 6 + 4].x = r->right;
- position[i * 6 + 4].y = r->bottom;
- position[i * 6 + 5].x = r->right;
- position[i * 6 + 5].y = r->top;
- }
- setupFillWithColor(red, green, blue, alpha);
- drawMesh(mesh);
-}
-
-void GLESRenderEngine::setScissor(const Rect& region) {
- glScissor(region.left, region.top, region.getWidth(), region.getHeight());
- glEnable(GL_SCISSOR_TEST);
-}
-
-void GLESRenderEngine::disableScissor() {
- glDisable(GL_SCISSOR_TEST);
-}
-
-void GLESRenderEngine::genTextures(size_t count, uint32_t* names) {
- glGenTextures(count, names);
-}
-
-void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) {
- for (int i = 0; i < count; ++i) {
- mTextureView.erase(names[i]);
- }
- glDeleteTextures(count, names);
-}
-
-void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
- ATRACE_CALL();
- const GLImage& glImage = static_cast<const GLImage&>(image);
- const GLenum target = GL_TEXTURE_EXTERNAL_OES;
-
- glBindTexture(target, texName);
- if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
- glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
- }
-}
-
-void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& bufferFence) {
- ATRACE_CALL();
-
- bool found = false;
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- auto cachedImage = mImageCache.find(buffer->getId());
- found = (cachedImage != mImageCache.end());
- }
-
- // If we couldn't find the image in the cache at this time, then either
- // SurfaceFlinger messed up registering the buffer ahead of time or we got
- // backed up creating other EGLImages.
- if (!found) {
- status_t cacheResult = mImageManager->cache(buffer);
- if (cacheResult != NO_ERROR) {
- ALOGE("Error with caching buffer: %d", cacheResult);
- return;
- }
- }
-
- // Whether or not we needed to cache, re-check mImageCache to make sure that
- // there's an EGLImage. The current threading model guarantees that we don't
- // destroy a cached image until it's really not needed anymore (i.e. this
- // function should not be called), so the only possibility is that something
- // terrible went wrong and we should just bind something and move on.
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- auto cachedImage = mImageCache.find(buffer->getId());
-
- if (cachedImage == mImageCache.end()) {
- // We failed creating the image if we got here, so bail out.
- ALOGE("Failed to create an EGLImage when rendering");
- bindExternalTextureImage(texName, *createImage());
- return;
- }
-
- bindExternalTextureImage(texName, *cachedImage->second);
- mTextureView.insert_or_assign(texName, buffer->getId());
- }
-
- // Wait for the new buffer to be ready.
- if (bufferFence != nullptr && bufferFence->isValid()) {
- if (GLExtensions::getInstance().hasWaitSync()) {
- base::unique_fd fenceFd(bufferFence->dup());
- if (fenceFd == -1) {
- ALOGE("error dup'ing fence fd: %d", errno);
- return;
- }
- if (!waitFence(std::move(fenceFd))) {
- ALOGE("failed to wait on fence fd");
- return;
- }
- } else {
- status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer");
- if (err != NO_ERROR) {
- ALOGE("error waiting for fence: %d", err);
- return;
- }
- }
- }
-
- return;
-}
-
-void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
- bool /*isRenderable*/) {
- ATRACE_CALL();
- mImageManager->cacheAsync(buffer, nullptr);
-}
-
-std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting(
- const sp<GraphicBuffer>& buffer) {
- auto barrier = std::make_shared<ImageManager::Barrier>();
- mImageManager->cacheAsync(buffer, barrier);
- return barrier;
-}
-
-status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) {
- if (buffer == nullptr) {
- return BAD_VALUE;
- }
-
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (mImageCache.count(buffer->getId()) > 0) {
- // If there's already an image then fail fast here.
- return NO_ERROR;
- }
- }
- ATRACE_CALL();
-
- // Create the image without holding a lock so that we don't block anything.
- std::unique_ptr<Image> newImage = createImage();
-
- bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
- buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
- if (!created) {
- ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
- buffer->getUsage(), buffer->getPixelFormat());
- return NO_INIT;
- }
-
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- if (mImageCache.count(buffer->getId()) > 0) {
- // In theory it's possible for another thread to recache the image,
- // so bail out if another thread won.
- return NO_ERROR;
- }
- mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage)));
- }
-
- return NO_ERROR;
-}
-
-void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) {
- mImageManager->releaseAsync(buffer->getId(), nullptr);
-}
-
-std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting(
- uint64_t bufferId) {
- auto barrier = std::make_shared<ImageManager::Barrier>();
- mImageManager->releaseAsync(bufferId, barrier);
- return barrier;
-}
-
-void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) {
- std::unique_ptr<Image> image;
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- const auto& cachedImage = mImageCache.find(bufferId);
-
- if (cachedImage != mImageCache.end()) {
- ALOGV("Destroying image for buffer: %" PRIu64, bufferId);
- // Move the buffer out of cache first, so that we can destroy
- // without holding the cache's lock.
- image = std::move(cachedImage->second);
- mImageCache.erase(bufferId);
- return;
- }
- }
- ALOGV("Failed to find image for buffer: %" PRIu64, bufferId);
-}
-
-int GLESRenderEngine::getContextPriority() {
- int value;
- eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value);
- return value;
-}
-
-FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) {
- // Translate win by the rounded corners rect coordinates, to have all values in
- // layer coordinate space.
- FloatRect cropWin = layer.geometry.boundaries;
- const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop;
- cropWin.left -= roundedCornersCrop.left;
- cropWin.right -= roundedCornersCrop.left;
- cropWin.top -= roundedCornersCrop.top;
- cropWin.bottom -= roundedCornersCrop.top;
- Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>());
- cropCoords[0] = vec2(cropWin.left, cropWin.top);
- cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight());
- cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight());
- cropCoords[3] = vec2(cropWin.right, cropWin.top);
-
- setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight());
- return cropWin;
-}
-
-void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
- const LayerSettings& layer, const Mesh& mesh) {
- // We separate the layer into 3 parts essentially, such that we only turn on blending for the
- // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
- FloatRect bounds = layer.geometry.roundedCornersCrop;
-
- // Explicitly compute the transform from the clip rectangle to the physical
- // display. Normally, this is done in glViewport but we explicitly compute
- // it here so that we can get the scissor bounds correct.
- const Rect& source = display.clip;
- const Rect& destination = display.physicalDisplay;
- // Here we compute the following transform:
- // 1. Translate the top left corner of the source clip to (0, 0)
- // 2. Rotate the clip rectangle about the origin in accordance with the
- // orientation flag
- // 3. Translate the top left corner back to the origin.
- // 4. Scale the clip rectangle to the destination rectangle dimensions
- // 5. Translate the top left corner to the destination rectangle's top left
- // corner.
- const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1));
- mat4 rotation;
- int displacementX = 0;
- int displacementY = 0;
- float destinationWidth = static_cast<float>(destination.getWidth());
- float destinationHeight = static_cast<float>(destination.getHeight());
- float sourceWidth = static_cast<float>(source.getWidth());
- float sourceHeight = static_cast<float>(source.getHeight());
- const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
- switch (display.orientation) {
- case ui::Transform::ROT_90:
- rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
- displacementX = source.getHeight();
- std::swap(sourceHeight, sourceWidth);
- break;
- case ui::Transform::ROT_180:
- rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
- displacementY = source.getHeight();
- displacementX = source.getWidth();
- break;
- case ui::Transform::ROT_270:
- rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
- displacementY = source.getWidth();
- std::swap(sourceHeight, sourceWidth);
- break;
- default:
- break;
- }
-
- const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1));
- const mat4 scale = mat4::scale(
- vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1));
- const mat4 translateDestination =
- mat4::translate(vec4(destination.left, destination.top, 0, 1));
- const mat4 globalTransform =
- translateDestination * scale * intermediateTranslation * rotation * translateSource;
-
- const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform;
- const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
- const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
- const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
- const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
- bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
- std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]),
- std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]),
- std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]));
-
- // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners
- // and the middle part without rounded corners.
- const int32_t radius = ceil(
- (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0);
- const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
- setScissor(topRect);
- drawMesh(mesh);
- const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
- setScissor(bottomRect);
- drawMesh(mesh);
-
- // The middle part of the layer can turn off blending.
- if (topRect.bottom < bottomRect.top) {
- const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
- bounds.bottom - radius);
- setScissor(middleRect);
- mState.cornerRadius = 0.0;
- disableBlending();
- drawMesh(mesh);
- }
- disableScissor();
-}
-
-status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
- ATRACE_CALL();
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
- EGLImageKHR eglImage = glFramebuffer->getEGLImage();
- uint32_t textureName = glFramebuffer->getTextureName();
- uint32_t framebufferName = glFramebuffer->getFramebufferName();
-
- // Bind the texture and turn our EGLImage into a texture
- glBindTexture(GL_TEXTURE_2D, textureName);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
-
- // Bind the Framebuffer to render into
- glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
-
- uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
- glStatus);
-
- return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
-}
-
-void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
- ATRACE_CALL();
-
- // back to main framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-bool GLESRenderEngine::canSkipPostRenderCleanup() const {
- return mPriorResourcesCleaned ||
- (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled);
-}
-
-void GLESRenderEngine::cleanupPostRender() {
- ATRACE_CALL();
-
- if (canSkipPostRenderCleanup()) {
- // If we don't have a prior frame needing cleanup, then don't do anything.
- return;
- }
-
- // Bind the texture to placeholder so that backing image data can be freed.
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
- glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
-
- // Release the cached fence here, so that we don't churn reallocations when
- // we could no-op repeated calls of this method instead.
- mLastDrawFence = nullptr;
- mPriorResourcesCleaned = true;
-}
-
-void GLESRenderEngine::cleanFramebufferCache() {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- // Bind the texture to placeholder so that backing image data can be freed.
- GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
- glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
-
- while (!mFramebufferImageCache.empty()) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
-}
-
-void GLESRenderEngine::checkErrors() const {
- checkErrors(nullptr);
-}
-
-void GLESRenderEngine::checkErrors(const char* tag) const {
- do {
- // there could be more than one error flag
- GLenum error = glGetError();
- if (error == GL_NO_ERROR) break;
- if (tag == nullptr) {
- ALOGE("GL error 0x%04x", int(error));
- } else {
- ALOGE("GL error: %s -> 0x%04x", tag, int(error));
- }
- } while (true);
-}
-
-bool GLESRenderEngine::supportsProtectedContent() const {
- return mProtectedEGLContext != EGL_NO_CONTEXT;
-}
-
-void GLESRenderEngine::useProtectedContext(bool useProtectedContext) {
- if (useProtectedContext == mInProtectedContext ||
- (useProtectedContext && !supportsProtectedContent())) {
- return;
- }
-
- const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface;
- const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext;
- if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) {
- mInProtectedContext = useProtectedContext;
- }
-}
-EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
- bool isProtected,
- bool useFramebufferCache) {
- sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
- if (useFramebufferCache) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- for (const auto& image : mFramebufferImageCache) {
- if (image.first == graphicBuffer->getId()) {
- return image.second;
- }
- }
- }
- EGLint attributes[] = {
- isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
- isProtected ? EGL_TRUE : EGL_NONE,
- EGL_NONE,
- };
- EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- nativeBuffer, attributes);
- if (useFramebufferCache) {
- if (image != EGL_NO_IMAGE_KHR) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) {
- EGLImageKHR expired = mFramebufferImageCache.front().second;
- mFramebufferImageCache.pop_front();
- eglDestroyImageKHR(mEGLDisplay, expired);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
- mFramebufferImageCache.push_back({graphicBuffer->getId(), image});
- }
- }
-
- if (image != EGL_NO_IMAGE_KHR) {
- DEBUG_EGL_IMAGE_TRACKER_CREATE();
- }
- return image;
-}
-
-void GLESRenderEngine::drawLayersInternal(
- const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) {
- ATRACE_CALL();
- if (layers.empty()) {
- ALOGV("Drawing empty layer stack");
- resultPromise->set_value(Fence::NO_FENCE);
- return;
- }
-
- if (bufferFence.get() >= 0) {
- // Duplicate the fence for passing to waitFence.
- base::unique_fd bufferFenceDup(dup(bufferFence.get()));
- if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
- ATRACE_NAME("Waiting before draw");
- sync_wait(bufferFence.get(), -1);
- }
- }
-
- if (buffer == nullptr) {
- ALOGE("No output buffer provided. Aborting GPU composition.");
- resultPromise->set_value(base::unexpected(BAD_VALUE));
- return;
- }
-
- validateOutputBufferUsage(buffer->getBuffer());
-
- std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
- // Gathering layers that requested blur, we'll need them to decide when to render to an
- // offscreen buffer, and when to render to the native buffer.
- std::deque<const LayerSettings> blurLayers;
- if (CC_LIKELY(mBlurFilter != nullptr)) {
- for (const auto& layer : layers) {
- if (layer.backgroundBlurRadius > 0) {
- blurLayers.push_back(layer);
- }
- }
- }
- const auto blurLayersSize = blurLayers.size();
-
- if (blurLayersSize == 0) {
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
- buffer->getBuffer()
- .get()
- ->getNativeBuffer(),
- useFramebufferCache);
- if (fbo->getStatus() != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors();
- resultPromise->set_value(base::unexpected(fbo->getStatus()));
- return;
- }
- setViewportAndProjection(display.physicalDisplay, display.clip);
- } else {
- setViewportAndProjection(display.physicalDisplay, display.clip);
- auto status =
- mBlurFilter->setAsDrawTarget(display, blurLayers.front().backgroundBlurRadius);
- if (status != NO_ERROR) {
- ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors();
- resultPromise->set_value(base::unexpected(status));
- return;
- }
- }
-
- // clear the entire buffer, sometimes when we reuse buffers we'd persist
- // ghost images otherwise.
- // we also require a full transparent framebuffer for overlays. This is
- // probably not quite efficient on all GPUs, since we could filter out
- // opaque layers.
- clearWithColor(0.0, 0.0, 0.0, 0.0);
-
- setOutputDataSpace(display.outputDataspace);
- setDisplayMaxLuminance(display.maxLuminance);
- setDisplayColorTransform(display.colorTransform);
-
- const mat4 projectionMatrix =
- ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
-
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLE_FAN)
- .setVertices(4 /* count */, 2 /* size */)
- .setTexCoords(2 /* size */)
- .setCropCoords(2 /* size */)
- .build();
- for (const auto& layer : layers) {
- if (blurLayers.size() > 0 && blurLayers.front() == layer) {
- blurLayers.pop_front();
-
- auto status = mBlurFilter->prepare();
- if (status != NO_ERROR) {
- ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't render first blur pass");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
-
- if (blurLayers.size() == 0) {
- // Done blurring, time to bind the native FBO and render our blur onto it.
- fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this,
- buffer.get()
- ->getBuffer()
- ->getNativeBuffer(),
- useFramebufferCache);
- status = fbo->getStatus();
- setViewportAndProjection(display.physicalDisplay, display.clip);
- } else {
- // There's still something else to blur, so let's keep rendering to our FBO
- // instead of to the display.
- status = mBlurFilter->setAsDrawTarget(display,
- blurLayers.front().backgroundBlurRadius);
- }
- if (status != NO_ERROR) {
- ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't bind native framebuffer");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
-
- status = mBlurFilter->render(blurLayersSize > 1);
- if (status != NO_ERROR) {
- ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
- buffer->getBuffer()->handle);
- checkErrors("Can't render blur filter");
- resultPromise->set_value(base::unexpected(status));
- return;
- }
- }
-
- // Ensure luminance is at least 100 nits to avoid div-by-zero
- const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits);
- mState.maxMasteringLuminance = maxLuminance;
- mState.maxContentLuminance = maxLuminance;
- mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
-
- const FloatRect bounds = layer.geometry.boundaries;
- Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = vec2(bounds.left, bounds.top);
- position[1] = vec2(bounds.left, bounds.bottom);
- position[2] = vec2(bounds.right, bounds.bottom);
- position[3] = vec2(bounds.right, bounds.top);
-
- setupLayerCropping(layer, mesh);
- setColorTransform(layer.colorTransform);
-
- bool usePremultipliedAlpha = true;
- bool disableTexture = true;
- bool isOpaque = false;
- if (layer.source.buffer.buffer != nullptr) {
- disableTexture = false;
- isOpaque = layer.source.buffer.isOpaque;
-
- sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer();
- validateInputBufferUsage(gBuf);
- bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
- layer.source.buffer.fence);
-
- usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
- Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
- mat4 texMatrix = layer.source.buffer.textureTransform;
-
- texture.setMatrix(texMatrix.asArray());
- texture.setFiltering(layer.source.buffer.useTextureFiltering);
-
- texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
-
- renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
- texCoords[0] = vec2(0.0, 0.0);
- texCoords[1] = vec2(0.0, 1.0);
- texCoords[2] = vec2(1.0, 1.0);
- texCoords[3] = vec2(1.0, 0.0);
- setupLayerTexturing(texture);
-
- // Do not cache protected EGLImage, protected memory is limited.
- if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
- unmapExternalTextureBuffer(std::move(gBuf));
- }
- }
-
- const half3 solidColor = layer.source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
- const float radius =
- (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) /
- 2.0f;
- // Buffer sources will have a black solid color ignored in the shader,
- // so in that scenario the solid color passed here is arbitrary.
- setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius);
- if (layer.disableBlending) {
- glDisable(GL_BLEND);
- }
- setSourceDataSpace(layer.sourceDataspace);
-
- if (layer.shadow.length > 0.0f) {
- handleShadow(layer.geometry.boundaries, radius, layer.shadow);
- }
- // We only want to do a special handling for rounded corners when having rounded corners
- // is the only reason it needs to turn on blending, otherwise, we handle it like the
- // usual way since it needs to turn on blending anyway.
- else if (radius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, layer, mesh);
- } else {
- drawMesh(mesh);
- }
-
- // Cleanup if there's a buffer source
- if (layer.source.buffer.buffer != nullptr) {
- disableBlending();
- disableTexturing();
- }
- }
-
- base::unique_fd drawFence = flush();
-
- // If flush failed or we don't support native fences, we need to force the
- // gl command stream to be executed.
- if (drawFence.get() < 0) {
- bool success = finish();
- if (!success) {
- ALOGE("Failed to flush RenderEngine commands");
- checkErrors();
- // Chances are, something illegal happened (either the caller passed
- // us bad parameters, or we messed up our shader generation).
- resultPromise->set_value(base::unexpected(INVALID_OPERATION));
- return;
- }
- mLastDrawFence = nullptr;
- } else {
- // The caller takes ownership of drawFence, so we need to duplicate the
- // fd here.
- mLastDrawFence = new Fence(dup(drawFence.get()));
- }
- mPriorResourcesCleaned = false;
-
- checkErrors();
- resultPromise->set_value(sp<Fence>::make(std::move(drawFence)));
-}
-
-void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
- ATRACE_CALL();
- mVpWidth = viewport.getWidth();
- mVpHeight = viewport.getHeight();
-
- // We pass the the top left corner instead of the bottom left corner,
- // because since we're rendering off-screen first.
- glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight);
-
- mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1);
-}
-
-void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius) {
- mState.isPremultipliedAlpha = premultipliedAlpha;
- mState.isOpaque = opaque;
- mState.color = color;
- mState.cornerRadius = cornerRadius;
-
- if (disableTexture) {
- mState.textureEnabled = false;
- }
-
- if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
- glEnable(GL_BLEND);
- glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
- GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- } else {
- glDisable(GL_BLEND);
- }
-}
-
-void GLESRenderEngine::setSourceDataSpace(Dataspace source) {
- mDataSpace = source;
-}
-
-void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) {
- mOutputDataSpace = dataspace;
-}
-
-void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
- mState.displayMaxLuminance = maxLuminance;
-}
-
-void GLESRenderEngine::setupLayerTexturing(const Texture& texture) {
- GLuint target = texture.getTextureTarget();
- glBindTexture(target, texture.getTextureName());
- GLenum filter = GL_NEAREST;
- if (texture.getFiltering()) {
- filter = GL_LINEAR;
- }
- glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
- glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
-
- mState.texture = texture;
- mState.textureEnabled = true;
-}
-
-void GLESRenderEngine::setColorTransform(const mat4& colorTransform) {
- mState.colorMatrix = colorTransform;
-}
-
-void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
- mState.displayColorMatrix = colorTransform;
-}
-
-void GLESRenderEngine::disableTexturing() {
- mState.textureEnabled = false;
-}
-
-void GLESRenderEngine::disableBlending() {
- glDisable(GL_BLEND);
-}
-
-void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) {
- mState.isPremultipliedAlpha = true;
- mState.isOpaque = false;
- mState.color = half4(r, g, b, a);
- mState.textureEnabled = false;
- glDisable(GL_BLEND);
-}
-
-void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) {
- mState.cropSize = half2(width, height);
-}
-
-void GLESRenderEngine::drawMesh(const Mesh& mesh) {
- ATRACE_CALL();
- if (mesh.getTexCoordsSize()) {
- glEnableVertexAttribArray(Program::texCoords);
- glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getTexCoords());
- }
-
- glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getPositions());
-
- if (mState.cornerRadius > 0.0f) {
- glEnableVertexAttribArray(Program::cropCoords);
- glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getCropCoords());
- }
-
- if (mState.drawShadows) {
- glEnableVertexAttribArray(Program::shadowColor);
- glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getShadowColor());
-
- glEnableVertexAttribArray(Program::shadowParams);
- glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
- mesh.getByteStride(), mesh.getShadowParams());
- }
-
- Description managedState = mState;
- // By default, DISPLAY_P3 is the only supported wide color output. However,
- // when HDR content is present, hardware composer may be able to handle
- // BT2020 data space, in that case, the output data space is set to be
- // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
- // to respect this and convert non-HDR content to HDR format.
- if (mUseColorManagement) {
- Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
- Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- Dataspace outputStandard =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
- Dataspace outputTransfer =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
- bool needsXYZConversion = needsXYZTransformMatrix();
-
- // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or
- // STANDARD_BT2020, it will be treated as STANDARD_BT709
- if (inputStandard != Dataspace::STANDARD_DCI_P3 &&
- inputStandard != Dataspace::STANDARD_BT2020) {
- inputStandard = Dataspace::STANDARD_BT709;
- }
-
- if (needsXYZConversion) {
- // The supported input color spaces are standard RGB, Display P3 and BT2020.
- switch (inputStandard) {
- case Dataspace::STANDARD_DCI_P3:
- managedState.inputTransformMatrix = mDisplayP3ToXyz;
- break;
- case Dataspace::STANDARD_BT2020:
- managedState.inputTransformMatrix = mBt2020ToXyz;
- break;
- default:
- managedState.inputTransformMatrix = mSrgbToXyz;
- break;
- }
-
- // The supported output color spaces are BT2020, Display P3 and standard RGB.
- switch (outputStandard) {
- case Dataspace::STANDARD_BT2020:
- managedState.outputTransformMatrix = mXyzToBt2020;
- break;
- case Dataspace::STANDARD_DCI_P3:
- managedState.outputTransformMatrix = mXyzToDisplayP3;
- break;
- default:
- managedState.outputTransformMatrix = mXyzToSrgb;
- break;
- }
- } else if (inputStandard != outputStandard) {
- // At this point, the input data space and output data space could be both
- // HDR data spaces, but they match each other, we do nothing in this case.
- // In addition to the case above, the input data space could be
- // - scRGB linear
- // - scRGB non-linear
- // - sRGB
- // - Display P3
- // - BT2020
- // The output data spaces could be
- // - sRGB
- // - Display P3
- // - BT2020
- switch (outputStandard) {
- case Dataspace::STANDARD_BT2020:
- if (inputStandard == Dataspace::STANDARD_BT709) {
- managedState.outputTransformMatrix = mSrgbToBt2020;
- } else if (inputStandard == Dataspace::STANDARD_DCI_P3) {
- managedState.outputTransformMatrix = mDisplayP3ToBt2020;
- }
- break;
- case Dataspace::STANDARD_DCI_P3:
- if (inputStandard == Dataspace::STANDARD_BT709) {
- managedState.outputTransformMatrix = mSrgbToDisplayP3;
- } else if (inputStandard == Dataspace::STANDARD_BT2020) {
- managedState.outputTransformMatrix = mBt2020ToDisplayP3;
- }
- break;
- default:
- if (inputStandard == Dataspace::STANDARD_DCI_P3) {
- managedState.outputTransformMatrix = mDisplayP3ToSrgb;
- } else if (inputStandard == Dataspace::STANDARD_BT2020) {
- managedState.outputTransformMatrix = mBt2020ToSrgb;
- }
- break;
- }
- }
-
- // we need to convert the RGB value to linear space and convert it back when:
- // - there is a color matrix that is not an identity matrix, or
- // - there is an output transform matrix that is not an identity matrix, or
- // - the input transfer function doesn't match the output transfer function.
- if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
- inputTransfer != outputTransfer) {
- managedState.inputTransferFunction =
- Description::dataSpaceToTransferFunction(inputTransfer);
- managedState.outputTransferFunction =
- Description::dataSpaceToTransferFunction(outputTransfer);
- }
- }
-
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
- managedState);
-
- if (mState.drawShadows) {
- glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
- mesh.getIndices());
- } else {
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
- }
-
- if (mUseColorManagement && outputDebugPPMs) {
- static uint64_t managedColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << managedColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
-
- if (mesh.getTexCoordsSize()) {
- glDisableVertexAttribArray(Program::texCoords);
- }
-
- if (mState.cornerRadius > 0.0f) {
- glDisableVertexAttribArray(Program::cropCoords);
- }
-
- if (mState.drawShadows) {
- glDisableVertexAttribArray(Program::shadowColor);
- glDisableVertexAttribArray(Program::shadowParams);
- }
-}
-
-size_t GLESRenderEngine::getMaxTextureSize() const {
- return mMaxTextureSize;
-}
-
-size_t GLESRenderEngine::getMaxViewportDims() const {
- return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
-}
-
-void GLESRenderEngine::dump(std::string& result) {
- const GLExtensions& extensions = GLExtensions::getInstance();
- ProgramCache& cache = ProgramCache::getInstance();
-
- StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
- StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
- StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
- extensions.getVersion());
- StringAppendF(&result, "%s\n", extensions.getExtensions());
- StringAppendF(&result, "RenderEngine supports protected context: %d\n",
- supportsProtectedContent());
- StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext);
- StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n",
- cache.getSize(mEGLContext));
- StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n",
- cache.getSize(mProtectedEGLContext));
- StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
- {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mImageCache) {
- StringAppendF(&result, "0x%" PRIx64 "\n", id);
- }
- }
- {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n",
- mFramebufferImageCache.size());
- StringAppendF(&result, "Dumping buffer ids...\n");
- for (const auto& [id, unused] : mFramebufferImageCache) {
- StringAppendF(&result, "0x%" PRIx64 "\n", id);
- }
- }
-}
-
-GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) {
- int major, minor;
- if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
- if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
- ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
- return GLES_VERSION_1_0;
- }
- }
-
- if (major == 1 && minor == 0) return GLES_VERSION_1_0;
- if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
- if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
- if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
-
- ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
- return GLES_VERSION_1_0;
-}
-
-EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext,
- std::optional<ContextPriority> contextPriority,
- Protection protection) {
- EGLint renderableType = 0;
- if (config == EGL_NO_CONFIG) {
- renderableType = EGL_OPENGL_ES3_BIT;
- } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
- LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
- }
- EGLint contextClientVersion = 0;
- if (renderableType & EGL_OPENGL_ES3_BIT) {
- contextClientVersion = 3;
- } else if (renderableType & EGL_OPENGL_ES2_BIT) {
- contextClientVersion = 2;
- } else if (renderableType & EGL_OPENGL_ES_BIT) {
- contextClientVersion = 1;
- } else {
- LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
- }
-
- std::vector<EGLint> contextAttributes;
- contextAttributes.reserve(7);
- contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
- contextAttributes.push_back(contextClientVersion);
- if (contextPriority) {
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- switch (*contextPriority) {
- case ContextPriority::REALTIME:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV);
- break;
- case ContextPriority::MEDIUM:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG);
- break;
- case ContextPriority::LOW:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG);
- break;
- case ContextPriority::HIGH:
- default:
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
- break;
- }
- }
- if (protection == Protection::PROTECTED) {
- contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT);
- contextAttributes.push_back(EGL_TRUE);
- }
- contextAttributes.push_back(EGL_NONE);
-
- EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data());
-
- if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) {
- // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus
- // EGL_NO_CONTEXT so that we can abort.
- if (config != EGL_NO_CONFIG) {
- return context;
- }
- // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should
- // try to fall back to GLES 2.
- contextAttributes[1] = 2;
- context = eglCreateContext(display, config, shareContext, contextAttributes.data());
- }
-
- return context;
-}
-
-EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection) {
- EGLConfig stubConfig = config;
- if (stubConfig == EGL_NO_CONFIG) {
- stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
- }
- std::vector<EGLint> attributes;
- attributes.reserve(7);
- attributes.push_back(EGL_WIDTH);
- attributes.push_back(1);
- attributes.push_back(EGL_HEIGHT);
- attributes.push_back(1);
- if (protection == Protection::PROTECTED) {
- attributes.push_back(EGL_PROTECTED_CONTENT_EXT);
- attributes.push_back(EGL_TRUE);
- }
- attributes.push_back(EGL_NONE);
-
- return eglCreatePbufferSurface(display, stubConfig, attributes.data());
-}
-
-bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
- const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
- const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
- return standard == Dataspace::STANDARD_BT2020 &&
- (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
-}
-
-// For convenience, we want to convert the input color space to XYZ color space first,
-// and then convert from XYZ color space to output color space when
-// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
-// HDR content will be tone-mapped to SDR; Or,
-// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
-// HLG content to PQ content.
-// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
-// input data space or output data space is HDR data space, and the input transfer function
-// doesn't match the output transfer function, we would enable an intermediate transfrom to
-// XYZ color space.
-bool GLESRenderEngine::needsXYZTransformMatrix() const {
- const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
- const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
- const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
- const Dataspace outputTransfer =
- static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
-
- return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
-}
-
-bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mRenderingMutex);
- const auto& cachedImage = mImageCache.find(bufferId);
- return cachedImage != mImageCache.end();
-}
-
-bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) {
- const auto& entry = mTextureView.find(texName);
- return entry != mTextureView.end();
-}
-
-std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) {
- const auto& entry = mTextureView.find(texName);
- return entry != mTextureView.end() ? entry->second : std::nullopt;
-}
-
-bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
- std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex);
- return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
- [=](std::pair<uint64_t, EGLImageKHR> image) {
- return image.first == bufferId;
- });
-}
-
-// FlushTracer implementation
-GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
- mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
-}
-
-GLESRenderEngine::FlushTracer::~FlushTracer() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mRunning = false;
- }
- mCondition.notify_all();
- if (mThread.joinable()) {
- mThread.join();
- }
-}
-
-void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) {
- std::lock_guard<std::mutex> lock(mMutex);
- char name[64];
- const uint64_t frameNum = mFramesQueued++;
- snprintf(name, sizeof(name), "Queueing sync for frame: %lu",
- static_cast<unsigned long>(frameNum));
- ATRACE_NAME(name);
- mQueue.push({sync, frameNum});
- ATRACE_INT("GPU Frames Outstanding", mQueue.size());
- mCondition.notify_one();
-}
-
-void GLESRenderEngine::FlushTracer::loop() {
- while (mRunning) {
- QueueEntry entry;
- {
- std::lock_guard<std::mutex> lock(mMutex);
-
- mCondition.wait(mMutex,
- [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
-
- if (!mRunning) {
- // if mRunning is false, then FlushTracer is being destroyed, so
- // bail out now.
- break;
- }
- entry = mQueue.front();
- mQueue.pop();
- }
- {
- char name[64];
- snprintf(name, sizeof(name), "waiting for frame %lu",
- static_cast<unsigned long>(entry.mFrameNum));
- ATRACE_NAME(name);
- mEngine->waitSync(entry.mSync, 0);
- }
- }
-}
-
-void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
- const ShadowSettings& settings) {
- ATRACE_CALL();
- const float casterZ = settings.length / 2.0f;
- const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
- settings.casterIsTranslucent, settings.ambientColor,
- settings.spotColor, settings.lightPos,
- settings.lightRadius);
-
- // setup mesh for both shadows
- Mesh mesh = Mesh::Builder()
- .setPrimitive(Mesh::TRIANGLES)
- .setVertices(shadows.getVertexCount(), 2 /* size */)
- .setShadowAttrs()
- .setIndices(shadows.getIndexCount())
- .build();
-
- Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
- Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
- Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
- shadows.fillVertices(position, shadowColor, shadowParams);
- shadows.fillIndices(mesh.getIndicesArray());
-
- mState.cornerRadius = 0.0f;
- mState.drawShadows = true;
- setupLayerTexturing(mShadowTexture->getTexture());
- drawMesh(mesh);
- mState.drawShadows = false;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
deleted file mode 100644
index ea75e21..0000000
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_GLESRENDERENGINE_H_
-#define SF_GLESRENDERENGINE_H_
-
-#include <condition_variable>
-#include <deque>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <unordered_map>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <android-base/thread_annotations.h>
-#include <renderengine/RenderEngine.h>
-#include <renderengine/private/Description.h>
-#include <sys/types.h>
-#include <ui/FenceResult.h>
-#include "GLShadowTexture.h"
-#include "ImageManager.h"
-
-#define EGL_NO_CONFIG ((EGLConfig)0)
-
-namespace android {
-
-namespace renderengine {
-
-class Mesh;
-class Texture;
-
-namespace gl {
-
-class GLImage;
-class BlurFilter;
-
-class GLESRenderEngine : public RenderEngine {
-public:
- static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
-
- GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
- EGLContext ctxt, EGLSurface stub, EGLContext protectedContext,
- EGLSurface protectedStub);
- ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
-
- std::future<void> primeCache() override;
- void genTextures(size_t count, uint32_t* names) override;
- void deleteTextures(size_t count, uint32_t const* names) override;
- bool isProtected() const { return mInProtectedContext; }
- bool supportsProtectedContent() const override;
- void useProtectedContext(bool useProtectedContext) override;
- void cleanupPostRender() override;
- int getContextPriority() override;
- bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
- void onActiveDisplaySizeChanged(ui::Size size) override {}
-
- EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
- // Creates an output image for rendering to
- EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- bool useFramebufferCache)
- EXCLUDES(mFramebufferImageCacheMutex);
-
- // Test-only methods
- // Returns true iff mImageCache contains an image keyed by bufferId
- bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- // Returns true iff texName was previously generated by RenderEngine and was
- // not destroyed.
- bool isTextureNameKnownForTesting(uint32_t texName);
- // Returns the buffer ID of the content bound to texName, or nullopt if no
- // such mapping exists.
- std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName);
- // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
- bool isFramebufferImageCachedForTesting(uint64_t bufferId)
- EXCLUDES(mFramebufferImageCacheMutex);
- // These are wrappers around public methods above, but exposing Barrier
- // objects so that tests can block.
- std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting(
- const sp<GraphicBuffer>& buffer);
- std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId);
-
-protected:
- Framebuffer* getFramebufferForDrawing();
- void dump(std::string& result) override EXCLUDES(mRenderingMutex)
- EXCLUDES(mFramebufferImageCacheMutex);
- size_t getMaxTextureSize() const override;
- size_t getMaxViewportDims() const override;
- void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable)
- EXCLUDES(mRenderingMutex);
- void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex);
- bool canSkipPostRenderCleanup() const override;
- void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
- const DisplaySettings& display,
- const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
-
-private:
- friend class BindNativeBufferAsFramebuffer;
-
- enum GlesVersion {
- GLES_VERSION_1_0 = 0x10000,
- GLES_VERSION_1_1 = 0x10001,
- GLES_VERSION_2_0 = 0x20000,
- GLES_VERSION_3_0 = 0x30000,
- };
-
- static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
- static GlesVersion parseGlesVersion(const char* str);
- static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
- EGLContext shareContext,
- std::optional<ContextPriority> contextPriority,
- Protection protection);
- static std::optional<RenderEngine::ContextPriority> createContextPriority(
- const RenderEngineCreationArgs& args);
- static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config,
- int hwcFormat, Protection protection);
- std::unique_ptr<Framebuffer> createFramebuffer();
- std::unique_ptr<Image> createImage();
- void checkErrors() const;
- void checkErrors(const char* tag) const;
- void setScissor(const Rect& region);
- void disableScissor();
- bool waitSync(EGLSyncKHR sync, EGLint flags);
- status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer)
- EXCLUDES(mRenderingMutex);
- void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex);
- status_t bindFrameBuffer(Framebuffer* framebuffer);
- void unbindFrameBuffer(Framebuffer* framebuffer);
- void bindExternalTextureImage(uint32_t texName, const Image& image);
- void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
- const sp<Fence>& fence) EXCLUDES(mRenderingMutex);
- void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override;
-
- // A data space is considered HDR data space if it has BT2020 color space
- // with PQ or HLG transfer function.
- bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
- bool needsXYZTransformMatrix() const;
- // Defines the viewport, and sets the projection matrix to the projection
- // defined by the clip.
- void setViewportAndProjection(Rect viewport, Rect clip);
- // Evicts stale images from the buffer cache.
- void evictImages(const std::vector<LayerSettings>& layers);
- // Computes the cropping window for the layer and sets up cropping
- // coordinates for the mesh.
- FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
-
- // We do a special handling for rounded corners when it's possible to turn off blending
- // for the majority of the layer. The rounded corners needs to turn on blending such that
- // we can set the alpha value correctly, however, only the corners need this, and since
- // blending is an expensive operation, we want to turn off blending when it's not necessary.
- void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
- const Mesh& mesh);
- base::unique_fd flush();
- bool finish();
- bool waitFence(base::unique_fd fenceFd);
- void clearWithColor(float red, float green, float blue, float alpha);
- void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
- void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
- const ShadowSettings& shadowSettings);
- void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color, float cornerRadius);
- void setupLayerTexturing(const Texture& texture);
- void setupFillWithColor(float r, float g, float b, float a);
- void setColorTransform(const mat4& colorTransform);
- void setDisplayColorTransform(const mat4& colorTransform);
- void disableTexturing();
- void disableBlending();
- void setupCornerRadiusCropSize(float width, float height);
-
- // HDR and color management related functions and state
- void setSourceDataSpace(ui::Dataspace source);
- void setOutputDataSpace(ui::Dataspace dataspace);
- void setDisplayMaxLuminance(const float maxLuminance);
-
- // drawing
- void drawMesh(const Mesh& mesh);
-
- EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
- EGLContext mEGLContext;
- EGLSurface mStubSurface;
- EGLContext mProtectedEGLContext;
- EGLSurface mProtectedStubSurface;
- GLint mMaxViewportDims[2];
- GLint mMaxTextureSize;
- GLuint mVpWidth;
- GLuint mVpHeight;
- Description mState;
- std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr;
-
- mat4 mSrgbToXyz;
- mat4 mDisplayP3ToXyz;
- mat4 mBt2020ToXyz;
- mat4 mXyzToSrgb;
- mat4 mXyzToDisplayP3;
- mat4 mXyzToBt2020;
- mat4 mSrgbToDisplayP3;
- mat4 mSrgbToBt2020;
- mat4 mDisplayP3ToSrgb;
- mat4 mDisplayP3ToBt2020;
- mat4 mBt2020ToSrgb;
- mat4 mBt2020ToDisplayP3;
-
- bool mInProtectedContext = false;
- // If set to true, then enables tracing flush() and finish() to systrace.
- bool mTraceGpuCompletion = false;
- // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
- // the last recently used buffer should be kicked out.
- uint32_t mFramebufferImageCacheSize = 0;
-
- // Cache of output images, keyed by corresponding GraphicBuffer ID.
- std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache
- GUARDED_BY(mFramebufferImageCacheMutex);
- // The only reason why we have this mutex is so that we don't segfault when
- // dumping info.
- std::mutex mFramebufferImageCacheMutex;
-
- // Current dataspace of layer being rendered
- ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
-
- // Current output dataspace of the render engine
- ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
-
- // Whether device supports color management, currently color management
- // supports sRGB, DisplayP3 color spaces.
- const bool mUseColorManagement = false;
-
- // Whether only shaders performing tone mapping from HDR to SDR will be generated on
- // primeCache().
- const bool mPrecacheToneMapperShaderOnly = false;
-
- // Cache of GL images that we'll store per GraphicBuffer ID
- std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex);
- std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView;
-
- // Mutex guarding rendering operations, so that:
- // 1. GL operations aren't interleaved, and
- // 2. Internal state related to rendering that is potentially modified by
- // multiple threads is guaranteed thread-safe.
- std::mutex mRenderingMutex;
-
- std::unique_ptr<Framebuffer> mDrawingBuffer;
- // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
- // memory or if it needs to satisfy alignment requirements. In this case:
- // assume that each channel requires 4 bytes, and add 3 additional bytes to
- // ensure that we align on a word. Allocating 16 bytes will provide a
- // guarantee that we don't clobber memory.
- uint32_t mPlaceholderDrawBuffer[4];
- // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but
- // instead these are intended for cleaning up texture memory with the
- // GL_TEXTURE_EXTERNAL_OES target.
- ANativeWindowBuffer* mPlaceholderBuffer = nullptr;
- EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR;
- sp<Fence> mLastDrawFence;
- // Store a separate boolean checking if prior resources were cleaned up, as
- // devices that don't support native sync fences can't rely on a last draw
- // fence that doesn't exist.
- bool mPriorResourcesCleaned = true;
-
- // Blur effect processor, only instantiated when a layer requests it.
- BlurFilter* mBlurFilter = nullptr;
-
- class FlushTracer {
- public:
- FlushTracer(GLESRenderEngine* engine);
- ~FlushTracer();
- void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex);
-
- struct QueueEntry {
- EGLSyncKHR mSync = nullptr;
- uint64_t mFrameNum = 0;
- };
-
- private:
- void loop();
- GLESRenderEngine* const mEngine;
- std::thread mThread;
- std::condition_variable_any mCondition;
- std::mutex mMutex;
- std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
- uint64_t mFramesQueued GUARDED_BY(mMutex) = 0;
- bool mRunning = true;
- };
- friend class FlushTracer;
- friend class ImageManager;
- friend class GLFramebuffer;
- friend class BlurFilter;
- friend class GenericProgram;
- std::unique_ptr<FlushTracer> mFlushTracer;
- std::unique_ptr<ImageManager> mImageManager;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
deleted file mode 100644
index 58d6caa..0000000
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2018 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLFramebuffer.h"
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-#include <gui/DebugEGLImageTracker.h>
-#include <nativebase/nativebase.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
- : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
- glGenTextures(1, &mTextureName);
- glGenFramebuffers(1, &mFramebufferName);
-}
-
-GLFramebuffer::~GLFramebuffer() {
- setNativeWindowBuffer(nullptr, false, false);
- glDeleteFramebuffers(1, &mFramebufferName);
- glDeleteTextures(1, &mTextureName);
-}
-
-bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) {
- ATRACE_CALL();
- if (mEGLImage != EGL_NO_IMAGE_KHR) {
- if (!usingFramebufferCache) {
- eglDestroyImageKHR(mEGLDisplay, mEGLImage);
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- }
- mEGLImage = EGL_NO_IMAGE_KHR;
- mBufferWidth = 0;
- mBufferHeight = 0;
- }
-
- if (nativeBuffer) {
- mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected,
- useFramebufferCache);
- if (mEGLImage == EGL_NO_IMAGE_KHR) {
- return false;
- }
- usingFramebufferCache = useFramebufferCache;
- mBufferWidth = nativeBuffer->width;
- mBufferHeight = nativeBuffer->height;
- }
- return true;
-}
-
-void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
- ATRACE_CALL();
-
- glBindTexture(GL_TEXTURE_2D, mTextureName);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
-
- mBufferHeight = height;
- mBufferWidth = width;
- mEngine.checkErrors("Allocating Fbo texture");
-
- bind();
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
- mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- unbind();
- glBindTexture(GL_TEXTURE_2D, 0);
-
- if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Frame buffer is not complete. Error %d", mStatus);
- }
-}
-
-void GLFramebuffer::bind() const {
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::bindAsReadBuffer() const {
- glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::bindAsDrawBuffer() const {
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
-}
-
-void GLFramebuffer::unbind() const {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
deleted file mode 100644
index 6757695..0000000
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <renderengine/Framebuffer.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLFramebuffer : public renderengine::Framebuffer {
-public:
- explicit GLFramebuffer(GLESRenderEngine& engine);
- explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
- ~GLFramebuffer() override;
-
- bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) override;
- void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
- EGLImageKHR getEGLImage() const { return mEGLImage; }
- uint32_t getTextureName() const { return mTextureName; }
- uint32_t getFramebufferName() const { return mFramebufferName; }
- int32_t getBufferHeight() const { return mBufferHeight; }
- int32_t getBufferWidth() const { return mBufferWidth; }
- GLenum getStatus() const { return mStatus; }
- void bind() const;
- void bindAsReadBuffer() const;
- void bindAsDrawBuffer() const;
- void unbind() const;
-
-private:
- GLESRenderEngine& mEngine;
- EGLDisplay mEGLDisplay;
- EGLImageKHR mEGLImage;
- bool usingFramebufferCache = false;
- GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
- uint32_t mTextureName, mFramebufferName;
-
- int32_t mBufferHeight = 0;
- int32_t mBufferWidth = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
deleted file mode 100644
index 8497721..0000000
--- a/libs/renderengine/gl/GLImage.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2018 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLImage.h"
-
-#include <vector>
-
-#include <gui/DebugEGLImageTracker.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "GLExtensions.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-static std::vector<EGLint> buildAttributeList(bool isProtected) {
- std::vector<EGLint> attrs;
- attrs.reserve(16);
-
- attrs.push_back(EGL_IMAGE_PRESERVED_KHR);
- attrs.push_back(EGL_TRUE);
-
- if (isProtected && GLExtensions::getInstance().hasProtectedContent()) {
- attrs.push_back(EGL_PROTECTED_CONTENT_EXT);
- attrs.push_back(EGL_TRUE);
- }
-
- attrs.push_back(EGL_NONE);
-
- return attrs;
-}
-
-GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
-
-GLImage::~GLImage() {
- setNativeWindowBuffer(nullptr, false);
-}
-
-bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
- ATRACE_CALL();
- if (mEGLImage != EGL_NO_IMAGE_KHR) {
- if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
- ALOGE("failed to destroy image: %#x", eglGetError());
- }
- DEBUG_EGL_IMAGE_TRACKER_DESTROY();
- mEGLImage = EGL_NO_IMAGE_KHR;
- }
-
- if (buffer) {
- std::vector<EGLint> attrs = buildAttributeList(isProtected);
- mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- static_cast<EGLClientBuffer>(buffer), attrs.data());
- if (mEGLImage == EGL_NO_IMAGE_KHR) {
- ALOGE("failed to create EGLImage: %#x", eglGetError());
- return false;
- }
- DEBUG_EGL_IMAGE_TRACKER_CREATE();
- mProtected = isProtected;
- }
-
- return true;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
deleted file mode 100644
index 59d6ce3..0000000
--- a/libs/renderengine/gl/GLImage.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <android-base/macros.h>
-#include <renderengine/Image.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLImage : public renderengine::Image {
-public:
- explicit GLImage(const GLESRenderEngine& engine);
- ~GLImage() override;
-
- bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
-
- EGLImageKHR getEGLImage() const { return mEGLImage; }
- bool isProtected() const { return mProtected; }
-
-private:
- EGLDisplay mEGLDisplay;
- EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
- bool mProtected = false;
-
- DISALLOW_COPY_AND_ASSIGN(GLImage);
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
deleted file mode 100644
index 2423a34..0000000
--- a/libs/renderengine/gl/GLShadowTexture.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GLES3/gl3.h>
-
-#include "GLShadowTexture.h"
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLShadowTexture::GLShadowTexture() {
- fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
-
- glGenTextures(1, &mName);
- glBindTexture(GL_TEXTURE_2D, mName);
- glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
- SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
- mTexture.init(Texture::TEXTURE_2D, mName);
- mTexture.setFiltering(true);
- mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
-}
-
-GLShadowTexture::~GLShadowTexture() {
- glDeleteTextures(1, &mName);
-}
-
-const Texture& GLShadowTexture::getTexture() {
- return mTexture;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
deleted file mode 100644
index 250a9d7..0000000
--- a/libs/renderengine/gl/GLShadowTexture.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <renderengine/Texture.h>
-#include <cstdint>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLShadowTexture {
-public:
- GLShadowTexture();
- ~GLShadowTexture();
-
- const Texture& getTexture();
-
-private:
- static constexpr int SHADOW_TEXTURE_WIDTH = 128;
- static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
-
- GLuint mName;
- Texture mTexture;
- uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
deleted file mode 100644
index 3181f9b..0000000
--- a/libs/renderengine/gl/GLShadowVertexGenerator.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2019 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 <renderengine/Mesh.h>
-
-#include <math/vec4.h>
-
-#include <ui/Rect.h>
-#include <ui/Transform.h>
-
-#include "GLShadowVertexGenerator.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& ambientColor,
- const vec4& spotColor, const vec3& lightPosition,
- float lightRadius) {
- mDrawAmbientShadow = ambientColor.a > 0.f;
- mDrawSpotShadow = spotColor.a > 0.f;
-
- // Generate geometries and find number of vertices to generate
- if (mDrawAmbientShadow) {
- mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
- casterIsTranslucent, ambientColor);
- mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
- mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
- } else {
- mAmbientShadowVertexCount = 0;
- mAmbientShadowIndexCount = 0;
- }
-
- if (mDrawSpotShadow) {
- mSpotShadowGeometry =
- getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
- spotColor, lightPosition, lightRadius);
- mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
- mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
- } else {
- mSpotShadowVertexCount = 0;
- mSpotShadowIndexCount = 0;
- }
-}
-
-size_t GLShadowVertexGenerator::getVertexCount() const {
- return mAmbientShadowVertexCount + mSpotShadowVertexCount;
-}
-
-size_t GLShadowVertexGenerator::getIndexCount() const {
- return mAmbientShadowIndexCount + mSpotShadowIndexCount;
-}
-
-void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& color,
- Mesh::VertexArray<vec3>& params) const {
- if (mDrawAmbientShadow) {
- fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
- color, params);
- }
- if (mDrawSpotShadow) {
- fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
- Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
- Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
- Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
- }
-}
-
-void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
- if (mDrawAmbientShadow) {
- fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
- 0 /* starting vertex offset */, indices);
- }
- if (mDrawSpotShadow) {
- fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
- mAmbientShadowVertexCount /* starting vertex offset */,
- &(indices[mAmbientShadowIndexCount]));
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
deleted file mode 100644
index 112f976..0000000
--- a/libs/renderengine/gl/GLShadowVertexGenerator.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2019 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 <math/vec4.h>
-#include <ui/Rect.h>
-
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-
-class Mesh;
-
-namespace gl {
-
-/**
- * Generates gl attributes required to draw shadow spot and/or ambient shadows.
- *
- * Each shadow can support different colors. This class generates three vertex attributes for
- * each shadow, its position, color and shadow params(offset and distance). These can be sent
- * using a single glDrawElements call.
- */
-class GLShadowVertexGenerator {
-public:
- GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& ambientColor,
- const vec4& spotColor, const vec3& lightPosition, float lightRadius);
- ~GLShadowVertexGenerator() = default;
-
- size_t getVertexCount() const;
- size_t getIndexCount() const;
- void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
- Mesh::VertexArray<vec3>& params) const;
- void fillIndices(uint16_t* indices) const;
-
-private:
- bool mDrawAmbientShadow;
- std::unique_ptr<Geometry> mAmbientShadowGeometry;
- int mAmbientShadowVertexCount = 0;
- int mAmbientShadowIndexCount = 0;
-
- bool mDrawSpotShadow;
- std::unique_ptr<Geometry> mSpotShadowGeometry;
- int mSpotShadowVertexCount = 0;
- int mSpotShadowIndexCount = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
deleted file mode 100644
index da8b435..0000000
--- a/libs/renderengine/gl/GLSkiaShadowPort.cpp
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright 2019 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 <math/vec4.h>
-
-#include <renderengine/Mesh.h>
-
-#include <ui/Rect.h>
-#include <ui/Transform.h>
-
-#include <utils/Log.h>
-
-#include "GLSkiaShadowPort.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * The shadow geometry logic and vertex generation code has been ported from skia shadow
- * fast path OpenGL implementation to draw shadows around rects and rounded rects including
- * circles.
- *
- * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
- *
- * Modifications made:
- * - Switched to using std lib math functions
- * - Fall off function is implemented in vertex shader rather than a shadow texture
- * - Removed transformations applied on the caster rect since the caster will be in local
- * coordinate space and will be transformed by the vertex shader.
- */
-
-static inline float divide_and_pin(float numer, float denom, float min, float max) {
- if (denom == 0.0f) return min;
- return std::clamp(numer / denom, min, max);
-}
-
-static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
-static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
-static constexpr auto kAmbientGeomFactor = 64.0f;
-// Assuming that we have a light height of 600 for the spot shadow,
-// the spot values will reach their maximum at a height of approximately 292.3077.
-// We'll round up to 300 to keep it simple.
-static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
-
-inline float AmbientBlurRadius(float height) {
- return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
-}
-inline float AmbientRecipAlpha(float height) {
- return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Circle Data
-//
-// We have two possible cases for geometry for a circle:
-
-// In the case of a normal fill, we draw geometry for the circle as an octagon.
-static const uint16_t gFillCircleIndices[] = {
- // enter the octagon
- // clang-format off
- 0, 1, 8, 1, 2, 8,
- 2, 3, 8, 3, 4, 8,
- 4, 5, 8, 5, 6, 8,
- 6, 7, 8, 7, 0, 8,
- // clang-format on
-};
-
-// For stroked circles, we use two nested octagons.
-static const uint16_t gStrokeCircleIndices[] = {
- // enter the octagon
- // clang-format off
- 0, 1, 9, 0, 9, 8,
- 1, 2, 10, 1, 10, 9,
- 2, 3, 11, 2, 11, 10,
- 3, 4, 12, 3, 12, 11,
- 4, 5, 13, 4, 13, 12,
- 5, 6, 14, 5, 14, 13,
- 6, 7, 15, 6, 15, 14,
- 7, 0, 8, 7, 8, 15,
- // clang-format on
-};
-
-#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
-static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
-static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
-static const int kVertsPerStrokeCircle = 16;
-static const int kVertsPerFillCircle = 9;
-
-static int circle_type_to_vert_count(bool stroked) {
- return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
-}
-
-static int circle_type_to_index_count(bool stroked) {
- return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
-}
-
-static const uint16_t* circle_type_to_indices(bool stroked) {
- return stroked ? gStrokeCircleIndices : gFillCircleIndices;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// RoundRect Data
-//
-// The geometry for a shadow roundrect is similar to a 9-patch:
-// ____________
-// |_|________|_|
-// | | | |
-// | | | |
-// | | | |
-// |_|________|_|
-// |_|________|_|
-//
-// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
-// shows the upper part of the upper left corner. The bottom triangle would similarly be split
-// into two triangles.)
-// ________
-// |\ \ |
-// | \ \ |
-// | \\ |
-// | \|
-// --------
-//
-// The center of the fan handles the curve of the corner. For roundrects where the stroke width
-// is greater than the corner radius, the outer triangles blend from the curve to the straight
-// sides. Otherwise these triangles will be degenerate.
-//
-// In the case where the stroke width is greater than the corner radius and the
-// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
-// This rectangle extends the coverage values of the center edges of the 9-patch.
-// ____________
-// |_|________|_|
-// | |\ ____ /| |
-// | | | | | |
-// | | |____| | |
-// |_|/______\|_|
-// |_|________|_|
-//
-// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
-
-static const uint16_t gRRectIndices[] = {
- // clang-format off
- // overstroke quads
- // we place this at the beginning so that we can skip these indices when rendering as filled
- 0, 6, 25, 0, 25, 24,
- 6, 18, 27, 6, 27, 25,
- 18, 12, 26, 18, 26, 27,
- 12, 0, 24, 12, 24, 26,
-
- // corners
- 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
- 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
- 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
- 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
-
- // edges
- 0, 5, 11, 0, 11, 6,
- 6, 7, 19, 6, 19, 18,
- 18, 23, 17, 18, 17, 12,
- 12, 13, 1, 12, 1, 0,
-
- // fill quad
- // we place this at the end so that we can skip these indices when rendering as stroked
- 0, 6, 18, 0, 18, 12,
- // clang-format on
-};
-
-// overstroke count
-static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
-// simple stroke count skips overstroke indices
-static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
-// fill count adds final quad to stroke count
-static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
-static const int kVertsPerStrokeRRect = 24;
-static const int kVertsPerOverstrokeRRect = 28;
-static const int kVertsPerFillRRect = 24;
-
-static int rrect_type_to_vert_count(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- return kVertsPerFillRRect;
- case kStroke_RRectType:
- return kVertsPerStrokeRRect;
- case kOverstroke_RRectType:
- return kVertsPerOverstrokeRRect;
- }
- ALOGE("Invalid rect type: %d", type);
- return -1;
-}
-
-static int rrect_type_to_index_count(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- return kIndicesPerFillRRect;
- case kStroke_RRectType:
- return kIndicesPerStrokeRRect;
- case kOverstroke_RRectType:
- return kIndicesPerOverstrokeRRect;
- }
- ALOGE("Invalid rect type: %d", type);
- return -1;
-}
-
-static const uint16_t* rrect_type_to_indices(RRectType type) {
- switch (type) {
- case kFill_RRectType:
- case kStroke_RRectType:
- return gRRectIndices + 6 * 4;
- case kOverstroke_RRectType:
- return gRRectIndices;
- }
- ALOGE("Invalid rect type: %d", type);
- return nullptr;
-}
-
-static void fillInCircleVerts(const Geometry& args, bool isStroked,
- Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& shadowColor,
- Mesh::VertexArray<vec3>& shadowParams) {
- vec4 color = args.fColor;
- float outerRadius = args.fOuterRadius;
- float innerRadius = args.fInnerRadius;
- float blurRadius = args.fBlurRadius;
- float distanceCorrection = outerRadius / blurRadius;
-
- const FloatRect& bounds = args.fDevBounds;
-
- // The inner radius in the vertex data must be specified in normalized space.
- innerRadius = innerRadius / outerRadius;
-
- vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
- float halfWidth = 0.5f * bounds.getWidth();
- float octOffset = 0.41421356237f; // sqrt(2) - 1
- int vertexCount = 0;
-
- position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
- vertexCount++;
-
- if (isStroked) {
- // compute the inner ring
-
- // cosine and sine of pi/8
- float c = 0.923579533f;
- float s = 0.382683432f;
- float r = args.fInnerRadius;
-
- position[vertexCount] = center + vec2(-s * r, -c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(s * r, -c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(c * r, -s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(c * r, s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(s * r, c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-s * r, c * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-c * r, s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = center + vec2(-c * r, -s * r);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
- vertexCount++;
- } else {
- // filled
- position[vertexCount] = center;
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
- }
-}
-
-static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
- Mesh::VertexArray<vec4>& shadowColor,
- Mesh::VertexArray<vec3>& shadowParams) {
- vec4 color = args.fColor;
- float outerRadius = args.fOuterRadius;
-
- const FloatRect& bounds = args.fDevBounds;
-
- float umbraInset = args.fUmbraInset;
- float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
- if (umbraInset > minDim) {
- umbraInset = minDim;
- }
-
- float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
- bounds.left + umbraInset, bounds.right - umbraInset};
- float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
- bounds.left + outerRadius, bounds.right - outerRadius};
- float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
- float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
- bounds.bottom - umbraInset};
- float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
- bounds.bottom - outerRadius, bounds.bottom - outerRadius};
- float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
-
- float blurRadius = args.fBlurRadius;
-
- // In the case where we have to inset more for the umbra, our two triangles in the
- // corner get skewed to a diamond rather than a square. To correct for that,
- // we also skew the vectors we send to the shader that help define the circle.
- // By doing so, we end up with a quarter circle in the corner rather than the
- // elliptical curve.
-
- // This is a bit magical, but it gives us the correct results at extrema:
- // a) umbraInset == outerRadius produces an orthogonal vector
- // b) outerRadius == 0 produces a diagonal vector
- // And visually the corner looks correct.
- vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
- outerVec = normalize(outerVec);
- // We want the circle edge to fall fractionally along the diagonal at
- // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
- //
- // Setting the components of the diagonal offset to the following value will give us that.
- float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
- vec2 diagVec = vec2(diagVal, diagVal);
- float distanceCorrection = umbraInset / blurRadius;
-
- int vertexCount = 0;
- // build corner by corner
- for (int i = 0; i < 4; ++i) {
- // inner point
- position[vertexCount] = vec2(xInner[i], yInner[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // outer points
- position[vertexCount] = vec2(xOuter[i], yInner[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xOuter[i], yMid[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xOuter[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xMid[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
- vertexCount++;
-
- position[vertexCount] = vec2(xInner[i], yOuter[i]);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
- vertexCount++;
- }
-
- // Add the additional vertices for overstroked rrects.
- // Effectively this is an additional stroked rrect, with its
- // parameters equal to those in the center of the 9-patch. This will
- // give constant values across this inner ring.
- if (kOverstroke_RRectType == args.fType) {
- float inset = umbraInset + args.fInnerRadius;
-
- // TL
- position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // TR
- position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // BL
- position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
-
- // BR
- position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
- shadowColor[vertexCount] = color;
- shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
- vertexCount++;
- }
-}
-
-int getVertexCountForGeometry(const Geometry& shadowGeometry) {
- if (shadowGeometry.fIsCircle) {
- return circle_type_to_vert_count(shadowGeometry.fType);
- }
-
- return rrect_type_to_vert_count(shadowGeometry.fType);
-}
-
-int getIndexCountForGeometry(const Geometry& shadowGeometry) {
- if (shadowGeometry.fIsCircle) {
- return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
- }
-
- return rrect_type_to_index_count(shadowGeometry.fType);
-}
-
-void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
- Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
- Mesh::VertexArray<vec3> shadowParams) {
- if (shadowGeometry.fIsCircle) {
- fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
- shadowParams);
- } else {
- fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
- }
-}
-
-void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
- int startingVertexOffset, uint16_t* indices) {
- if (shadowGeometry.fIsCircle) {
- const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
- for (int i = 0; i < indexCount; ++i) {
- indices[i] = primIndices[i] + startingVertexOffset;
- }
- } else {
- const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
- for (int i = 0; i < indexCount; ++i) {
- indices[i] = primIndices[i] + startingVertexOffset;
- }
- }
-}
-
-inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
- float lightRadius, float& blurRadius, float& scale, vec2& translate) {
- float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
- blurRadius = lightRadius * zRatio;
- scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
- translate.x = -zRatio * lightX;
- translate.y = -zRatio * lightY;
-}
-
-static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
- float devRadius, float blurRadius,
- float insetWidth) {
- // An insetWidth > 1/2 rect width or height indicates a simple fill.
- const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
-
- FloatRect bounds = devRect;
- float innerRadius = 0.0f;
- float outerRadius = devRadius;
- float umbraInset;
-
- RRectType type = kFill_RRectType;
- if (isCircle) {
- umbraInset = 0;
- } else {
- umbraInset = std::max(outerRadius, blurRadius);
- }
-
- // If stroke is greater than width or height, this is still a fill,
- // otherwise we compute stroke params.
- if (isCircle) {
- innerRadius = devRadius - insetWidth;
- type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
- } else {
- if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
- // We don't worry about a real inner radius, we just need to know if we
- // need to create overstroke vertices.
- innerRadius = std::max(insetWidth - umbraInset, 0.0f);
- type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
- }
- }
- const bool isStroked = (kStroke_RRectType == type);
- return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
- blurRadius, bounds, type, isCircle, isStroked});
-}
-
-std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent,
- const vec4& ambientColor) {
- float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
- const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
- const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
-
- // Outset the shadow rrect to the border of the penumbra
- float ambientPathOutset = devSpaceInsetWidth;
- FloatRect outsetRect(casterRect);
- outsetRect.left -= ambientPathOutset;
- outsetRect.top -= ambientPathOutset;
- outsetRect.right += ambientPathOutset;
- outsetRect.bottom += ambientPathOutset;
-
- float outsetRad = casterCornerRadius + ambientPathOutset;
- if (casterIsTranslucent) {
- // set a large inset to force a fill
- devSpaceInsetWidth = outsetRect.getWidth();
- }
-
- return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
- std::abs(devSpaceInsetWidth));
-}
-
-std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& spotColor,
- const vec3& lightPosition, float lightRadius) {
- float devSpaceSpotBlur;
- float spotScale;
- vec2 spotOffset;
- GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
- devSpaceSpotBlur, spotScale, spotOffset);
- // handle scale of radius due to CTM
- const float srcSpaceSpotBlur = devSpaceSpotBlur;
-
- // Adjust translate for the effect of the scale.
- spotOffset.x += spotScale;
- spotOffset.y += spotScale;
-
- // Compute the transformed shadow rect
- ui::Transform shadowTransform;
- shadowTransform.set(spotOffset.x, spotOffset.y);
- shadowTransform.set(spotScale, 0, 0, spotScale);
- FloatRect spotShadowRect = shadowTransform.transform(casterRect);
- float spotShadowRadius = casterCornerRadius * spotScale;
-
- // Compute the insetWidth
- float blurOutset = srcSpaceSpotBlur;
- float insetWidth = blurOutset;
- if (casterIsTranslucent) {
- // If transparent, just do a fill
- insetWidth += spotShadowRect.getWidth();
- } else {
- // For shadows, instead of using a stroke we specify an inset from the penumbra
- // border. We want to extend this inset area so that it meets up with the caster
- // geometry. The inset geometry will by default already be inset by the blur width.
- //
- // We compare the min and max corners inset by the radius between the original
- // rrect and the shadow rrect. The distance between the two plus the difference
- // between the scaled radius and the original radius gives the distance from the
- // transformed shadow shape to the original shape in that corner. The max
- // of these gives the maximum distance we need to cover.
- //
- // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
- // that to get the full insetWidth.
- float maxOffset;
- if (casterCornerRadius <= 0.f) {
- // Manhattan distance works better for rects
- maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
- std::abs(spotShadowRect.top - casterRect.top)),
- std::max(std::abs(spotShadowRect.right - casterRect.right),
- std::abs(spotShadowRect.bottom - casterRect.bottom)));
- } else {
- float dr = spotShadowRadius - casterCornerRadius;
- vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
- spotShadowRect.top - casterRect.top + dr);
- vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
- spotShadowRect.bottom - casterRect.bottom - dr);
- maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
- dot(lowerRightOffset, lowerRightOffset))) +
- dr;
- }
- insetWidth += std::max(blurOutset, maxOffset);
- }
-
- // Outset the shadow rrect to the border of the penumbra
- spotShadowRadius += blurOutset;
- spotShadowRect.left -= blurOutset;
- spotShadowRect.top -= blurOutset;
- spotShadowRect.right += blurOutset;
- spotShadowRect.bottom += blurOutset;
-
- return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
- 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
-}
-
-void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
- for (int i = 0; i < shadowTextureWidth; i++) {
- const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
- data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
deleted file mode 100644
index 912c8bb..0000000
--- a/libs/renderengine/gl/GLSkiaShadowPort.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2019 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 <math/vec4.h>
-#include <renderengine/Mesh.h>
-#include <ui/Rect.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * The shadow geometry logic and vertex generation code has been ported from skia shadow
- * fast path OpenGL implementation to draw shadows around rects and rounded rects including
- * circles.
- *
- * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
- *
- * Modifications made:
- * - Switched to using std lib math functions
- * - Fall off function is implemented in vertex shader rather than a shadow texture
- * - Removed transformations applied on the caster rect since the caster will be in local
- * coordinate space and will be transformed by the vertex shader.
- */
-
-enum RRectType {
- kFill_RRectType,
- kStroke_RRectType,
- kOverstroke_RRectType,
-};
-
-struct Geometry {
- vec4 fColor;
- float fOuterRadius;
- float fUmbraInset;
- float fInnerRadius;
- float fBlurRadius;
- FloatRect fDevBounds;
- RRectType fType;
- bool fIsCircle;
- bool fIsStroked;
-};
-
-std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent, const vec4& spotColor,
- const vec3& lightPosition, float lightRadius);
-
-std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
- float casterCornerRadius, float casterZ,
- bool casterIsTranslucent,
- const vec4& ambientColor);
-
-int getVertexCountForGeometry(const Geometry& shadowGeometry);
-
-int getIndexCountForGeometry(const Geometry& shadowGeometry);
-
-void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
- Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
- Mesh::VertexArray<vec3> shadowParams);
-
-void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
- int startingVertexOffset, uint16_t* indices);
-
-/**
- * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
- * darkness at that spot. Values are determined by an exponential falloff
- * function provided by UX.
- *
- * The texture is used for quick lookup in theshadow shader.
- *
- * textureData - filled with shadow texture data that needs to be at least of
- * size textureWidth
- *
- * textureWidth - width of the texture, height is always 1
- */
-void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
deleted file mode 100644
index e50c471..0000000
--- a/libs/renderengine/gl/GLVertexBuffer.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GLVertexBuffer.h"
-
-#include <GLES/gl.h>
-#include <GLES2/gl2.h>
-#include <nativebase/nativebase.h>
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLVertexBuffer::GLVertexBuffer() {
- glGenBuffers(1, &mBufferName);
-}
-
-GLVertexBuffer::~GLVertexBuffer() {
- glDeleteBuffers(1, &mBufferName);
-}
-
-void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
- ATRACE_CALL();
- bind();
- glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
- unbind();
-}
-
-void GLVertexBuffer::bind() const {
- glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
-}
-
-void GLVertexBuffer::unbind() const {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
deleted file mode 100644
index c0fd0c1..0000000
--- a/libs/renderengine/gl/GLVertexBuffer.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class GLVertexBuffer {
-public:
- explicit GLVertexBuffer();
- ~GLVertexBuffer();
-
- void allocateBuffers(const GLfloat data[], const GLuint size);
- uint32_t getBufferName() const { return mBufferName; }
- void bind() const;
- void unbind() const;
-
-private:
- uint32_t mBufferName;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp
deleted file mode 100644
index 6256649..0000000
--- a/libs/renderengine/gl/ImageManager.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2019 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_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "RenderEngine"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <pthread.h>
-
-#include <processgroup/sched_policy.h>
-#include <utils/Trace.h>
-#include "GLESRenderEngine.h"
-#include "ImageManager.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {}
-
-void ImageManager::initThread() {
- mThread = std::thread([this]() { threadMain(); });
- pthread_setname_np(mThread.native_handle(), "ImageManager");
- // Use SCHED_FIFO to minimize jitter
- struct sched_param param = {0};
- param.sched_priority = 2;
- if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
- ALOGE("Couldn't set SCHED_FIFO for ImageManager");
- }
-}
-
-ImageManager::~ImageManager() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mRunning = false;
- }
- mCondition.notify_all();
- if (mThread.joinable()) {
- mThread.join();
- }
-}
-
-void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer,
- const std::shared_ptr<Barrier>& barrier) {
- if (buffer == nullptr) {
- {
- std::lock_guard<std::mutex> lock(barrier->mutex);
- barrier->isOpen = true;
- barrier->result = BAD_VALUE;
- }
- barrier->condition.notify_one();
- return;
- }
- ATRACE_CALL();
- QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier};
- queueOperation(std::move(entry));
-}
-
-status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) {
- ATRACE_CALL();
- auto barrier = std::make_shared<Barrier>();
- cacheAsync(buffer, barrier);
- std::lock_guard<std::mutex> lock(barrier->mutex);
- barrier->condition.wait(barrier->mutex,
- [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; });
- return barrier->result;
-}
-
-void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) {
- ATRACE_CALL();
- QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier};
- queueOperation(std::move(entry));
-}
-
-void ImageManager::queueOperation(const QueueEntry&& entry) {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mQueue.emplace(entry);
- ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
- }
- mCondition.notify_one();
-}
-
-void ImageManager::threadMain() {
- set_sched_policy(0, SP_FOREGROUND);
- bool run;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- run = mRunning;
- }
- while (run) {
- QueueEntry entry;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mCondition.wait(mMutex,
- [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; });
- run = mRunning;
-
- if (!mRunning) {
- // if mRunning is false, then ImageManager is being destroyed, so
- // bail out now.
- break;
- }
-
- entry = mQueue.front();
- mQueue.pop();
- ATRACE_INT("ImageManagerQueueDepth", mQueue.size());
- }
-
- status_t result = NO_ERROR;
- switch (entry.op) {
- case QueueEntry::Operation::Delete:
- mEngine->unbindExternalTextureBufferInternal(entry.bufferId);
- break;
- case QueueEntry::Operation::Insert:
- result = mEngine->cacheExternalTextureBufferInternal(entry.buffer);
- break;
- }
- if (entry.barrier != nullptr) {
- {
- std::lock_guard<std::mutex> entryLock(entry.barrier->mutex);
- entry.barrier->result = result;
- entry.barrier->isOpen = true;
- }
- entry.barrier->condition.notify_one();
- }
- }
-
- ALOGD("Reached end of threadMain, terminating ImageManager thread!");
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h
deleted file mode 100644
index be67de8..0000000
--- a/libs/renderengine/gl/ImageManager.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019 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 <condition_variable>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLESRenderEngine;
-
-class ImageManager {
-public:
- struct Barrier {
- std::mutex mutex;
- std::condition_variable_any condition;
- bool isOpen GUARDED_BY(mutex) = false;
- status_t result GUARDED_BY(mutex) = NO_ERROR;
- };
- ImageManager(GLESRenderEngine* engine);
- ~ImageManager();
- // Starts the background thread for the ImageManager
- // We need this to guarantee that the class is fully-constructed before the
- // thread begins running.
- void initThread();
- void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
- EXCLUDES(mMutex);
- status_t cache(const sp<GraphicBuffer>& buffer);
- void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex);
-
-private:
- struct QueueEntry {
- enum class Operation { Delete, Insert };
-
- Operation op = Operation::Delete;
- sp<GraphicBuffer> buffer = nullptr;
- uint64_t bufferId = 0;
- std::shared_ptr<Barrier> barrier = nullptr;
- };
-
- void queueOperation(const QueueEntry&& entry);
- void threadMain();
- GLESRenderEngine* const mEngine;
- std::thread mThread;
- std::condition_variable_any mCondition;
- std::mutex mMutex;
- std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
-
- bool mRunning GUARDED_BY(mMutex) = true;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
deleted file mode 100644
index 26f6166..0000000
--- a/libs/renderengine/gl/Program.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*Gluint
- * Copyright 2013 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 "Program.h"
-
-#include <stdint.h>
-
-#include <log/log.h>
-#include <math/mat4.h>
-#include <utils/String8.h>
-#include "ProgramCache.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
- : mInitialized(false) {
- GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER);
- GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER);
- GLuint programId = glCreateProgram();
- glAttachShader(programId, vertexId);
- glAttachShader(programId, fragmentId);
- glBindAttribLocation(programId, position, "position");
- glBindAttribLocation(programId, texCoords, "texCoords");
- glBindAttribLocation(programId, cropCoords, "cropCoords");
- glBindAttribLocation(programId, shadowColor, "shadowColor");
- glBindAttribLocation(programId, shadowParams, "shadowParams");
- glLinkProgram(programId);
-
- GLint status;
- glGetProgramiv(programId, GL_LINK_STATUS, &status);
- if (status != GL_TRUE) {
- ALOGE("Error while linking shaders:");
- GLint infoLen = 0;
- glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen);
- if (infoLen > 1) {
- GLchar log[infoLen];
- glGetProgramInfoLog(programId, infoLen, 0, &log[0]);
- ALOGE("%s", log);
- }
- glDetachShader(programId, vertexId);
- glDetachShader(programId, fragmentId);
- glDeleteShader(vertexId);
- glDeleteShader(fragmentId);
- glDeleteProgram(programId);
- } else {
- mProgram = programId;
- mVertexShader = vertexId;
- mFragmentShader = fragmentId;
- mInitialized = true;
- mProjectionMatrixLoc = glGetUniformLocation(programId, "projection");
- mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
- mSamplerLoc = glGetUniformLocation(programId, "sampler");
- mColorLoc = glGetUniformLocation(programId, "color");
- mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
- mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
- mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
- mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
- mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
- mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
- mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
- mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
-
- // set-up the default values for our uniforms
- glUseProgram(programId);
- glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray());
- glEnableVertexAttribArray(0);
- }
-}
-
-Program::~Program() {
- glDetachShader(mProgram, mVertexShader);
- glDetachShader(mProgram, mFragmentShader);
- glDeleteShader(mVertexShader);
- glDeleteShader(mFragmentShader);
- glDeleteProgram(mProgram);
-}
-
-bool Program::isValid() const {
- return mInitialized;
-}
-
-void Program::use() {
- glUseProgram(mProgram);
-}
-
-GLuint Program::getAttrib(const char* name) const {
- // TODO: maybe use a local cache
- return glGetAttribLocation(mProgram, name);
-}
-
-GLint Program::getUniform(const char* name) const {
- // TODO: maybe use a local cache
- return glGetUniformLocation(mProgram, name);
-}
-
-GLuint Program::buildShader(const char* source, GLenum type) {
- GLuint shader = glCreateShader(type);
- glShaderSource(shader, 1, &source, 0);
- glCompileShader(shader);
- GLint status;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
- if (status != GL_TRUE) {
- // Some drivers return wrong values for GL_INFO_LOG_LENGTH
- // use a fixed size instead
- GLchar log[512];
- glGetShaderInfoLog(shader, sizeof(log), 0, log);
- ALOGE("Error while compiling shader: \n%s\n%s", source, log);
- glDeleteShader(shader);
- return 0;
- }
- return shader;
-}
-
-void Program::setUniforms(const Description& desc) {
- // TODO: we should have a mechanism here to not always reset uniforms that
- // didn't change for this program.
-
- if (mSamplerLoc >= 0) {
- glUniform1i(mSamplerLoc, 0);
- glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
- }
- if (mColorLoc >= 0) {
- const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
- glUniform4fv(mColorLoc, 1, color);
- }
- if (mDisplayColorMatrixLoc >= 0) {
- glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
- }
- if (mInputTransformMatrixLoc >= 0) {
- mat4 inputTransformMatrix = desc.inputTransformMatrix;
- glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
- }
- if (mOutputTransformMatrixLoc >= 0) {
- // The output transform matrix and color matrix can be combined as one matrix
- // that is applied right before applying OETF.
- mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
- glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
- }
- if (mDisplayMaxLuminanceLoc >= 0) {
- glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
- }
- if (mMaxMasteringLuminanceLoc >= 0) {
- glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
- }
- if (mMaxContentLuminanceLoc >= 0) {
- glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
- }
- if (mCornerRadiusLoc >= 0) {
- glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
- }
- if (mCropCenterLoc >= 0) {
- glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
- }
- // these uniforms are always present
- glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
deleted file mode 100644
index 41f1bf8..0000000
--- a/libs/renderengine/gl/Program.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_PROGRAM_H
-#define SF_RENDER_ENGINE_PROGRAM_H
-
-#include <stdint.h>
-
-#include <GLES2/gl2.h>
-#include <renderengine/private/Description.h>
-#include "ProgramCache.h"
-
-namespace android {
-
-class String8;
-
-namespace renderengine {
-namespace gl {
-
-/*
- * Abstracts a GLSL program comprising a vertex and fragment shader
- */
-class Program {
-public:
- // known locations for position and texture coordinates
- enum {
- /* position of each vertex for vertex shader */
- position = 0,
-
- /* UV coordinates for texture mapping */
- texCoords = 1,
-
- /* Crop coordinates, in pixels */
- cropCoords = 2,
-
- /* Shadow color */
- shadowColor = 3,
-
- /* Shadow params */
- shadowParams = 4,
- };
-
- Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
- ~Program();
-
- /* whether this object is usable */
- bool isValid() const;
-
- /* Binds this program to the GLES context */
- void use();
-
- /* Returns the location of the specified attribute */
- GLuint getAttrib(const char* name) const;
-
- /* Returns the location of the specified uniform */
- GLint getUniform(const char* name) const;
-
- /* set-up uniforms from the description */
- void setUniforms(const Description& desc);
-
-private:
- GLuint buildShader(const char* source, GLenum type);
-
- // whether the initialization succeeded
- bool mInitialized;
-
- // Name of the OpenGL program and shaders
- GLuint mProgram;
- GLuint mVertexShader;
- GLuint mFragmentShader;
-
- /* location of the projection matrix uniform */
- GLint mProjectionMatrixLoc;
-
- /* location of the texture matrix uniform */
- GLint mTextureMatrixLoc;
-
- /* location of the sampler uniform */
- GLint mSamplerLoc;
-
- /* location of the color uniform */
- GLint mColorLoc;
-
- /* location of display luminance uniform */
- GLint mDisplayMaxLuminanceLoc;
- /* location of max mastering luminance uniform */
- GLint mMaxMasteringLuminanceLoc;
- /* location of max content luminance uniform */
- GLint mMaxContentLuminanceLoc;
-
- /* location of transform matrix */
- GLint mInputTransformMatrixLoc;
- GLint mOutputTransformMatrixLoc;
- GLint mDisplayColorMatrixLoc;
-
- /* location of corner radius uniform */
- GLint mCornerRadiusLoc;
-
- /* location of surface crop origin uniform, for rounded corner clipping */
- GLint mCropCenterLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
deleted file mode 100644
index 812dda0..0000000
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * Copyright 2013 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "ProgramCache.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <log/log.h>
-#include <renderengine/private/Description.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-#include "Program.h"
-
-ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/*
- * A simple formatter class to automatically add the endl and
- * manage the indentation.
- */
-
-class Formatter;
-static Formatter& indent(Formatter& f);
-static Formatter& dedent(Formatter& f);
-
-class Formatter {
- String8 mString;
- int mIndent;
- typedef Formatter& (*FormaterManipFunc)(Formatter&);
- friend Formatter& indent(Formatter& f);
- friend Formatter& dedent(Formatter& f);
-
-public:
- Formatter() : mIndent(0) {}
-
- String8 getString() const { return mString; }
-
- friend Formatter& operator<<(Formatter& out, const char* in) {
- for (int i = 0; i < out.mIndent; i++) {
- out.mString.append(" ");
- }
- out.mString.append(in);
- out.mString.append("\n");
- return out;
- }
- friend inline Formatter& operator<<(Formatter& out, const String8& in) {
- return operator<<(out, in.string());
- }
- friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
- return (*func)(to);
- }
-};
-Formatter& indent(Formatter& f) {
- f.mIndent++;
- return f;
-}
-Formatter& dedent(Formatter& f) {
- f.mIndent--;
- return f;
-}
-
-void ProgramCache::primeCache(
- EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) {
- auto& cache = mCaches[context];
- uint32_t shaderCount = 0;
-
- if (toneMapperShaderOnly) {
- Key shaderKey;
- // base settings used by HDR->SDR tonemap only
- shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK |
- Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK |
- Key::OPACITY_MASK | Key::ALPHA_MASK |
- Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK,
- Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON |
- Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB |
- Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE |
- Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT);
- for (int i = 0; i < 4; i++) {
- // Cache input transfer for HLG & ST2084
- shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ?
- Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084);
-
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
- return;
- }
-
- uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
- | Key::ROUNDED_CORNERS_MASK;
- // Prime the cache for all combinations of the above masks,
- // leaving off the experimental color matrix mask options.
-
- nsecs_t timeBefore = systemTime();
- for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) {
- Key shaderKey;
- shaderKey.set(keyMask, keyVal);
- uint32_t tex = shaderKey.getTextureTarget();
- if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
- continue;
- }
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
-
- // Prime for sRGB->P3 conversion
- if (useColorManagement) {
- Key shaderKey;
- shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK |
- Key::OUTPUT_TF_MASK,
- Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB |
- Key::OUTPUT_TF_SRGB);
- for (int i = 0; i < 16; i++) {
- shaderKey.set(Key::OPACITY_MASK,
- (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
- shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
-
- // Cache rounded corners
- shaderKey.set(Key::ROUNDED_CORNERS_MASK,
- (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
- // Cache texture off option for window transition
- shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF);
- if (cache.count(shaderKey) == 0) {
- cache.emplace(shaderKey, generateProgram(shaderKey));
- shaderCount++;
- }
- }
- }
-
- nsecs_t timeAfter = systemTime();
- float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs);
-}
-
-ProgramCache::Key ProgramCache::computeKey(const Description& description) {
- Key needs;
- needs.set(Key::TEXTURE_MASK,
- !description.textureEnabled ? Key::TEXTURE_OFF
- : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
- ? Key::TEXTURE_EXT
- : description.texture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D
- : Key::TEXTURE_OFF)
- .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
- .set(Key::BLEND_MASK,
- description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
- .set(Key::OPACITY_MASK,
- description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
- .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
- : Key::INPUT_TRANSFORM_MATRIX_OFF)
- .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
- description.hasOutputTransformMatrix() || description.hasColorMatrix()
- ? Key::OUTPUT_TRANSFORM_MATRIX_ON
- : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
- .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
- description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
- : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
- .set(Key::ROUNDED_CORNERS_MASK,
- description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
- .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
-
- if (needs.hasTransformMatrix() ||
- (description.inputTransferFunction != description.outputTransferFunction)) {
- switch (description.inputTransferFunction) {
- case Description::TransferFunction::LINEAR:
- default:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
- break;
- case Description::TransferFunction::SRGB:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
- break;
- case Description::TransferFunction::ST2084:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
- break;
- case Description::TransferFunction::HLG:
- needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG);
- break;
- }
-
- switch (description.outputTransferFunction) {
- case Description::TransferFunction::LINEAR:
- default:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
- break;
- case Description::TransferFunction::SRGB:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
- break;
- case Description::TransferFunction::ST2084:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
- break;
- case Description::TransferFunction::HLG:
- needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG);
- break;
- }
- }
-
- return needs;
-}
-
-// Generate EOTF that converts signal values to relative display light,
-// both normalized to [0, 1].
-void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) {
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_SRGB:
- fs << R"__SHADER__(
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- vec3 EOTF_sRGB(const vec3 srgb) {
- return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- vec3 EOTF(const vec3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_ST2084:
- fs << R"__SHADER__(
- vec3 EOTF(const highp vec3 color) {
- const highp float m1 = (2610.0 / 4096.0) / 4.0;
- const highp float m2 = (2523.0 / 4096.0) * 128.0;
- const highp float c1 = (3424.0 / 4096.0);
- const highp float c2 = (2413.0 / 4096.0) * 32.0;
- const highp float c3 = (2392.0 / 4096.0) * 32.0;
-
- highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / vec3(m1));
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_HLG:
- fs << R"__SHADER__(
- highp float EOTF_channel(const highp float channel) {
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- vec3 EOTF(const highp vec3 color) {
- return vec3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- vec3 EOTF(const vec3 linear) {
- return linear;
- }
- )__SHADER__";
- break;
- }
-}
-
-void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) {
- // Convert relative light to absolute light.
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- return color * 10000.0;
- }
- )__SHADER__";
- break;
- case Key::INPUT_TF_HLG:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- // The formula is:
- // alpha * pow(Y, gamma - 1.0) * color + beta;
- // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
- return color * 1000.0 * pow(color.y, 0.2);
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 ScaleLuminance(highp vec3 color) {
- return color * displayMaxLuminance;
- }
- )__SHADER__";
- break;
- }
-
- // Tone map absolute light to display luminance range.
- switch (needs.getInputTF()) {
- case Key::INPUT_TF_ST2084:
- case Key::INPUT_TF_HLG:
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_HLG:
- // Right now when mixed PQ and HLG contents are presented,
- // HLG content will always be converted to PQ. However, for
- // completeness, we simply clamp the value to [0.0, 1000.0].
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- return clamp(color, 0.0, 1000.0);
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- return color;
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- float maxMasteringLumi = maxMasteringLuminance;
- float maxContentLumi = maxContentLuminance;
- float maxInLumi = min(maxMasteringLumi, maxContentLumi);
- float maxOutLumi = displayMaxLuminance;
-
- float nits = color.y;
-
- // clamp to max input luminance
- nits = clamp(nits, 0.0, maxInLumi);
-
- // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
- if (maxInLumi <= maxOutLumi) {
- return color * (maxOutLumi / maxInLumi);
- } else {
- // three control points
- const float x0 = 10.0;
- const float y0 = 17.0;
- float x1 = maxOutLumi * 0.75;
- float y1 = x1;
- float x2 = x1 + (maxInLumi - x1) / 2.0;
- float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
- // horizontal distances between the last three control points
- float h12 = x2 - x1;
- float h23 = maxInLumi - x2;
- // tangents at the last three control points
- float m1 = (y2 - y1) / h12;
- float m3 = (maxOutLumi - y2) / h23;
- float m2 = (m1 + m3) / 2.0;
-
- if (nits < x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return color * slope;
- } else if (nits < x1) {
- // scale [x0, x1] to [y0, y1] linearly
- float slope = (y1 - y0) / (x1 - x0);
- nits = y0 + (nits - x0) * slope;
- } else if (nits < x2) {
- // scale [x1, x2] to [y1, y2] using Hermite interp
- float t = (nits - x1) / h12;
- nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
- (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
- } else {
- // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
- float t = (nits - x2) / h23;
- nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
- (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
- }
- }
-
- // color.y is greater than x0 and is thus non-zero
- return color * (nits / color.y);
- }
- )__SHADER__";
- break;
- }
- break;
- default:
- // inverse tone map; the output luminance can be up to maxOutLumi.
- fs << R"__SHADER__(
- highp vec3 ToneMap(highp vec3 color) {
- const float maxOutLumi = 3000.0;
-
- const float x0 = 5.0;
- const float y0 = 2.5;
- float x1 = displayMaxLuminance * 0.7;
- float y1 = maxOutLumi * 0.15;
- float x2 = displayMaxLuminance * 0.9;
- float y2 = maxOutLumi * 0.45;
- float x3 = displayMaxLuminance;
- float y3 = maxOutLumi;
-
- float c1 = y1 / 3.0;
- float c2 = y2 / 2.0;
- float c3 = y3 / 1.5;
-
- float nits = color.y;
-
- float scale;
- if (nits <= x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- const float slope = y0 / x0;
- return color * slope;
- } else if (nits <= x1) {
- // scale [x0, x1] to [y0, y1] using a curve
- float t = (nits - x0) / (x1 - x0);
- nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
- } else if (nits <= x2) {
- // scale [x1, x2] to [y1, y2] using a curve
- float t = (nits - x1) / (x2 - x1);
- nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
- } else {
- // scale [x2, x3] to [y2, y3] using a curve
- float t = (nits - x2) / (x3 - x2);
- nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
- }
-
- // color.y is greater than x0 and is thus non-zero
- return color * (nits / color.y);
- }
- )__SHADER__";
- break;
- }
-
- // convert absolute light to relative light.
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / 10000.0;
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_HLG:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2);
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- highp vec3 NormalizeLuminance(highp vec3 color) {
- return color / displayMaxLuminance;
- }
- )__SHADER__";
- break;
- }
-}
-
-// Generate OOTF that modifies the relative scence light to relative display light.
-void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) {
- if (!needs.needsToneMapping()) {
- fs << R"__SHADER__(
- highp vec3 OOTF(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- } else {
- generateToneMappingProcess(fs, needs);
- fs << R"__SHADER__(
- highp vec3 OOTF(const highp vec3 color) {
- return NormalizeLuminance(ToneMap(ScaleLuminance(color)));
- }
- )__SHADER__";
- }
-}
-
-// Generate OETF that converts relative display light to signal values,
-// both normalized to [0, 1]
-void ProgramCache::generateOETF(Formatter& fs, const Key& needs) {
- switch (needs.getOutputTF()) {
- case Key::OUTPUT_TF_SRGB:
- fs << R"__SHADER__(
- float OETF_sRGB(const float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- vec3 OETF_sRGB(const vec3 linear) {
- return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- vec3 OETF(const vec3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_ST2084:
- fs << R"__SHADER__(
- vec3 OETF(const vec3 linear) {
- const highp float m1 = (2610.0 / 4096.0) / 4.0;
- const highp float m2 = (2523.0 / 4096.0) * 128.0;
- const highp float c1 = (3424.0 / 4096.0);
- const highp float c2 = (2413.0 / 4096.0) * 32.0;
- const highp float c3 = (2392.0 / 4096.0) * 32.0;
-
- highp vec3 tmp = pow(linear, vec3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, vec3(m2));
- }
- )__SHADER__";
- break;
- case Key::OUTPUT_TF_HLG:
- fs << R"__SHADER__(
- highp float OETF_channel(const highp float channel) {
- const highp float a = 0.17883277;
- const highp float b = 0.28466892;
- const highp float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- vec3 OETF(const highp vec3 color) {
- return vec3(OETF_channel(color.r), OETF_channel(color.g),
- OETF_channel(color.b));
- }
- )__SHADER__";
- break;
- default:
- fs << R"__SHADER__(
- vec3 OETF(const vec3 linear) {
- return linear;
- }
- )__SHADER__";
- break;
- }
-}
-
-String8 ProgramCache::generateVertexShader(const Key& needs) {
- Formatter vs;
- if (needs.hasTextureCoords()) {
- vs << "attribute vec4 texCoords;"
- << "varying vec2 outTexCoords;";
- }
- if (needs.hasRoundedCorners()) {
- vs << "attribute lowp vec4 cropCoords;";
- vs << "varying lowp vec2 outCropCoords;";
- }
- if (needs.drawShadows()) {
- vs << "attribute lowp vec4 shadowColor;";
- vs << "varying lowp vec4 outShadowColor;";
- vs << "attribute lowp vec4 shadowParams;";
- vs << "varying lowp vec3 outShadowParams;";
- }
- vs << "attribute vec4 position;"
- << "uniform mat4 projection;"
- << "uniform mat4 texture;"
- << "void main(void) {" << indent << "gl_Position = projection * position;";
- if (needs.hasTextureCoords()) {
- vs << "outTexCoords = (texture * texCoords).st;";
- }
- if (needs.hasRoundedCorners()) {
- vs << "outCropCoords = cropCoords.st;";
- }
- if (needs.drawShadows()) {
- vs << "outShadowColor = shadowColor;";
- vs << "outShadowParams = shadowParams.xyz;";
- }
- vs << dedent << "}";
- return vs.getString();
-}
-
-String8 ProgramCache::generateFragmentShader(const Key& needs) {
- Formatter fs;
- if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "#extension GL_OES_EGL_image_external : require";
- }
-
- // default precision is required-ish in fragment shaders
- fs << "precision mediump float;";
-
- if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
- fs << "uniform samplerExternalOES sampler;";
- } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
- fs << "uniform sampler2D sampler;";
- }
-
- if (needs.hasTextureCoords()) {
- fs << "varying highp vec2 outTexCoords;";
- }
-
- if (needs.hasRoundedCorners()) {
- // Rounded corners implementation using a signed distance function.
- fs << R"__SHADER__(
- uniform float cornerRadius;
- uniform vec2 cropCenter;
- varying vec2 outCropCoords;
-
- /**
- * This function takes the current crop coordinates and calculates an alpha value based
- * on the corner radius and distance from the crop center.
- */
- float applyCornerRadius(vec2 cropCoords)
- {
- vec2 position = cropCoords - cropCenter;
- // Scale down the dist vector here, as otherwise large corner
- // radii can cause floating point issues when computing the norm
- vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0;
- // Once we've found the norm, then scale back up.
- float plane = length(max(dist, vec2(0.0))) * 16.0;
- return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
- }
- )__SHADER__";
- }
-
- if (needs.drawShadows()) {
- fs << R"__SHADER__(
- varying lowp vec4 outShadowColor;
- varying lowp vec3 outShadowParams;
-
- /**
- * Returns the shadow color.
- */
- vec4 getShadowColor()
- {
- lowp float d = length(outShadowParams.xy);
- vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
- lowp float factor = texture2D(sampler, uv).a;
- return outShadowColor * factor;
- }
- )__SHADER__";
- }
-
- if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
- fs << "uniform vec4 color;";
- }
-
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
- needs.hasDisplayColorMatrix()) {
- if (needs.needsToneMapping()) {
- fs << "uniform float displayMaxLuminance;";
- fs << "uniform float maxMasteringLuminance;";
- fs << "uniform float maxContentLuminance;";
- }
-
- if (needs.hasInputTransformMatrix()) {
- fs << "uniform mat4 inputTransformMatrix;";
- fs << R"__SHADER__(
- highp vec3 InputTransform(const highp vec3 color) {
- return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 InputTransform(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- }
-
- // the transformation from a wider colorspace to a narrower one can
- // result in >1.0 or <0.0 pixel values
- if (needs.hasOutputTransformMatrix()) {
- fs << "uniform mat4 outputTransformMatrix;";
- fs << R"__SHADER__(
- highp vec3 OutputTransform(const highp vec3 color) {
- return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 OutputTransform(const highp vec3 color) {
- return clamp(color, 0.0, 1.0);
- }
- )__SHADER__";
- }
-
- if (needs.hasDisplayColorMatrix()) {
- fs << "uniform mat4 displayColorMatrix;";
- fs << R"__SHADER__(
- highp vec3 DisplayColorMatrix(const highp vec3 color) {
- return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
- }
- )__SHADER__";
- } else {
- fs << R"__SHADER__(
- highp vec3 DisplayColorMatrix(const highp vec3 color) {
- return color;
- }
- )__SHADER__";
- }
-
- generateEOTF(fs, needs);
- generateOOTF(fs, needs);
- generateOETF(fs, needs);
- }
-
- fs << "void main(void) {" << indent;
- if (needs.drawShadows()) {
- fs << "gl_FragColor = getShadowColor();";
- } else {
- if (needs.isTexturing()) {
- fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
- } else {
- fs << "gl_FragColor.rgb = color.rgb;";
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.isOpaque()) {
- fs << "gl_FragColor.a = 1.0;";
- }
- }
-
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
- needs.hasDisplayColorMatrix()) {
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // un-premultiply if needed before linearization
- // avoid divide by 0 by adding 0.5/256 to the alpha channel
- fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
- }
- fs << "gl_FragColor.rgb = "
- "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
- ")));";
-
- if (!needs.isOpaque() && needs.isPremultiplied()) {
- // and re-premultiply if needed after gamma correction
- fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
- }
- }
-
- /*
- * Whether applying layer alpha before or after color transform doesn't matter,
- * as long as we can undo premultiplication. But we cannot un-premultiply
- * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0.
- */
- if (!needs.drawShadows()) {
- if (needs.hasAlpha()) {
- // modulate the current alpha value with alpha set
- if (needs.isPremultiplied()) {
- // ... and the color too if we're premultiplied
- fs << "gl_FragColor *= color.a;";
- } else {
- fs << "gl_FragColor.a *= color.a;";
- }
- }
- }
-
- if (needs.hasRoundedCorners()) {
- if (needs.isPremultiplied()) {
- fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
- } else {
- fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
- }
- }
-
- fs << dedent << "}";
- return fs.getString();
-}
-
-std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
- ATRACE_CALL();
-
- // vertex shader
- String8 vs = generateVertexShader(needs);
-
- // fragment shader
- String8 fs = generateFragmentShader(needs);
-
- return std::make_unique<Program>(needs, vs.string(), fs.string());
-}
-
-void ProgramCache::useProgram(EGLContext context, const Description& description) {
- // generate the key for the shader based on the description
- Key needs(computeKey(description));
-
- // look-up the program in the cache
- auto& cache = mCaches[context];
- auto it = cache.find(needs);
- if (it == cache.end()) {
- // we didn't find our program, so generate one...
- nsecs_t time = systemTime();
- it = cache.emplace(needs, generateProgram(needs)).first;
- time = systemTime() - time;
-
- ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)",
- context, needs.mKey, uint32_t(ns2ms(time)), cache.size());
- }
-
- // here we have a suitable program for this description
- std::unique_ptr<Program>& program = it->second;
- if (program->isValid()) {
- program->use();
- program->setUniforms(description);
- }
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
deleted file mode 100644
index b18914f..0000000
--- a/libs/renderengine/gl/ProgramCache.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
-#define SF_RENDER_ENGINE_PROGRAMCACHE_H
-
-#include <memory>
-#include <unordered_map>
-
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-#include <renderengine/private/Description.h>
-#include <utils/Singleton.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-class String8;
-
-namespace renderengine {
-
-struct Description;
-
-namespace gl {
-
-class Formatter;
-class Program;
-
-/*
- * This class generates GLSL programs suitable to handle a given
- * Description. It's responsible for figuring out what to
- * generate from a Description.
- * It also maintains a cache of these Programs.
- */
-class ProgramCache : public Singleton<ProgramCache> {
-public:
- /*
- * Key is used to retrieve a Program in the cache.
- * A Key is generated from a Description.
- */
- class Key {
- friend class ProgramCache;
- typedef uint32_t key_t;
- key_t mKey;
-
- public:
- enum {
- BLEND_SHIFT = 0,
- BLEND_MASK = 1 << BLEND_SHIFT,
- BLEND_PREMULT = 1 << BLEND_SHIFT,
- BLEND_NORMAL = 0 << BLEND_SHIFT,
-
- OPACITY_SHIFT = 1,
- OPACITY_MASK = 1 << OPACITY_SHIFT,
- OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
- OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
-
- ALPHA_SHIFT = 2,
- ALPHA_MASK = 1 << ALPHA_SHIFT,
- ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
- ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
-
- TEXTURE_SHIFT = 3,
- TEXTURE_MASK = 3 << TEXTURE_SHIFT,
- TEXTURE_OFF = 0 << TEXTURE_SHIFT,
- TEXTURE_EXT = 1 << TEXTURE_SHIFT,
- TEXTURE_2D = 2 << TEXTURE_SHIFT,
-
- ROUNDED_CORNERS_SHIFT = 5,
- ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
- ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
- ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
-
- INPUT_TRANSFORM_MATRIX_SHIFT = 6,
- INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
-
- OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
- OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
-
- INPUT_TF_SHIFT = 8,
- INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
- INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
- INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
- INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
- INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
-
- OUTPUT_TF_SHIFT = 10,
- OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
- OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
-
- SHADOW_SHIFT = 13,
- SHADOW_MASK = 1 << SHADOW_SHIFT,
- SHADOW_OFF = 0 << SHADOW_SHIFT,
- SHADOW_ON = 1 << SHADOW_SHIFT,
-
- DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
- DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
- };
-
- inline Key() : mKey(0) {}
- inline Key(const Key& rhs) : mKey(rhs.mKey) {}
-
- inline Key& set(key_t mask, key_t value) {
- mKey = (mKey & ~mask) | value;
- return *this;
- }
-
- inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
- inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
- inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
- inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
- inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
- inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
- inline bool hasRoundedCorners() const {
- return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
- }
- inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
- inline bool hasInputTransformMatrix() const {
- return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
- }
- inline bool hasOutputTransformMatrix() const {
- return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
- }
- inline bool hasDisplayColorMatrix() const {
- return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
- DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
- }
- inline bool hasTransformMatrix() const {
- return hasInputTransformMatrix() || hasOutputTransformMatrix();
- }
- inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
- inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
-
- // When HDR and non-HDR contents are mixed, or different types of HDR contents are
- // mixed, we will do a tone mapping process to tone map the input content to output
- // content. Currently, the following conversions handled, they are:
- // * SDR -> HLG
- // * SDR -> PQ
- // * HLG -> PQ
- inline bool needsToneMapping() const {
- int inputTF = getInputTF();
- int outputTF = getOutputTF();
-
- // Return false when converting from SDR to SDR.
- if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) {
- return false;
- }
- if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) {
- return false;
- }
-
- inputTF >>= Key::INPUT_TF_SHIFT;
- outputTF >>= Key::OUTPUT_TF_SHIFT;
- return inputTF != outputTF;
- }
-
- // for use by std::unordered_map
-
- bool operator==(const Key& other) const { return mKey == other.mKey; }
-
- struct Hash {
- size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
- };
- };
-
- ProgramCache() = default;
- ~ProgramCache() = default;
-
- // Generate shaders to populate the cache
- void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly);
-
- size_t getSize(const EGLContext context) { return mCaches[context].size(); }
-
- // useProgram lookup a suitable program in the cache or generates one
- // if none can be found.
- void useProgram(const EGLContext context, const Description& description);
-
- void purgeCaches() { mCaches.clear(); }
-
-private:
- // compute a cache Key from a Description
- static Key computeKey(const Description& description);
- // Generate EOTF based from Key.
- static void generateEOTF(Formatter& fs, const Key& needs);
- // Generate necessary tone mapping methods for OOTF.
- static void generateToneMappingProcess(Formatter& fs, const Key& needs);
- // Generate OOTF based from Key.
- static void generateOOTF(Formatter& fs, const Key& needs);
- // Generate OETF based from Key.
- static void generateOETF(Formatter& fs, const Key& needs);
- // generates a program from the Key
- static std::unique_ptr<Program> generateProgram(const Key& needs);
- // generates the vertex shader from the Key
- static String8 generateVertexShader(const Key& needs);
- // generates the fragment shader from the Key
- static String8 generateFragmentShader(const Key& needs);
-
- // Key/Value map used for caching Programs. Currently the cache
- // is never shrunk (and the GL program objects are never deleted).
- std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>>
- mCaches;
-};
-
-} // namespace gl
-} // namespace renderengine
-
-ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
-
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
deleted file mode 100644
index 3455e08..0000000
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2019 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "BlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-#include <cstdint>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine),
- mCompositionFbo(engine),
- mPingFbo(engine),
- mPongFbo(engine),
- mMixProgram(engine),
- mBlurProgram(engine) {
- mMixProgram.compile(getVertexShader(), getMixFragShader());
- mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
- mMUvLoc = mMixProgram.getAttributeLocation("aUV");
- mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
- mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
- mMMixLoc = mMixProgram.getUniformLocation("uMix");
-
- mBlurProgram.compile(getVertexShader(), getFragmentShader());
- mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
- mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
- mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
- mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
-
- static constexpr auto size = 2.0f;
- static constexpr auto translation = 1.0f;
- const GLfloat vboData[] = {
- // Vertex data
- translation - size, -translation - size,
- translation - size, -translation + size,
- translation + size, -translation + size,
- // UV data
- 0.0f, 0.0f - translation,
- 0.0f, size - translation,
- size, size - translation
- };
- mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
-}
-
-status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
- ATRACE_NAME("BlurFilter::setAsDrawTarget");
- mRadius = radius;
- mDisplayX = display.physicalDisplay.left;
- mDisplayY = display.physicalDisplay.top;
-
- if (mDisplayWidth < display.physicalDisplay.width() ||
- mDisplayHeight < display.physicalDisplay.height()) {
- ATRACE_NAME("BlurFilter::allocatingTextures");
-
- mDisplayWidth = display.physicalDisplay.width();
- mDisplayHeight = display.physicalDisplay.height();
- mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
-
- const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
- const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
- mPingFbo.allocateBuffers(fboWidth, fboHeight);
- mPongFbo.allocateBuffers(fboWidth, fboHeight);
-
- if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid ping buffer");
- return mPingFbo.getStatus();
- }
- if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid pong buffer");
- return mPongFbo.getStatus();
- }
- if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid composition buffer");
- return mCompositionFbo.getStatus();
- }
- if (!mBlurProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
- }
-
- mCompositionFbo.bind();
- glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
- return NO_ERROR;
-}
-
-void BlurFilter::drawMesh(GLuint uv, GLuint position) {
-
- glEnableVertexAttribArray(uv);
- glEnableVertexAttribArray(position);
- mMeshBuffer.bind();
- glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
- 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
- glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
- (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
- mMeshBuffer.unbind();
-
- // draw mesh
- glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
-}
-
-status_t BlurFilter::prepare() {
- ATRACE_NAME("BlurFilter::prepare");
-
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- const auto radius = mRadius / 6.0f;
-
- // Calculate how many passes we'll do, based on the radius.
- // Too many passes will make the operation expensive.
- const auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
-
- const float radiusByPasses = radius / (float)passes;
- const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth();
- const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight();
-
- // Let's start by downsampling and blurring the composited frame simultaneously.
- mBlurProgram.useProgram();
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mBTextureLoc, 0);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glUniform2f(mBOffsetLoc, stepX, stepY);
- glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight());
- mPingFbo.bind();
- drawMesh(mBUvLoc, mBPosLoc);
-
- // And now we'll ping pong between our textures, to accumulate the result of various offsets.
- GLFramebuffer* read = &mPingFbo;
- GLFramebuffer* draw = &mPongFbo;
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
- for (auto i = 1; i < passes; i++) {
- ATRACE_NAME("BlurFilter::renderPass");
- draw->bind();
-
- glBindTexture(GL_TEXTURE_2D, read->getTextureName());
- glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
-
- drawMesh(mBUvLoc, mBPosLoc);
-
- // Swap buffers for next iteration
- auto tmp = draw;
- draw = read;
- read = tmp;
- }
- mLastDrawTarget = read;
-
- return NO_ERROR;
-}
-
-status_t BlurFilter::render(bool multiPass) {
- ATRACE_NAME("BlurFilter::render");
-
- // Now let's scale our blur up. It will be interpolated with the larger composited
- // texture for the first frames, to hide downscaling artifacts.
- GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
-
- // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
- // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
- // as large as the screen size.
- if (mix >= 1 || multiPass) {
- mLastDrawTarget->bindAsReadBuffer();
- glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
- mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth,
- mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
- return NO_ERROR;
- }
-
- mMixProgram.useProgram();
- glUniform1f(mMMixLoc, mix);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
- glUniform1i(mMTextureLoc, 0);
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
- glUniform1i(mMCompositionTextureLoc, 1);
-
- drawMesh(mMUvLoc, mMPosLoc);
-
- glUseProgram(0);
- glActiveTexture(GL_TEXTURE0);
- mEngine.checkErrors("Drawing blur mesh");
- return NO_ERROR;
-}
-
-string BlurFilter::getVertexShader() const {
- return R"SHADER(#version 300 es
- precision mediump float;
-
- in vec2 aPosition;
- in highp vec2 aUV;
- out highp vec2 vUV;
-
- void main() {
- vUV = aUV;
- gl_Position = vec4(aPosition, 0.0, 1.0);
- }
- )SHADER";
-}
-
-string BlurFilter::getFragmentShader() const {
- return R"SHADER(#version 300 es
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform vec2 uOffset;
-
- in highp vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- fragColor = texture(uTexture, vUV, 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
-
- fragColor = vec4(fragColor.rgb * 0.2, 1.0);
- }
- )SHADER";
-}
-
-string BlurFilter::getMixFragShader() const {
- string shader = R"SHADER(#version 300 es
- precision mediump float;
-
- in highp vec2 vUV;
- out vec4 fragColor;
-
- uniform sampler2D uCompositionTexture;
- uniform sampler2D uTexture;
- uniform float uMix;
-
- void main() {
- vec4 blurred = texture(uTexture, vUV);
- vec4 composition = texture(uCompositionTexture, vUV);
- fragColor = mix(composition, blurred, uMix);
- }
- )SHADER";
- return shader;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
deleted file mode 100644
index 593a8fd..0000000
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2019 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "../GLVertexBuffer.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
-class BlurFilter {
-public:
- // Downsample FBO to improve performance
- static constexpr float kFboScale = 0.25f;
- // Maximum number of render passes
- static constexpr uint32_t kMaxPasses = 4;
- // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
- // image, up to this radius.
- static constexpr float kMaxCrossFadeRadius = 30.0f;
-
- explicit BlurFilter(GLESRenderEngine& engine);
- virtual ~BlurFilter(){};
-
- // Set up render targets, redirecting output to offscreen texture.
- status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
- // Execute blur passes, rendering to offscreen texture.
- status_t prepare();
- // Render blur to the bound framebuffer (screen).
- status_t render(bool multiPass);
-
-private:
- uint32_t mRadius;
- void drawMesh(GLuint uv, GLuint position);
- string getVertexShader() const;
- string getFragmentShader() const;
- string getMixFragShader() const;
-
- GLESRenderEngine& mEngine;
- // Frame buffer holding the composited background.
- GLFramebuffer mCompositionFbo;
- // Frame buffers holding the blur passes.
- GLFramebuffer mPingFbo;
- GLFramebuffer mPongFbo;
- uint32_t mDisplayWidth = 0;
- uint32_t mDisplayHeight = 0;
- uint32_t mDisplayX = 0;
- uint32_t mDisplayY = 0;
- // Buffer holding the final blur pass.
- GLFramebuffer* mLastDrawTarget;
-
- // VBO containing vertex and uv data of a fullscreen triangle.
- GLVertexBuffer mMeshBuffer;
-
- GenericProgram mMixProgram;
- GLuint mMPosLoc;
- GLuint mMUvLoc;
- GLuint mMMixLoc;
- GLuint mMTextureLoc;
- GLuint mMCompositionTextureLoc;
-
- GenericProgram mBlurProgram;
- GLuint mBPosLoc;
- GLuint mBUvLoc;
- GLuint mBTextureLoc;
- GLuint mBOffsetLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp
deleted file mode 100644
index bb35889..0000000
--- a/libs/renderengine/gl/filters/GenericProgram.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2019 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 "GenericProgram.h"
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
-
-GenericProgram::~GenericProgram() {
- if (mVertexShaderHandle != 0) {
- if (mProgramHandle != 0) {
- glDetachShader(mProgramHandle, mVertexShaderHandle);
- }
- glDeleteShader(mVertexShaderHandle);
- }
-
- if (mFragmentShaderHandle != 0) {
- if (mProgramHandle != 0) {
- glDetachShader(mProgramHandle, mFragmentShaderHandle);
- }
- glDeleteShader(mFragmentShaderHandle);
- }
-
- if (mProgramHandle != 0) {
- glDeleteProgram(mProgramHandle);
- }
-}
-
-void GenericProgram::compile(string vertexShader, string fragmentShader) {
- mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
- mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
- if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
- ALOGE("Aborting program creation.");
- return;
- }
- mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
- mEngine.checkErrors("Linking program");
-}
-
-void GenericProgram::useProgram() const {
- glUseProgram(mProgramHandle);
-}
-
-GLuint GenericProgram::compileShader(GLuint type, string src) const {
- const GLuint shader = glCreateShader(type);
- if (shader == 0) {
- mEngine.checkErrors("Creating shader");
- return 0;
- }
- const GLchar* charSrc = (const GLchar*)src.c_str();
- glShaderSource(shader, 1, &charSrc, nullptr);
- glCompileShader(shader);
-
- GLint isCompiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
- if (isCompiled == GL_FALSE) {
- GLint maxLength = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
- string errorLog;
- errorLog.reserve(maxLength);
- glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
- glDeleteShader(shader);
- ALOGE("Error compiling shader: %s", errorLog.c_str());
- return 0;
- }
- return shader;
-}
-GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
- const GLuint program = glCreateProgram();
- mEngine.checkErrors("Creating program");
-
- glAttachShader(program, vertexShader);
- glAttachShader(program, fragmentShader);
- glLinkProgram(program);
- mEngine.checkErrors("Linking program");
- return program;
-}
-
-GLuint GenericProgram::getUniformLocation(const string name) const {
- if (mProgramHandle == 0) {
- ALOGE("Can't get location of %s on an invalid program.", name.c_str());
- return -1;
- }
- return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
-}
-
-GLuint GenericProgram::getAttributeLocation(const string name) const {
- if (mProgramHandle == 0) {
- ALOGE("Can't get location of %s on an invalid program.", name.c_str());
- return -1;
- }
- return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
-}
-
-bool GenericProgram::isValid() const {
- return mProgramHandle != 0;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h
deleted file mode 100644
index 6da2a5a..0000000
--- a/libs/renderengine/gl/filters/GenericProgram.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019 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 <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GenericProgram {
-public:
- explicit GenericProgram(GLESRenderEngine& renderEngine);
- ~GenericProgram();
- void compile(string vertexShader, string fragmentShader);
- bool isValid() const;
- void useProgram() const;
- GLuint getAttributeLocation(const string name) const;
- GLuint getUniformLocation(const string name) const;
-
-private:
- GLuint compileShader(GLuint type, const string src) const;
- GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
-
- GLESRenderEngine& mEngine;
- GLuint mVertexShaderHandle = 0;
- GLuint mFragmentShaderHandle = 0;
- GLuint mProgramHandle = 0;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h
deleted file mode 100644
index 6511127..0000000
--- a/libs/renderengine/include/renderengine/Framebuffer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2018 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 <cstdint>
-
-struct ANativeWindowBuffer;
-
-namespace android {
-namespace renderengine {
-
-class Framebuffer {
-public:
- virtual ~Framebuffer() = default;
-
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
- const bool useFramebufferCache) = 0;
-};
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b501c40..28aa4dd 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -46,10 +46,6 @@
// Fence that will fire when the buffer is ready to be bound.
sp<Fence> fence = nullptr;
- // Texture identifier to bind the external texture to.
- // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
- uint32_t textureName = 0;
-
// Whether to use filtering when rendering the texture.
bool useTextureFiltering = false;
@@ -182,7 +178,6 @@
// compositionengine/impl/ClientCompositionRequestCache.cpp
static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
- lhs.textureName == rhs.textureName &&
lhs.useTextureFiltering == rhs.useTextureFiltering &&
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
@@ -237,7 +232,6 @@
<< (settings.buffer.get() ? decodePixelFormat(settings.buffer->getPixelFormat()).c_str()
: "");
*os << "\n .fence = " << settings.fence.get();
- *os << "\n .textureName = " << settings.textureName;
*os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
*os << "\n .textureTransform = ";
PrintMatrix(settings.textureTransform, os);
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
deleted file mode 100644
index 167f13f..0000000
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_MESH_H
-#define SF_RENDER_ENGINE_MESH_H
-
-#include <vector>
-
-#include <stdint.h>
-
-namespace android {
-namespace renderengine {
-
-class Mesh {
-public:
- class Builder;
-
- enum Primitive {
- TRIANGLES = 0x0004, // GL_TRIANGLES
- TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
- TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
- };
-
- ~Mesh() = default;
-
- /*
- * VertexArray handles the stride automatically.
- */
- template <typename TYPE>
- class VertexArray {
- friend class Mesh;
- float* mData;
- size_t mStride;
- size_t mOffset = 0;
- VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
-
- public:
- // Returns a vertex array at an offset so its easier to append attributes from
- // multiple sources.
- VertexArray(VertexArray<TYPE>& other, size_t offset)
- : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
-
- TYPE& operator[](size_t index) {
- return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
- }
- TYPE const& operator[](size_t index) const {
- return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
- }
- };
-
- template <typename TYPE>
- VertexArray<TYPE> getPositionArray() {
- return VertexArray<TYPE>(getPositions(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getTexCoordArray() {
- return VertexArray<TYPE>(getTexCoords(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getCropCoordArray() {
- return VertexArray<TYPE>(getCropCoords(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getShadowColorArray() {
- return VertexArray<TYPE>(getShadowColor(), mStride);
- }
-
- template <typename TYPE>
- VertexArray<TYPE> getShadowParamsArray() {
- return VertexArray<TYPE>(getShadowParams(), mStride);
- }
-
- uint16_t* getIndicesArray() { return getIndices(); }
-
- Primitive getPrimitive() const;
-
- // returns a pointer to the vertices positions
- float const* getPositions() const;
-
- // returns a pointer to the vertices texture coordinates
- float const* getTexCoords() const;
-
- // returns a pointer to the vertices crop coordinates
- float const* getCropCoords() const;
-
- // returns a pointer to colors
- float const* getShadowColor() const;
-
- // returns a pointer to the shadow params
- float const* getShadowParams() const;
-
- // returns a pointer to indices
- uint16_t const* getIndices() const;
-
- // number of vertices in this mesh
- size_t getVertexCount() const;
-
- // dimension of vertices
- size_t getVertexSize() const;
-
- // dimension of texture coordinates
- size_t getTexCoordsSize() const;
-
- size_t getShadowParamsSize() const;
-
- size_t getShadowColorSize() const;
-
- size_t getIndexCount() const;
-
- // return stride in bytes
- size_t getByteStride() const;
-
- // return stride in floats
- size_t getStride() const;
-
-private:
- Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
- size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
- Mesh(const Mesh&);
- Mesh& operator=(const Mesh&);
- Mesh const& operator=(const Mesh&) const;
-
- float* getPositions();
- float* getTexCoords();
- float* getCropCoords();
- float* getShadowColor();
- float* getShadowParams();
- uint16_t* getIndices();
-
- std::vector<float> mVertices;
- size_t mVertexCount;
- size_t mVertexSize;
- size_t mTexCoordsSize;
- size_t mCropCoordsSize;
- size_t mShadowColorSize;
- size_t mShadowParamsSize;
- size_t mStride;
- Primitive mPrimitive;
- std::vector<uint16_t> mIndices;
- size_t mIndexCount;
-};
-
-class Mesh::Builder {
-public:
- Builder& setPrimitive(Primitive primitive) {
- mPrimitive = primitive;
- return *this;
- };
- Builder& setVertices(size_t vertexCount, size_t vertexSize) {
- mVertexCount = vertexCount;
- mVertexSize = vertexSize;
- return *this;
- };
- Builder& setTexCoords(size_t texCoordsSize) {
- mTexCoordsSize = texCoordsSize;
- return *this;
- };
- Builder& setCropCoords(size_t cropCoordsSize) {
- mCropCoordsSize = cropCoordsSize;
- return *this;
- };
- Builder& setShadowAttrs() {
- mShadowParamsSize = 3;
- mShadowColorSize = 4;
- return *this;
- };
- Builder& setIndices(size_t indexCount) {
- mIndexCount = indexCount;
- return *this;
- };
- Mesh build() const {
- return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
- mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
- }
-
-private:
- size_t mVertexCount = 0;
- size_t mVertexSize = 0;
- size_t mTexCoordsSize = 0;
- size_t mCropCoordsSize = 0;
- size_t mShadowColorSize = 0;
- size_t mShadowParamsSize = 0;
- size_t mIndexCount = 0;
- Primitive mPrimitive;
-};
-
-} // namespace renderengine
-} // namespace android
-#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 0d910c9..b992d82 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -22,8 +22,6 @@
#include <math/mat4.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/ExternalTexture.h>
-#include <renderengine/Framebuffer.h>
-#include <renderengine/Image.h>
#include <renderengine/LayerSettings.h>
#include <stdint.h>
#include <sys/types.h>
@@ -95,8 +93,6 @@
};
enum class RenderEngineType {
- GLES = 1,
- THREADED = 2,
SKIA_GL = 3,
SKIA_GL_THREADED = 4,
SKIA_VK = 5,
@@ -116,9 +112,6 @@
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
- virtual void genTextures(size_t count, uint32_t* names) = 0;
- virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
-
// queries that are required to be thread safe
virtual size_t getMaxTextureSize() const = 0;
virtual size_t getMaxViewportDims() const = 0;
@@ -152,9 +145,6 @@
// @param layers The layers to draw onto the display, in Z-order.
// @param buffer The buffer which will be drawn to. This buffer will be
// ready once drawFence fires.
- // @param useFramebufferCache True if the framebuffer cache should be used.
- // If an implementation does not cache output framebuffers, then this
- // parameter does nothing.
// @param bufferFence Fence signalling that the buffer is ready to be drawn
// to.
// @return A future object of FenceResult indicating whether drawing was
@@ -162,7 +152,6 @@
virtual ftl::Future<FenceResult> drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
base::unique_fd&& bufferFence);
// Clean-up method that should be called on the main thread after the
@@ -171,8 +160,6 @@
// being drawn, then the implementation is free to silently ignore this call.
virtual void cleanupPostRender() = 0;
- virtual void cleanFramebufferCache() = 0;
-
// Returns the priority this context was actually created with. Note: this
// may not be the same as specified at context creation time, due to
// implementation limits on the number of contexts that can be created at a
@@ -204,7 +191,7 @@
virtual void setEnableTracing(bool /*tracingEnabled*/) {}
protected:
- RenderEngine() : RenderEngine(RenderEngineType::GLES) {}
+ RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {}
RenderEngine(RenderEngineType type) : mRenderEngineType(type) {}
@@ -253,8 +240,7 @@
virtual void drawLayersInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) = 0;
+ const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0;
};
struct RenderEngineCreationArgs {
@@ -271,14 +257,13 @@
private:
// must be created by Builder via constructor with full argument list
- RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+ RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize,
bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
bool _supportsBackgroundBlur,
RenderEngine::ContextPriority _contextPriority,
RenderEngine::RenderEngineType _renderEngineType)
: pixelFormat(_pixelFormat),
imageCacheSize(_imageCacheSize),
- useColorManagement(_useColorManagement),
enableProtectedContext(_enableProtectedContext),
precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
supportsBackgroundBlur(_supportsBackgroundBlur),
@@ -298,10 +283,6 @@
this->imageCacheSize = imageCacheSize;
return *this;
}
- Builder& setUseColorManagerment(bool useColorManagement) {
- this->useColorManagement = useColorManagement;
- return *this;
- }
Builder& setEnableProtectedContext(bool enableProtectedContext) {
this->enableProtectedContext = enableProtectedContext;
return *this;
@@ -323,16 +304,15 @@
return *this;
}
RenderEngineCreationArgs build() const {
- return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
- enableProtectedContext, precacheToneMapperShaderOnly,
- supportsBackgroundBlur, contextPriority, renderEngineType);
+ return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext,
+ precacheToneMapperShaderOnly, supportsBackgroundBlur,
+ contextPriority, renderEngineType);
}
private:
// 1 means RGBA_8888
int pixelFormat = 1;
uint32_t imageCacheSize = 0;
- bool useColorManagement = true;
bool enableProtectedContext = false;
bool precacheToneMapperShaderOnly = false;
bool supportsBackgroundBlur = false;
diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h
deleted file mode 100644
index c69ace0..0000000
--- a/libs/renderengine/include/renderengine/Texture.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_TEXTURE_H
-#define SF_RENDER_ENGINE_TEXTURE_H
-
-#include <stdint.h>
-
-#include <math/mat4.h>
-
-namespace android {
-namespace renderengine {
-
-class Texture {
-public:
- enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
-
- Texture();
- Texture(Target textureTarget, uint32_t textureName);
- ~Texture();
-
- void init(Target textureTarget, uint32_t textureName);
-
- void setMatrix(float const* matrix);
- void setFiltering(bool enabled);
- void setDimensions(size_t width, size_t height);
-
- uint32_t getTextureName() const;
- uint32_t getTextureTarget() const;
-
- const mat4& getMatrix() const;
- bool getFiltering() const;
- size_t getWidth() const;
- size_t getHeight() const;
-
-private:
- uint32_t mTextureName;
- uint32_t mTextureTarget;
- size_t mWidth;
- size_t mHeight;
- bool mFiltering;
- mat4 mTextureMatrix;
-};
-
-} // namespace renderengine
-} // namespace android
-#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/libs/renderengine/include/renderengine/mock/Framebuffer.h b/libs/renderengine/include/renderengine/mock/Framebuffer.h
deleted file mode 100644
index dfb6a4e..0000000
--- a/libs/renderengine/include/renderengine/mock/Framebuffer.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2018 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 <gmock/gmock.h>
-#include <renderengine/Framebuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-class Framebuffer : public renderengine::Framebuffer {
-public:
- Framebuffer();
- ~Framebuffer() override;
-
- MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool));
-};
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/Image.h b/libs/renderengine/include/renderengine/mock/Image.h
deleted file mode 100644
index 2b0eed1..0000000
--- a/libs/renderengine/include/renderengine/mock/Image.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2018 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 <gmock/gmock.h>
-#include <renderengine/Image.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-class Image : public renderengine::Image {
-public:
- Image();
- ~Image() override;
-
- MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected));
-};
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index d3035e2..160006d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -19,9 +19,7 @@
#include <gmock/gmock.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
-#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
-#include <renderengine/Texture.h>
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
@@ -37,9 +35,6 @@
MOCK_METHOD0(primeCache, std::future<void>());
MOCK_METHOD1(dump, void(std::string&));
- MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
- MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
- MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
MOCK_CONST_METHOD0(isProtected, bool());
@@ -47,15 +42,14 @@
MOCK_METHOD1(useProtectedContext, void(bool));
MOCK_METHOD0(cleanupPostRender, void());
MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
- MOCK_METHOD5(drawLayers,
+ MOCK_METHOD4(drawLayers,
ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&,
- const std::shared_ptr<ExternalTexture>&, const bool,
+ const std::shared_ptr<ExternalTexture>&,
base::unique_fd&&));
- MOCK_METHOD6(drawLayersInternal,
+ MOCK_METHOD5(drawLayersInternal,
void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&,
const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&,
- const bool, base::unique_fd&&));
- MOCK_METHOD0(cleanFramebufferCache, void());
+ base::unique_fd&&));
MOCK_METHOD0(getContextPriority, int());
MOCK_METHOD0(supportsBackgroundBlur, bool());
MOCK_METHOD1(onActiveDisplaySizeChanged, void(ui::Size));
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
deleted file mode 100644
index 2873ad7..0000000
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
-#define SF_RENDER_ENGINE_DESCRIPTION_H_
-
-#include <renderengine/Texture.h>
-#include <ui/GraphicTypes.h>
-
-namespace android {
-namespace renderengine {
-
-/*
- * This is the structure that holds the state of the rendering engine.
- * This class is used to generate a corresponding GLSL program and set the
- * appropriate uniform.
- */
-struct Description {
- enum class TransferFunction : int {
- LINEAR,
- SRGB,
- ST2084,
- HLG, // Hybrid Log-Gamma for HDR.
- };
-
- static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
-
- Description() = default;
- ~Description() = default;
-
- bool hasInputTransformMatrix() const;
- bool hasOutputTransformMatrix() const;
- bool hasColorMatrix() const;
- bool hasDisplayColorMatrix() const;
-
- // whether textures are premultiplied
- bool isPremultipliedAlpha = false;
- // whether this layer is marked as opaque
- bool isOpaque = true;
-
- // corner radius of the layer
- float cornerRadius = 0;
-
- // Size of the rounded rectangle we are cropping to
- half2 cropSize;
-
- // Texture this layer uses
- Texture texture;
- bool textureEnabled = false;
-
- // color used when texturing is disabled or when setting alpha.
- half4 color;
-
- // transfer functions for the input/output
- TransferFunction inputTransferFunction = TransferFunction::LINEAR;
- TransferFunction outputTransferFunction = TransferFunction::LINEAR;
-
- float displayMaxLuminance;
- float maxMasteringLuminance;
- float maxContentLuminance;
-
- // projection matrix
- mat4 projectionMatrix;
-
- // The color matrix will be applied in linear space right before OETF.
- mat4 colorMatrix;
- // The display color matrix will be applied in gamma space after OETF
- mat4 displayColorMatrix;
- mat4 inputTransformMatrix;
- mat4 outputTransformMatrix;
-
- // True if this layer will draw a shadow.
- bool drawShadows = false;
-};
-
-} // namespace renderengine
-} // namespace android
-
-#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/libs/renderengine/mock/Framebuffer.cpp b/libs/renderengine/mock/Framebuffer.cpp
deleted file mode 100644
index fbdcaab..0000000
--- a/libs/renderengine/mock/Framebuffer.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2018 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 <renderengine/mock/Framebuffer.h>
-
-namespace android {
-namespace renderengine {
-namespace mock {
-
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Framebuffer::Framebuffer() = default;
-Framebuffer::~Framebuffer() = default;
-
-} // namespace mock
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index dad3c19..02d1e1e 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -23,7 +23,8 @@
#include <SkImage.h>
#include <include/gpu/ganesh/SkImageGanesh.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/vk/GrVkTypes.h>
#include <android/hardware_buffer.h>
#include "ColorSpaces.h"
#include "log/log_main.h"
@@ -40,13 +41,44 @@
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(buffer, &desc);
bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
- GrBackendFormat backendFormat =
- GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height,
- &mDeleteProc, &mUpdateProc, &mImageCtx,
- createProtectedImage, backendFormat,
- isOutputBuffer);
+ GrBackendFormat backendFormat;
+
+ GrBackendApi backend = context->backend();
+ if (backend == GrBackendApi::kOpenGL) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeGLBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ isOutputBuffer);
+ } else if (backend == GrBackendApi::kVulkan) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
+ buffer,
+ desc.format,
+ false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
+ buffer,
+ desc.width,
+ desc.height,
+ &mDeleteProc,
+ &mUpdateProc,
+ &mImageCtx,
+ createProtectedImage,
+ backendFormat,
+ isOutputBuffer);
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+ }
+
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
@@ -94,7 +126,7 @@
switch (tex.backend()) {
case GrBackendApi::kOpenGL: {
GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo);
+ bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo);
LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
"\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
"texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index f6b9183..9d61d62 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -29,8 +29,6 @@
namespace android::renderengine::skia {
namespace {
-// Warming shader cache, not framebuffer cache.
-constexpr bool kUseFrameBufferCache = false;
// clang-format off
// Any non-identity matrix will do.
@@ -65,9 +63,12 @@
.geometry =
Geometry{
.boundaries = rect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
+ .alpha = 1,
+ // setting this is mandatory for shadows and blurs
+ .skipContentDraw = true,
// drawShadow ignores alpha
.shadow =
ShadowSettings{
@@ -78,16 +79,13 @@
.lightRadius = 2500.0f,
.length = 15.f,
},
- // setting this is mandatory for shadows and blurs
- .skipContentDraw = true,
- .alpha = 1,
};
LayerSettings caster{
.geometry =
Geometry{
.boundaries = smallerRect,
- .roundedCornersCrop = rect,
.roundedCornersRadius = {50.f, 50.f},
+ .roundedCornersCrop = rect,
},
.source =
PixelSource{
@@ -116,8 +114,7 @@
caster.geometry.positionTransform = transform;
auto layers = std::vector<LayerSettings>{layer, caster};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
}
@@ -129,10 +126,10 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// The position transform doesn't matter when the reduced shader mode
// in in effect. A matrix transform stage is always included.
.positionTransform = mat4(),
- .boundaries = rect,
.roundedCornersCrop = rect,
},
.source = PixelSource{.buffer =
@@ -154,8 +151,7 @@
for (auto alpha : {half(.2f), half(1.0f)}) {
layer.alpha = alpha;
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
}
}
@@ -183,8 +179,7 @@
for (float roundedCornersRadius : {0.0f, 50.f}) {
layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius};
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
}
}
@@ -207,8 +202,7 @@
for (int radius : {9, 60}) {
layer.backgroundBlurRadius = radius;
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
}
@@ -254,8 +248,7 @@
for (float alpha : {0.5f, 1.f}) {
layer.alpha = alpha;
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
}
}
@@ -270,11 +263,11 @@
LayerSettings layer{
.geometry =
Geometry{
+ .boundaries = rect,
// Note that this flip matrix only makes a difference when clipping,
// which happens in this layer because the roundrect crop is just a bit
// larger than the layer bounds.
.positionTransform = kFlip,
- .boundaries = rect,
.roundedCornersRadius = {94.2551f, 94.2551f},
.roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75,
displayRect.height()),
@@ -282,17 +275,17 @@
.source = PixelSource{.buffer =
Buffer{
.buffer = srcTexture,
- .maxLuminanceNits = 1000.f,
- .isOpaque = 0,
.usePremultipliedAlpha = 1,
+ .isOpaque = 0,
+ .maxLuminanceNits = 1000.f,
}},
- .sourceDataspace = kOtherDataSpace,
.alpha = 1,
+ .sourceDataspace = kOtherDataSpace,
};
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -303,10 +296,10 @@
LayerSettings layer{
.geometry =
Geometry{
- .positionTransform = kScaleAndTranslate,
// the boundaries have to be smaller than the rounded crop so that
// clipRRect is used instead of drawRRect
.boundaries = small,
+ .positionTransform = kScaleAndTranslate,
.roundedCornersRadius = {50.f, 50.f},
.roundedCornersCrop = rect,
},
@@ -314,14 +307,14 @@
PixelSource{
.solidColor = half3(0.f, 0.f, 0.f),
},
- .sourceDataspace = kDestDataSpace,
.alpha = 0,
+ .sourceDataspace = kDestDataSpace,
.disableBlending = true,
};
auto layers = std::vector<LayerSettings>{layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
}
//
@@ -436,9 +429,7 @@
};
auto layers = std::vector<LayerSettings>{layer};
// call get() to make it synchronous
- renderengine
- ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd())
- .get();
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get();
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/skia/GLExtensions.cpp
similarity index 92%
rename from libs/renderengine/gl/GLExtensions.cpp
rename to libs/renderengine/skia/GLExtensions.cpp
index 3dd534e..1d4d35f 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/skia/GLExtensions.cpp
@@ -23,11 +23,11 @@
#include <stdio.h>
#include <stdlib.h>
-ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::skia::GLExtensions)
namespace android {
namespace renderengine {
-namespace gl {
+namespace skia {
namespace {
@@ -68,19 +68,19 @@
}
char const* GLExtensions::getVendor() const {
- return mVendor.string();
+ return mVendor.c_str();
}
char const* GLExtensions::getRenderer() const {
- return mRenderer.string();
+ return mRenderer.c_str();
}
char const* GLExtensions::getVersion() const {
- return mVersion.string();
+ return mVersion.c_str();
}
char const* GLExtensions::getExtensions() const {
- return mExtensions.string();
+ return mExtensions.c_str();
}
void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
@@ -127,13 +127,13 @@
}
char const* GLExtensions::getEGLVersion() const {
- return mEGLVersion.string();
+ return mEGLVersion.c_str();
}
char const* GLExtensions::getEGLExtensions() const {
- return mEGLExtensions.string();
+ return mEGLExtensions.c_str();
}
-} // namespace gl
+} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/skia/GLExtensions.h
similarity index 98%
rename from libs/renderengine/gl/GLExtensions.h
rename to libs/renderengine/skia/GLExtensions.h
index e415ff3..0cb1bda 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/skia/GLExtensions.h
@@ -29,7 +29,7 @@
namespace android {
namespace renderengine {
-namespace gl {
+namespace skia {
class GLExtensions : public Singleton<GLExtensions> {
public:
@@ -81,7 +81,7 @@
GLExtensions& operator=(const GLExtensions&);
};
-} // namespace gl
+} // namespace skia
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index ff598e7..e253ad5 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -36,17 +36,26 @@
#include <memory>
#include <numeric>
-#include "../gl/GLExtensions.h"
+#include "GLExtensions.h"
#include "log/log_main.h"
-bool checkGlError(const char* op, int lineNumber);
-
namespace android {
namespace renderengine {
namespace skia {
using base::StringAppendF;
+static bool checkGlError(const char* op, int lineNumber) {
+ bool errorFound = false;
+ GLint error = glGetError();
+ while (error != GL_NO_ERROR) {
+ errorFound = true;
+ error = glGetError();
+ ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+ }
+ return errorFound;
+}
+
static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
EGLint wanted, EGLConfig* outConfig) {
EGLint numConfigs = -1, n = 0;
@@ -149,7 +158,7 @@
LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed");
}
- auto& extensions = gl::GLExtensions::getInstance();
+ auto& extensions = GLExtensions::getInstance();
extensions.initWithEGLStrings(eglVersion, eglExtensions);
// The code assumes that ES2 or later is available if this extension is
@@ -251,14 +260,13 @@
SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
- : SkiaRenderEngine(args.renderEngineType,
- static_cast<PixelFormat>(args.pixelFormat),
- args.useColorManagement, args.supportsBackgroundBlur),
+ : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
+ args.supportsBackgroundBlur),
mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
- mProtectedPlaceholderSurface(protectedPlaceholder) { }
+ mProtectedPlaceholderSurface(protectedPlaceholder) {}
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
finishRenderingAndAbandonContext();
@@ -343,8 +351,8 @@
}
bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) {
- if (!gl::GLExtensions::getInstance().hasNativeFenceSync() ||
- !gl::GLExtensions::getInstance().hasWaitSync()) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
return false;
}
@@ -379,7 +387,7 @@
base::unique_fd SkiaGLRenderEngine::flush() {
ATRACE_CALL();
- if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
}
@@ -470,13 +478,13 @@
std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority(
const RenderEngineCreationArgs& args) {
- if (!gl::GLExtensions::getInstance().hasContextPriority()) {
+ if (!GLExtensions::getInstance().hasContextPriority()) {
return std::nullopt;
}
switch (args.contextPriority) {
case RenderEngine::ContextPriority::REALTIME:
- if (gl::GLExtensions::getInstance().hasRealtimePriority()) {
+ if (GLExtensions::getInstance().hasRealtimePriority()) {
return RenderEngine::ContextPriority::REALTIME;
} else {
ALOGI("Realtime priority unsupported, degrading gracefully to high priority");
@@ -520,7 +528,7 @@
}
void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
- const gl::GLExtensions& extensions = gl::GLExtensions::getInstance();
+ const GLExtensions& extensions = GLExtensions::getInstance();
StringAppendF(&result, "\n ------------RE GLES------------\n");
StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion());
StringAppendF(&result, "%s\n", extensions.getEGLExtensions());
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 1f4c989..709de0d 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,7 +20,6 @@
#include "SkiaRenderEngine.h"
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <GrBackendSemaphore.h>
#include <GrContextOptions.h>
#include <SkBlendMode.h>
@@ -55,13 +54,14 @@
#include <android-base/stringprintf.h>
#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <pthread.h>
#include <src/core/SkTraceEventCommon.h>
#include <sync/sync.h>
#include <ui/BlurRegion.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <utils/Trace.h>
#include <cmath>
@@ -269,10 +269,8 @@
}
SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat,
- bool useColorManagement, bool supportsBackgroundBlur)
- : RenderEngine(type),
- mDefaultPixelFormat(pixelFormat),
- mUseColorManagement(useColorManagement) {
+ bool supportsBackgroundBlur)
+ : RenderEngine(type), mDefaultPixelFormat(pixelFormat) {
if (supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
mBlurFilter = new KawaseBlurFilter();
@@ -398,12 +396,10 @@
}
// We don't attempt to map a buffer if the buffer contains protected content. In GL this is
// important because GPU resources for protected buffers are much more limited. (In Vk we
- // simply match the existing behavior for protected buffers.) In Vk, we never cache any
- // buffers while in a protected context, since Vk cannot share across contexts, and protected
- // is less common.
+ // simply match the existing behavior for protected buffers.) We also never cache any
+ // buffers while in a protected context.
const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
- if (isProtectedBuffer ||
- (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && isProtected())) {
+ if (isProtectedBuffer || isProtected()) {
return;
}
ATRACE_CALL();
@@ -468,9 +464,8 @@
std::shared_ptr<AutoBackendTexture::LocalRef> SkiaRenderEngine::getOrCreateBackendTexture(
const sp<GraphicBuffer>& buffer, bool isOutputBuffer) {
- // Do not lookup the buffer in the cache for protected contexts with the SkiaVk back-end
- if (mRenderEngineType == RenderEngineType::SKIA_GL_THREADED ||
- (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && !isProtected())) {
+ // Do not lookup the buffer in the cache for protected contexts
+ if (!isProtected()) {
if (const auto& it = mTextureCache.find(buffer->getId()); it != mTextureCache.end()) {
return it->second;
}
@@ -652,8 +647,7 @@
void SkiaRenderEngine::drawLayersInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
- base::unique_fd&& bufferFence) {
+ const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str());
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -926,8 +920,7 @@
// luminance in linear space, which color pipelines request GAMMA_OETF break
// without a gamma 2.2 fixup.
const bool requiresLinearEffect = layer.colorTransform != mat4() ||
- (mUseColorManagement &&
- needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+ (needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
(dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)) ||
(!dimInLinearSpace && isExtendedHdr);
@@ -938,10 +931,7 @@
continue;
}
- // If color management is disabled, then mark the source image with the same colorspace as
- // the destination surface so that Skia's color management is a no-op.
- const ui::Dataspace layerDataspace =
- !mUseColorManagement ? display.outputDataspace : layer.sourceDataspace;
+ const ui::Dataspace layerDataspace = layer.sourceDataspace;
SkPaint paint;
if (layer.source.buffer.buffer) {
@@ -1031,7 +1021,10 @@
// Most HDR standards require at least 10-bits of color depth for source content, so we
// can just extract the transfer function rather than dig into precise gralloc layout.
// Furthermore, we can assume that the only 8-bit target we support is RGBA8888.
- const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) &&
+ const bool requiresDownsample =
+ getHdrRenderType(layer.sourceDataspace,
+ std::optional<ui::PixelFormat>(static_cast<ui::PixelFormat>(
+ buffer->getPixelFormat()))) != HdrRenderType::SDR &&
buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888;
if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) {
paint.setDither(true);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 723e73c..3db0c1b 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,23 +59,17 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(RenderEngineType type,
- PixelFormat pixelFormat,
- bool useColorManagement,
- bool supportsBackgroundBlur);
+ SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur);
~SkiaRenderEngine() override;
std::future<void> primeCache() override final;
void cleanupPostRender() override final;
- void cleanFramebufferCache() override final{ }
bool supportsBackgroundBlur() override final {
return mBlurFilter != nullptr;
}
void onActiveDisplaySizeChanged(ui::Size size) override final;
int reportShadersCompiled();
- virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override final{};
- virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override final{};
virtual void setEnableTracing(bool tracingEnabled) override final;
void useProtectedContext(bool useProtectedContext) override;
@@ -142,7 +136,6 @@
const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
base::unique_fd&& bufferFence) override final;
void dump(std::string& result) override final;
@@ -162,7 +155,6 @@
sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
const PixelFormat mDefaultPixelFormat;
- const bool mUseColorManagement;
// Identifier used for various mappings of layers to various
// textures or shaders
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index c16586b..6ecc6ab 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -592,7 +592,7 @@
SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
: SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat),
- args.useColorManagement, args.supportsBackgroundBlur) {}
+ args.supportsBackgroundBlur) {}
SkiaVkRenderEngine::~SkiaVkRenderEngine() {
finishRenderingAndAbandonContext();
diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh
index e99b7ae..c818c40 100755
--- a/libs/renderengine/skia/debug/record.sh
+++ b/libs/renderengine/skia/debug/record.sh
@@ -16,7 +16,6 @@
# first time use requires these changes
adb root
adb shell setenforce 0
- adb shell setprop debug.renderengine.backend "skiaglthreaded"
adb shell stop
adb shell start
exit 1;
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index 7bf2b0c..5c9820c 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -111,9 +111,9 @@
constexpr int kSampleCount = 1;
constexpr bool kMipmapped = false;
constexpr SkSurfaceProps* kProps = nullptr;
- sk_sp<SkSurface> surface =
- SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo, kSampleCount,
- kTopLeft_GrSurfaceOrigin, kProps, kMipmapped);
+ sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo,
+ kSampleCount, kTopLeft_GrSurfaceOrigin,
+ kProps, kMipmapped, input->isProtected());
LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__);
sk_sp<SkImage> tmpBlur = makeImage(surface.get(), &blurBuilder);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index f3f2da8..7dde716 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -109,7 +109,6 @@
virtual renderengine::RenderEngine::RenderEngineType type() = 0;
virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0;
virtual bool typeSupported() = 0;
- virtual bool useColorManagement() const = 0;
};
class SkiaVkRenderEngineFactory : public RenderEngineFactory {
@@ -130,13 +129,11 @@
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
- .setUseColorManagerment(false)
.setEnableProtectedContext(false)
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs);
}
@@ -144,14 +141,9 @@
bool typeSupported() override {
return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine();
}
- bool useColorManagement() const override { return false; }
void skip() { GTEST_SKIP(); }
};
-class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory {
-public:
- bool useColorManagement() const override { return true; }
-};
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -170,13 +162,11 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
bool typeSupported() override { return true; }
- bool useColorManagement() const override { return false; }
};
class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory {
@@ -197,13 +187,11 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(type())
- .setUseColorManagerment(useColorManagement())
.build();
return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs);
}
bool typeSupported() override { return true; }
- bool useColorManagement() const override { return true; }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -290,9 +278,6 @@
if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
writeBufferToFile("/data/texture_out_");
}
- for (uint32_t texName : mTexNames) {
- mRE->deleteTextures(1, &texName);
- }
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
@@ -505,7 +490,7 @@
void invokeDraw(const renderengine::DisplaySettings& settings,
const std::vector<renderengine::LayerSettings>& layers) {
ftl::Future<FenceResult> future =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+ mRE->drawLayers(settings, layers, mBuffer, base::unique_fd());
ASSERT_TRUE(future.valid());
auto result = future.get();
@@ -635,8 +620,6 @@
std::unique_ptr<renderengine::RenderEngine> mRE;
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
-
- std::vector<uint32_t> mTexNames;
};
void RenderEngineTest::initializeRenderEngine() {
@@ -680,9 +663,6 @@
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
RenderEngineTest* fixture) {
const auto buf = fixture->allocateSourceBuffer(1, 1);
- uint32_t texName;
- fixture->mRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -702,7 +682,6 @@
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
OpaquenessVariant::setOpaqueBit(layer);
}
@@ -1251,9 +1230,6 @@
// Here will allocate a checker board texture, but transform texture
// coordinates so that only the upper left is applied.
const auto buf = allocateSourceBuffer(2, 2);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1274,7 +1250,6 @@
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
// Transform coordinates to only be inside the red quadrant.
layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
layer.alpha = 1.0f;
@@ -1300,9 +1275,6 @@
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1314,7 +1286,6 @@
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.source.buffer.usePremultipliedAlpha = true;
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -1339,9 +1310,6 @@
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
- uint32_t texName;
- RenderEngineTest::mRE->genTextures(1, &texName);
- this->mTexNames.push_back(texName);
uint8_t* pixels;
buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -1353,7 +1321,6 @@
buf->getBuffer()->unlock();
layer.source.buffer.buffer = buf;
- layer.source.buffer.textureName = texName;
layer.source.buffer.usePremultipliedAlpha = false;
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -1559,9 +1526,7 @@
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
- std::make_shared<SkiaGLESCMRenderEngineFactory>(),
- std::make_shared<SkiaVkRenderEngineFactory>(),
- std::make_shared<SkiaVkCMRenderEngineFactory>()));
+ std::make_shared<SkiaVkRenderEngineFactory>()));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
if (!GetParam()->typeSupported()) {
@@ -1645,8 +1610,7 @@
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layers.push_back(layer);
- ftl::Future<FenceResult> future =
- mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
+ ftl::Future<FenceResult> future = mRE->drawLayers(settings, layers, nullptr, base::unique_fd());
ASSERT_TRUE(future.valid());
auto result = future.get();
@@ -1745,7 +1709,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1756,7 +1720,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1895,7 +1859,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -1906,7 +1870,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2045,7 +2009,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2056,7 +2020,7 @@
TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
const auto& renderEngineFactory = GetParam();
// skip for non color management
- if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) {
+ if (!renderEngineFactory->typeSupported()) {
GTEST_SKIP();
}
@@ -2298,14 +2262,14 @@
layers.push_back(layer);
ftl::Future<FenceResult> futureOne =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+ mRE->drawLayers(settings, layers, mBuffer, base::unique_fd());
ASSERT_TRUE(futureOne.valid());
auto resultOne = futureOne.get();
ASSERT_TRUE(resultOne.ok());
auto fenceOne = resultOne.value();
ftl::Future<FenceResult> futureTwo =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(fenceOne->dup()));
+ mRE->drawLayers(settings, layers, mBuffer, base::unique_fd(fenceOne->dup()));
ASSERT_TRUE(futureTwo.valid());
auto resultTwo = futureTwo.get();
ASSERT_TRUE(resultTwo.ok());
@@ -2592,10 +2556,6 @@
GTEST_SKIP();
}
- if (!GetParam()->useColorManagement()) {
- GTEST_SKIP();
- }
-
initializeRenderEngine();
const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB;
@@ -3017,15 +2977,11 @@
std::vector<renderengine::LayerSettings> layers{greenLayer};
invokeDraw(display, layers);
- if (GetParam()->useColorManagement()) {
- expectBufferColor(rect, 117, 251, 76, 255);
- } else {
- expectBufferColor(rect, 0, 255, 0, 255);
- }
+ expectBufferColor(rect, 117, 251, 76, 255);
}
TEST_P(RenderEngineTest, test_tonemapPQMatches) {
- if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported()) {
GTEST_SKIP();
}
@@ -3042,7 +2998,7 @@
}
TEST_P(RenderEngineTest, test_tonemapHLGMatches) {
- if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) {
+ if (!GetParam()->typeSupported()) {
GTEST_SKIP();
}
@@ -3262,9 +3218,9 @@
fut.wait();
}
- const int minimumExpectedShadersCompiled = GetParam()->useColorManagement() ? 60 : 30;
+ static constexpr int kMinimumExpectedShadersCompiled = 60;
ASSERT_GT(static_cast<skia::SkiaGLRenderEngine*>(mRE.get())->reportShadersCompiled(),
- minimumExpectedShadersCompiled);
+ kMinimumExpectedShadersCompiled);
}
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index fe3a16d..475dc15 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -36,7 +36,7 @@
void SetUp() override {
mThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
[this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); },
- renderengine::RenderEngine::RenderEngineType::THREADED);
+ renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED);
}
std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE;
@@ -57,18 +57,6 @@
mThreadedRE->getContextPriority();
}
-TEST_F(RenderEngineThreadedTest, genTextures) {
- uint32_t texName;
- EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
- mThreadedRE->genTextures(1, &texName);
-}
-
-TEST_F(RenderEngineThreadedTest, deleteTextures) {
- uint32_t texName;
- EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
- mThreadedRE->deleteTextures(1, &texName);
-}
-
TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
size_t size = 20;
EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
@@ -155,11 +143,11 @@
.WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
ftl::Future<FenceResult> future =
- mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence));
ASSERT_TRUE(future.valid());
auto result = future.get();
ASSERT_TRUE(result.ok());
@@ -188,11 +176,11 @@
.WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
ftl::Future<FenceResult> future =
- mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence));
ASSERT_TRUE(future.valid());
auto result = future.get();
ASSERT_TRUE(result.ok());
@@ -216,11 +204,11 @@
.WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); });
ftl::Future<FenceResult> future =
- mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence));
ASSERT_TRUE(future.valid());
auto result = future.get();
ASSERT_TRUE(result.ok());
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 6a1561a..2cb66cb 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -27,8 +27,6 @@
#include <processgroup/processgroup.h>
#include <utils/Trace.h>
-#include "gl/GLESRenderEngine.h"
-
using namespace std::chrono_literals;
namespace android {
@@ -175,46 +173,6 @@
result.assign(resultFuture.get());
}
-void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
- ATRACE_CALL();
- // This is a no-op in SkiaRenderEngine.
- if (getRenderEngineType() != RenderEngineType::THREADED) {
- return;
- }
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::genTextures");
- instance.genTextures(count, names);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
-void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
- ATRACE_CALL();
- // This is a no-op in SkiaRenderEngine.
- if (getRenderEngineType() != RenderEngineType::THREADED) {
- return;
- }
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::deleteTextures");
- instance.deleteTextures(count, names);
- resultPromise.set_value();
- });
- }
- mCondition.notify_one();
- resultFuture.wait();
-}
-
void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) {
ATRACE_CALL();
@@ -285,48 +243,32 @@
void RenderEngineThreaded::drawLayersInternal(
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) {
+ const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
resultPromise->set_value(Fence::NO_FENCE);
return;
}
ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
- base::unique_fd&& bufferFence) {
+ const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
ATRACE_CALL();
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
int fd = bufferFence.release();
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
- fd](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::drawLayers");
- instance.updateProtectedContext(layers, buffer);
- instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
- useFramebufferCache, base::unique_fd(fd));
- });
+ mFunctionCalls.push(
+ [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::drawLayers");
+ instance.updateProtectedContext(layers, buffer);
+ instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
+ base::unique_fd(fd));
+ });
}
mCondition.notify_one();
return resultFuture;
}
-void RenderEngineThreaded::cleanFramebufferCache() {
- ATRACE_CALL();
- // This function is designed so it can run asynchronously, so we do not need to wait
- // for the futures.
- {
- std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::cleanFramebufferCache");
- instance.cleanFramebufferCache();
- });
- }
- mCondition.notify_one();
-}
-
int RenderEngineThreaded::getContextPriority() {
std::promise<int> resultPromise;
std::future<int> resultFuture = resultPromise.get_future();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index 6eb108e..43ec011 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -46,8 +46,6 @@
void dump(std::string& result) override;
- void genTextures(size_t count, uint32_t* names) override;
- void deleteTextures(size_t count, uint32_t const* names) override;
size_t getMaxTextureSize() const override;
size_t getMaxViewportDims() const override;
@@ -57,10 +55,8 @@
ftl::Future<FenceResult> drawLayers(const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
base::unique_fd&& bufferFence) override;
- void cleanFramebufferCache() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
void onActiveDisplaySizeChanged(ui::Size size) override;
@@ -75,7 +71,7 @@
const DisplaySettings& display,
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
+ base::unique_fd&& bufferFence) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index b6ea77d..9127b37 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -74,7 +74,7 @@
if (hwSensor.maxDelay > INT_MAX) {
// Max delay is declared as a 64 bit integer for 64 bit architectures. But it should
// always fit in a 32 bit integer, log error and cap it to INT_MAX.
- ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.string(),
+ ALOGE("Sensor maxDelay overflow error %s %" PRId64, mName.c_str(),
static_cast<int64_t>(hwSensor.maxDelay));
mMaxDelay = INT_MAX;
} else {
@@ -335,7 +335,7 @@
if (actualReportingMode != expectedReportingMode) {
ALOGE("Reporting Mode incorrect: sensor %s handle=%#010" PRIx32 " type=%" PRId32 " "
"actual=%d expected=%d",
- mName.string(), mHandle, mType, actualReportingMode, expectedReportingMode);
+ mName.c_str(), mHandle, mType, actualReportingMode, expectedReportingMode);
}
}
@@ -613,7 +613,7 @@
const String8& string8) {
uint32_t len = static_cast<uint32_t>(string8.length());
FlattenableUtils::write(buffer, size, len);
- memcpy(static_cast<char*>(buffer), string8.string(), len);
+ memcpy(static_cast<char*>(buffer), string8.c_str(), len);
FlattenableUtils::advance(buffer, size, len);
size -= FlattenableUtils::align<4>(buffer);
}
diff --git a/libs/shaders/tests/Android.bp b/libs/shaders/tests/Android.bp
index 1e4f45a..5639d74 100644
--- a/libs/shaders/tests/Android.bp
+++ b/libs/shaders/tests/Android.bp
@@ -37,6 +37,7 @@
shared_libs: [
"android.hardware.graphics.common@1.2",
"libnativewindow",
+ "libbase",
],
static_libs: [
"libarect",
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index 2abf515..5c5fc6c 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -36,6 +36,7 @@
],
shared_libs: [
"libnativewindow",
+ "libbase",
],
static_libs: [
"libmath",
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index cc96f83..4be0a3a 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -115,7 +115,7 @@
sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1,
const sp<Fence>& f2) {
- return merge(name.string(), f1, f2);
+ return merge(name.c_str(), f1, f2);
}
int Fence::dup() const {
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h
deleted file mode 100644
index a461cb4..0000000
--- a/libs/ui/include_types/ui/DataspaceUtils.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2021 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 <ui/GraphicTypes.h>
-
-namespace android {
-
-inline bool isHdrDataspace(ui::Dataspace dataspace) {
- const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
-
- return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
-}
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/ui/include_types/ui/HdrRenderTypeUtils.h b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
new file mode 100644
index 0000000..b0af878
--- /dev/null
+++ b/libs/ui/include_types/ui/HdrRenderTypeUtils.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 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 <ui/GraphicTypes.h>
+
+namespace android {
+
+enum class HdrRenderType {
+ SDR, // just render to SDR
+ DISPLAY_HDR, // HDR by extended brightness
+ GENERIC_HDR // tonemapped HDR
+};
+
+/***
+ * A helper function to classify how we treat the result based on params.
+ *
+ * @param dataspace the dataspace
+ * @param pixelFormat optional, in case there is no source buffer.
+ * @param hdrSdrRatio default is 1.f, render engine side doesn't take care of it.
+ * @return HdrRenderType
+ */
+inline HdrRenderType getHdrRenderType(ui::Dataspace dataspace,
+ std::optional<ui::PixelFormat> pixelFormat,
+ float hdrSdrRatio = 1.f) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+ const auto range = dataspace & HAL_DATASPACE_RANGE_MASK;
+
+ if (transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG) {
+ return HdrRenderType::GENERIC_HDR;
+ }
+
+ static const auto BT2020_LINEAR_EXT = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_LINEAR |
+ HAL_DATASPACE_RANGE_EXTENDED);
+
+ if ((dataspace == BT2020_LINEAR_EXT || dataspace == ui::Dataspace::V0_SCRGB) &&
+ pixelFormat.has_value() && pixelFormat.value() == ui::PixelFormat::RGBA_FP16) {
+ return HdrRenderType::GENERIC_HDR;
+ }
+
+ // Extended range layer with an hdr/sdr ratio of > 1.01f can "self-promote" to HDR.
+ if (range == HAL_DATASPACE_RANGE_EXTENDED && hdrSdrRatio > 1.01f) {
+ return HdrRenderType::DISPLAY_HDR;
+ }
+
+ return HdrRenderType::SDR;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 831b64d..8ce017d 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -164,9 +164,9 @@
}
cc_test {
- name: "DataspaceUtils_test",
+ name: "HdrRenderTypeUtils_test",
shared_libs: ["libui"],
- srcs: ["DataspaceUtils_test.cpp"],
+ srcs: ["HdrRenderTypeUtils_test.cpp"],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
deleted file mode 100644
index 3e09671..0000000
--- a/libs/ui/tests/DataspaceUtils_test.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "DataspaceUtilsTest"
-
-#include <gtest/gtest.h>
-#include <ui/DataspaceUtils.h>
-
-namespace android {
-
-class DataspaceUtilsTest : public testing::Test {};
-
-TEST_F(DataspaceUtilsTest, isHdrDataspace) {
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
- EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
-
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
- // scRGB defines a very wide gamut but not an expanded luminance range
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
- EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
-}
-
-} // namespace android
diff --git a/libs/ui/tests/HdrRenderTypeUtils_test.cpp b/libs/ui/tests/HdrRenderTypeUtils_test.cpp
new file mode 100644
index 0000000..efe819d
--- /dev/null
+++ b/libs/ui/tests/HdrRenderTypeUtils_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "HdrRenderTypeUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/HdrRenderTypeUtils.h>
+
+namespace android {
+
+class HdrRenderTypeUtilsTest : public testing::Test {};
+
+TEST_F(HdrRenderTypeUtilsTest, getHdrRenderType) {
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU_HLG, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU_PQ, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_PQ, std::nullopt), HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_HLG, std::nullopt),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_FP16)),
+ HdrRenderType::GENERIC_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_8888), 2.f),
+ HdrRenderType::DISPLAY_HDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB_LINEAR,
+ std::optional<ui::PixelFormat>(ui::PixelFormat::RGBA_8888), 2.f),
+ HdrRenderType::DISPLAY_HDR);
+
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SRGB_LINEAR, std::nullopt), HdrRenderType::SDR);
+ // scRGB defines a very wide gamut but not an expanded luminance range
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SCRGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_SRGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_JFIF, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT601_625, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT601_525, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::V0_BT709, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DCI_P3_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DCI_P3, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_P3_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_P3, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::ADOBE_RGB, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_LINEAR, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::BT2020_ITU, std::nullopt), HdrRenderType::SDR);
+ EXPECT_EQ(getHdrRenderType(ui::Dataspace::DISPLAY_BT2020, std::nullopt), HdrRenderType::SDR);
+}
+
+} // namespace android
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
index bf9b031..2d59e8b 100644
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
@@ -161,10 +161,8 @@
fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
}
} else {
- size_t map_width = static_cast<size_t>(
- floor((width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
- size_t map_height = static_cast<size_t>(
- floor((height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
+ size_t map_width = width / kMapDimensionScaleFactor;
+ size_t map_height = height / kMapDimensionScaleFactor;
// init 400 image
grayImg.width = map_width;
grayImg.height = map_height;
@@ -207,7 +205,7 @@
fill420Buffer(bufferYSdr.get(), width, height, yStride);
size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
- yuv420Img.chroma_data = bufferYSdr.get();
+ yuv420Img.chroma_data = bufferUVSdr.get();
yuv420Img.chroma_stride = uvStride;
fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2,
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
index 55333b3..f27bd2d 100644
--- a/libs/ultrahdr/jpegr.cpp
+++ b/libs/ultrahdr/jpegr.cpp
@@ -801,10 +801,8 @@
size_t image_width = yuv420_image_ptr->width;
size_t image_height = yuv420_image_ptr->height;
- size_t map_width = static_cast<size_t>(
- floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
- size_t map_height = static_cast<size_t>(
- floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
+ size_t map_width = image_width / kMapDimensionScaleFactor;
+ size_t map_height = image_height / kMapDimensionScaleFactor;
dest->data = new uint8_t[map_width * map_height];
dest->width = map_width;
@@ -987,10 +985,8 @@
// TODO: remove once map scaling factor is computed based on actual map dims
size_t image_width = yuv420_image_ptr->width;
size_t image_height = yuv420_image_ptr->height;
- size_t map_width = static_cast<size_t>(
- floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
- size_t map_height = static_cast<size_t>(
- floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
+ size_t map_width = image_width / kMapDimensionScaleFactor;
+ size_t map_height = image_height / kMapDimensionScaleFactor;
if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) {
ALOGE("gain map dimensions and primary image dimensions are not to scale, computed gain map "
"resolution is %dx%d, received gain map resolution is %dx%d",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 4a08c11..48d793a 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -143,7 +143,7 @@
ALOGV("shellCommand");
for (size_t i = 0, n = args.size(); i < n; i++)
- ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string());
+ ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str());
if (args.size() >= 1) {
if (args[0] == String16("vkjson")) return cmdVkjson(out, err);
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 18f6dbc..7451037 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -77,6 +77,7 @@
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
"InputProcessor.cpp",
+ "PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
"UnwantedInteractionBlocker.cpp",
],
@@ -87,6 +88,7 @@
srcs: [":libinputflinger_sources"],
shared_libs: [
"android.hardware.input.processor-V1-ndk",
+ "com.android.server.inputflinger-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -107,6 +109,14 @@
"libpalmrejection",
"libui-types",
],
+ generated_headers: [
+ "cxx-bridge-header",
+ "inputflinger_rs_bootstrap_bridge_header",
+ ],
+ header_libs: ["inputflinger_rs_bootstrap_cxx_headers"],
+ generated_sources: ["inputflinger_rs_bootstrap_bridge_code"],
+ whole_static_libs: ["libinputflinger_rs"],
+ export_shared_lib_headers: ["com.android.server.inputflinger-ndk"],
target: {
android: {
shared_libs: [
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index aa55873..d319d6d 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -24,7 +24,7 @@
#include <android-base/stringprintf.h>
#include <android/log.h>
-#include <utils/Trace.h>
+#include <input/TraceTools.h>
using android::base::StringPrintf;
@@ -61,58 +61,42 @@
// --- QueuedInputListener ---
-static inline void traceEvent(const char* functionName, int32_t id) {
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("%s(id=0x%" PRIx32 ")", functionName, id);
- ATRACE_NAME(message.c_str());
- }
-}
-
QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener)
: mInnerListener(innerListener) {}
void QueuedInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifySwitch(const NotifySwitchArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifySensor(const NotifySensorArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyVibratorState(const NotifyVibratorStateArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
void QueuedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
- traceEvent(__func__, args.id);
mArgsQueue.emplace_back(args);
}
@@ -123,4 +107,81 @@
mArgsQueue.clear();
}
+// --- TracedInputListener ---
+
+TracedInputListener::TracedInputListener(const char* name, InputListenerInterface& innerListener)
+ : mInnerListener(innerListener), mName(name) {}
+
+void TracedInputListener::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyKey(const NotifyKeyArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyMotion(const NotifyMotionArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifySwitch(const NotifySwitchArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifySensor(const NotifySensorArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
+void TracedInputListener::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
+ constexpr static auto& fnName = __func__;
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("%s::%s(id=0x%" PRIx32 ")", mName, fnName, args.id);
+ });
+ mInnerListener.notify(args);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c903564..0567a32 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -23,22 +23,25 @@
#include "InputReaderFactory.h"
#include "UnwantedInteractionBlocker.h"
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <android/binder_interface_utils.h>
#include <android/sysprop/InputProperties.sysprop.h>
#include <binder/IPCThreadState.h>
-
+#include <inputflinger_bootstrap.rs.h>
#include <log/log.h>
-#include <unordered_map>
-
#include <private/android_filesystem_config.h>
namespace android {
-static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
+namespace {
+
+const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
-using gui::FocusRequest;
+const bool ENABLE_POINTER_CHOREOGRAPHER =
+ sysprop::InputProperties::enable_pointer_choreographer().value_or(false);
-static int32_t exceptionCodeFromStatusT(status_t status) {
+int32_t exceptionCodeFromStatusT(status_t status) {
switch (status) {
case OK:
return binder::Status::EX_NONE;
@@ -57,26 +60,98 @@
}
}
+// Convert a binder interface into a raw pointer to an AIBinder.
+using IInputFlingerRustBootstrapCallback = aidl::com::android::server::inputflinger::
+ IInputFlingerRust::IInputFlingerRustBootstrapCallback;
+IInputFlingerRustBootstrapCallbackAIBinder* binderToPointer(
+ IInputFlingerRustBootstrapCallback& interface) {
+ ndk::SpAIBinder spAIBinder = interface.asBinder();
+ auto* ptr = spAIBinder.get();
+ AIBinder_incStrong(ptr);
+ return ptr;
+}
+
+// Create the Rust component of InputFlinger that uses AIDL interfaces as a the foreign function
+// interface (FFI). The bootstraping process for IInputFlingerRust is as follows:
+// - Create BnInputFlingerRustBootstrapCallback in C++.
+// - Use the cxxbridge ffi interface to call the Rust function `create_inputflinger_rust()`, and
+// pass the callback binder object as a raw pointer.
+// - The Rust implementation will create the implementation of IInputFlingerRust, and pass it
+// to C++ through the callback.
+// - After the Rust function returns, the binder interface provided to the callback will be the
+// only strong reference to the IInputFlingerRust.
+std::shared_ptr<IInputFlingerRust> createInputFlingerRust() {
+ using namespace aidl::com::android::server::inputflinger;
+
+ class Callback : public IInputFlingerRust::BnInputFlingerRustBootstrapCallback {
+ ndk::ScopedAStatus onProvideInputFlingerRust(
+ const std::shared_ptr<IInputFlingerRust>& inputFlingerRust) override {
+ mService = inputFlingerRust;
+ return ndk::ScopedAStatus::ok();
+ }
+
+ public:
+ std::shared_ptr<IInputFlingerRust> consumeInputFlingerRust() {
+ auto service = mService;
+ mService.reset();
+ return service;
+ }
+
+ private:
+ std::shared_ptr<IInputFlingerRust> mService;
+ };
+
+ auto callback = ndk::SharedRefBase::make<Callback>();
+ create_inputflinger_rust(binderToPointer(*callback));
+ auto service = callback->consumeInputFlingerRust();
+ LOG_ALWAYS_FATAL_IF(!service,
+ "create_inputflinger_rust did not provide the IInputFlingerRust "
+ "implementation through the callback.");
+ return service;
+}
+
+} // namespace
+
/**
* The event flow is via the "InputListener" interface, as follows:
* InputReader
* -> UnwantedInteractionBlocker
+ * -> PointerChoreographer
* -> InputProcessor
* -> InputDeviceMetricsCollector
* -> InputDispatcher
*/
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
- InputDispatcherPolicyInterface& dispatcherPolicy) {
+ InputDispatcherPolicyInterface& dispatcherPolicy,
+ PointerChoreographerPolicyInterface& choreographerPolicy) {
+ mInputFlingerRust = createInputFlingerRust();
+
mDispatcher = createInputDispatcher(dispatcherPolicy);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
- mCollector = std::make_unique<InputDeviceMetricsCollector>(*mDispatcher);
+ mCollector = std::make_unique<InputDeviceMetricsCollector>(*mTracingStages.back());
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("MetricsCollector", *mCollector));
}
- mProcessor = ENABLE_INPUT_DEVICE_USAGE_METRICS ? std::make_unique<InputProcessor>(*mCollector)
- : std::make_unique<InputProcessor>(*mDispatcher);
- mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor);
- mReader = createInputReader(readerPolicy, *mBlocker);
+ mProcessor = std::make_unique<InputProcessor>(*mTracingStages.back());
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("InputProcessor", *mProcessor));
+
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mChoreographer =
+ std::make_unique<PointerChoreographer>(*mTracingStages.back(), choreographerPolicy);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("PointerChoreographer", *mChoreographer));
+ }
+
+ mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mTracingStages.back());
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("UnwantedInteractionBlocker", *mBlocker));
+
+ mReader = createInputReader(readerPolicy, *mTracingStages.back());
}
InputManager::~InputManager() {
@@ -123,6 +198,10 @@
return *mReader;
}
+PointerChoreographerInterface& InputManager::getChoreographer() {
+ return *mChoreographer;
+}
+
InputProcessorInterface& InputManager::getProcessor() {
return *mProcessor;
}
@@ -147,6 +226,10 @@
dump += '\n';
mBlocker->dump(dump);
dump += '\n';
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mChoreographer->dump(dump);
+ dump += '\n';
+ }
mProcessor->dump(dump);
dump += '\n';
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
@@ -190,7 +273,7 @@
return NO_ERROR;
}
-binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
+binder::Status InputManager::setFocusedWindow(const gui::FocusRequest& request) {
mDispatcher->setFocusedWindow(request);
return binder::Status::ok();
}
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 9dc285f..20b9fd5 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -23,13 +23,16 @@
#include "InputDeviceMetricsCollector.h"
#include "InputProcessor.h"
#include "InputReaderBase.h"
+#include "PointerChoreographer.h"
#include "include/UnwantedInteractionBlockerInterface.h"
#include <InputDispatcherInterface.h>
#include <InputDispatcherPolicyInterface.h>
+#include <PointerChoreographerPolicyInterface.h>
#include <input/Input.h>
#include <input/InputTransport.h>
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <android/os/BnInputFlinger.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -37,6 +40,8 @@
using android::os::BnInputFlinger;
+using aidl::com::android::server::inputflinger::IInputFlingerRust;
+
namespace android {
class InputChannel;
class InputDispatcherThread;
@@ -83,6 +88,9 @@
/* Gets the input reader. */
virtual InputReaderInterface& getReader() = 0;
+ /* Gets the PointerChoreographer. */
+ virtual PointerChoreographerInterface& getChoreographer() = 0;
+
/* Gets the input processor. */
virtual InputProcessorInterface& getProcessor() = 0;
@@ -105,12 +113,14 @@
public:
InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
- InputDispatcherPolicyInterface& dispatcherPolicy);
+ InputDispatcherPolicyInterface& dispatcherPolicy,
+ PointerChoreographerPolicyInterface& choreographerPolicy);
status_t start() override;
status_t stop() override;
InputReaderInterface& getReader() override;
+ PointerChoreographerInterface& getChoreographer() override;
InputProcessorInterface& getProcessor() override;
InputDeviceMetricsCollectorInterface& getMetricsCollector() override;
InputDispatcherInterface& getDispatcher() override;
@@ -127,11 +137,17 @@
std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker;
+ std::unique_ptr<PointerChoreographerInterface> mChoreographer;
+
std::unique_ptr<InputProcessorInterface> mProcessor;
std::unique_ptr<InputDeviceMetricsCollectorInterface> mCollector;
std::unique_ptr<InputDispatcherInterface> mDispatcher;
+
+ std::shared_ptr<IInputFlingerRust> mInputFlingerRust;
+
+ std::vector<std::unique_ptr<TracedInputListener>> mTracingStages;
};
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
new file mode 100644
index 0000000..e411abb
--- /dev/null
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 "PointerChoreographer"
+
+#include "PointerChoreographer.h"
+
+namespace android {
+
+// --- PointerChoreographer ---
+
+PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
+ PointerChoreographerPolicyInterface& policy)
+ : mNextListener(listener) {}
+
+void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::notifyPointerCaptureChanged(
+ const NotifyPointerCaptureChangedArgs& args) {
+ mNextListener.notify(args);
+}
+
+void PointerChoreographer::dump(std::string& dump) {
+ dump += "PointerChoreographer:\n";
+}
+
+} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
new file mode 100644
index 0000000..5e5f782
--- /dev/null
+++ b/services/inputflinger/PointerChoreographer.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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 "InputListener.h"
+#include "NotifyArgs.h"
+#include "PointerChoreographerPolicyInterface.h"
+
+namespace android {
+
+/**
+ * PointerChoreographer manages the icons shown by the system for input interactions.
+ * This includes showing the mouse cursor, stylus hover icons, and touch spots.
+ * It is responsible for accumulating the location of the mouse cursor, and populating
+ * the cursor position for incoming events, if necessary.
+ */
+class PointerChoreographerInterface : public InputListenerInterface {
+public:
+ /**
+ * This method may be called on any thread (usually by the input manager on a binder thread).
+ */
+ virtual void dump(std::string& dump) = 0;
+};
+
+class PointerChoreographer : public PointerChoreographerInterface {
+public:
+ explicit PointerChoreographer(InputListenerInterface& listener,
+ PointerChoreographerPolicyInterface&);
+ ~PointerChoreographer() override = default;
+
+ void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+ void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+ void notifyKey(const NotifyKeyArgs& args) override;
+ void notifyMotion(const NotifyMotionArgs& args) override;
+ void notifySwitch(const NotifySwitchArgs& args) override;
+ void notifySensor(const NotifySensorArgs& args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+ void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+
+ void dump(std::string& dump) override;
+
+private:
+ InputListenerInterface& mNextListener;
+};
+
+} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index c2da5ba..ee1f3cb 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -72,9 +72,6 @@
"include-filter": "android.view.cts.TouchDelegateTest"
},
{
- "include-filter": "android.view.cts.VelocityTrackerTest"
- },
- {
"include-filter": "android.view.cts.VerifyInputEventTest"
},
{
@@ -218,9 +215,6 @@
"include-filter": "android.view.cts.TouchDelegateTest"
},
{
- "include-filter": "android.view.cts.VelocityTrackerTest"
- },
- {
"include-filter": "android.view.cts.VerifyInputEventTest"
},
{
diff --git a/services/inputflinger/aidl/Android.bp b/services/inputflinger/aidl/Android.bp
new file mode 100644
index 0000000..314c433
--- /dev/null
+++ b/services/inputflinger/aidl/Android.bp
@@ -0,0 +1,31 @@
+// Copyright 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.
+
+aidl_interface {
+ name: "com.android.server.inputflinger",
+ srcs: ["**/*.aidl"],
+ unstable: true,
+ host_supported: true,
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ java: {
+ enabled: false,
+ },
+ rust: {
+ enabled: true,
+ },
+ },
+}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
new file mode 100644
index 0000000..8e826fd
--- /dev/null
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.server.inputflinger;
+
+/**
+ * A local AIDL interface used as a foreign function interface (ffi) to
+ * communicate with the Rust component of inputflinger.
+ *
+ * NOTE: Since we use this as a local interface, all processing happens on the
+ * calling thread.
+ */
+interface IInputFlingerRust {
+
+ /**
+ * An interface used to get a strong reference to IInputFlingerRust on boot.
+ */
+ interface IInputFlingerRustBootstrapCallback {
+ void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust);
+ }
+}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 6dd785a..188d5f0 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -185,10 +185,7 @@
mInfo.token = mClientChannel->getConnectionToken();
mInfo.name = "FakeWindowHandle";
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frameLeft = mFrame.left;
- mInfo.frameTop = mFrame.top;
- mInfo.frameRight = mFrame.right;
- mInfo.frameBottom = mFrame.bottom;
+ mInfo.frame = mFrame;
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(mFrame);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 2923a3c..640602f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -32,6 +32,7 @@
#endif
#include <input/InputDevice.h>
#include <input/PrintTools.h>
+#include <input/TraceTools.h>
#include <openssl/mem.h>
#include <powermanager/PowerManager.h>
#include <unistd.h>
@@ -1105,7 +1106,8 @@
const auto [x, y] = resolveTouchedPosition(motionEntry);
const bool isStylus = isPointerFromStylus(motionEntry, /*pointerIndex=*/0);
- auto [touchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ sp<WindowInfoHandle> touchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (touchedWindowHandle != nullptr &&
touchedWindowHandle->getApplicationToken() !=
mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1229,11 +1231,10 @@
}
}
-std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
-InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
- bool ignoreDragWindow) const {
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
+ bool isStylus,
+ bool ignoreDragWindow) const {
// Traverse windows from front to back to find touched window.
- std::vector<InputTarget> outsideTargets;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
@@ -1243,16 +1244,35 @@
const WindowInfo& info = *windowHandle->getInfo();
if (!info.isSpy() &&
windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
- return {windowHandle, outsideTargets};
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
+std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
+ int32_t displayId, const sp<WindowInfoHandle>& touchedWindow) const {
+ if (touchedWindow == nullptr) {
+ return {};
+ }
+ // Traverse windows from front to back until we encounter the touched window.
+ std::vector<InputTarget> outsideTargets;
+ const auto& windowHandles = getWindowHandlesLocked(displayId);
+ for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+ if (windowHandle == touchedWindow) {
+ // Stop iterating once we found a touched window. Any WATCH_OUTSIDE_TOUCH window
+ // below the touched window will not get ACTION_OUTSIDE event.
+ return outsideTargets;
}
+ const WindowInfo& info = *windowHandle->getInfo();
if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
/*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
outsideTargets);
}
}
- return {nullptr, {}};
+ return outsideTargets;
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
@@ -2308,11 +2328,11 @@
// Outside targets should be added upon first dispatched DOWN event. That means, this should
// be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
- auto [newTouchedWindowHandle, outsideTargets] =
+ sp<WindowInfoHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (isDown) {
- targets += outsideTargets;
+ targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle);
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
@@ -2488,7 +2508,8 @@
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
- auto [newTouchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ sp<WindowInfoHandle> newTouchedWindowHandle =
+ findTouchedWindowAtLocked(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
@@ -2734,7 +2755,7 @@
// have an explicit reason to support it.
constexpr bool isStylus = false;
- auto [dropWindow, _] =
+ sp<WindowInfoHandle> dropWindow =
findTouchedWindowAtLocked(displayId, x, y, isStylus, /*ignoreDragWindow=*/true);
if (dropWindow) {
vec2 local = dropWindow->getInfo()->transform.transform(x, y);
@@ -2788,8 +2809,9 @@
// until we have an explicit reason to support it.
constexpr bool isStylus = false;
- auto [hoverWindowHandle, _] = findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
- /*ignoreDragWindow=*/true);
+ sp<WindowInfoHandle> hoverWindowHandle =
+ findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
+ /*ignoreDragWindow=*/true);
// enqueue drag exit if needed.
if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
!haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -3011,8 +3033,8 @@
"hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
isTouchedWindow ? "[TOUCHED] " : "", info->packageName.c_str(),
info->ownerUid.toString().c_str(), info->id,
- toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
- info->frameTop, info->frameRight, info->frameBottom,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frame.left,
+ info->frame.top, info->frame.right, info->frame.bottom,
dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
info->inputConfig.string().c_str(), toString(info->token != nullptr),
info->applicationInfo.name.c_str(),
@@ -3161,12 +3183,10 @@
const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
- connection->getInputChannelName().c_str(), eventEntry->id);
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->id);
+ });
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
"globalScaleFactor=%f, pointerIds=%s %s",
@@ -3231,12 +3251,10 @@
const std::shared_ptr<Connection>& connection,
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
- if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
- connection->getInputChannelName().c_str(), eventEntry->id);
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
+ connection->getInputChannelName().c_str(), eventEntry->id);
+ });
LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
"No dispatch flags are set for %s", eventEntry->getDescription().c_str());
@@ -3266,12 +3284,6 @@
std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget,
ftl::Flags<InputTarget::Flags> dispatchMode) {
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
- connection->getInputChannelName().c_str(),
- dispatchMode.string().c_str());
- ATRACE_NAME(message.c_str());
- }
ftl::Flags<InputTarget::Flags> inputTargetFlags = inputTarget.flags;
if (!inputTargetFlags.any(dispatchMode)) {
return;
@@ -3558,11 +3570,10 @@
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
const std::shared_ptr<Connection>& connection) {
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
- connection->getInputChannelName().c_str());
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
+ connection->getInputChannelName().c_str());
+ });
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
}
@@ -4136,12 +4147,10 @@
}
int32_t newId = mIdGenerator.nextId();
- if (ATRACE_ENABLED()) {
- std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32
- ") to MotionEvent(id=0x%" PRIx32 ").",
- originalMotionEntry.id, newId);
- ATRACE_NAME(message.c_str());
- }
+ ATRACE_NAME_IF(ATRACE_ENABLED(), [&]() {
+ return StringPrintf("Split MotionEvent(id=0x%" PRIx32 ") to MotionEvent(id=0x%" PRIx32 ").",
+ originalMotionEntry.id, newId);
+ });
std::unique_ptr<MotionEntry> splitMotionEntry =
std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
originalMotionEntry.deviceId, originalMotionEntry.source,
@@ -5644,9 +5653,9 @@
i, windowInfo->name.c_str(), windowInfo->id,
windowInfo->displayId,
windowInfo->inputConfig.string().c_str(),
- windowInfo->alpha, windowInfo->frameLeft,
- windowInfo->frameTop, windowInfo->frameRight,
- windowInfo->frameBottom, windowInfo->globalScaleFactor,
+ windowInfo->alpha, windowInfo->frame.left,
+ windowInfo->frame.top, windowInfo->frame.right,
+ windowInfo->frame.bottom, windowInfo->globalScaleFactor,
windowInfo->applicationInfo.name.c_str(),
binderToString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index fef726f..58ae9c7 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -238,9 +238,12 @@
// to transfer focus to a new application.
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
- std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>>
- findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus = false,
- bool ignoreDragWindow = false) const REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
+ int32_t displayId, float x, float y, bool isStylus = false,
+ bool ignoreDragWindow = false) const REQUIRES(mLock);
+ std::vector<InputTarget> findOutsideTargetsLocked(
+ int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow) const
+ REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp
index ec0388d..de99fc7 100644
--- a/services/inputflinger/host/InputDriver.cpp
+++ b/services/inputflinger/host/InputDriver.cpp
@@ -256,14 +256,14 @@
const char* InputDriver::inputGetPropertyKey(input_property_t* property) {
if (property != nullptr) {
- return property->key.string();
+ return property->key.c_str();
}
return nullptr;
}
const char* InputDriver::inputGetPropertyValue(input_property_t* property) {
if (property != nullptr) {
- return property->value.string();
+ return property->value.c_str();
}
return nullptr;
}
@@ -281,7 +281,7 @@
}
void InputDriver::dump(String8& result) {
- result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.string());
+ result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.c_str());
}
} // namespace android
diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp
index 2da2a70..d974c43 100644
--- a/services/inputflinger/host/InputFlinger.cpp
+++ b/services/inputflinger/host/InputFlinger.cpp
@@ -57,7 +57,7 @@
} else {
dumpInternal(result);
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return OK;
}
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 4f78f03..0b7f7c2 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -76,4 +76,26 @@
std::vector<NotifyArgs> mArgsQueue;
};
+/*
+ * An implementation of the listener interface that traces the calls to its inner listener.
+ */
+class TracedInputListener : public InputListenerInterface {
+public:
+ explicit TracedInputListener(const char* name, InputListenerInterface& innerListener);
+
+ virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
+ virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
+ virtual void notifyKey(const NotifyKeyArgs& args) override;
+ virtual void notifyMotion(const NotifyMotionArgs& args) override;
+ virtual void notifySwitch(const NotifySwitchArgs& args) override;
+ virtual void notifySensor(const NotifySensorArgs& args) override;
+ virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
+ void notifyVibratorState(const NotifyVibratorStateArgs& args) override;
+ void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+
+private:
+ InputListenerInterface& mInnerListener;
+ const char* mName;
+};
+
} // namespace android
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
new file mode 100644
index 0000000..9e020c7
--- /dev/null
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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 "PointerControllerInterface.h"
+
+namespace android {
+
+/**
+ * The PointerChoreographer policy interface.
+ *
+ * This is the interface that PointerChoreographer uses to talk to Window Manager and other
+ * system components.
+ */
+class PointerChoreographerPolicyInterface {
+public:
+ virtual ~PointerChoreographerPolicyInterface() = default;
+
+ /**
+ * A factory method for PointerController. The PointerController implementation has
+ * dependencies on a graphical library - libgui, used to draw icons on the screen - which
+ * isn't available for the host. Since we want libinputflinger and its test to be buildable
+ * for and runnable on the host, the PointerController implementation must be in a separate
+ * library, libinputservice, that has the additional dependencies. The PointerController
+ * will be mocked when testing PointerChoreographer.
+ */
+ virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index c0c6d1f..8fe6411 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -66,6 +66,7 @@
"mapper/accumulator/TouchButtonAccumulator.cpp",
"mapper/gestures/GestureConverter.cpp",
"mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/HardwareProperties.cpp",
"mapper/gestures/HardwareStateConverter.cpp",
"mapper/gestures/PropertyProvider.cpp",
],
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e69c99e..f7bbc51 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -2473,6 +2473,7 @@
// See if this device has any stylus buttons that we would want to fuse with touch data.
if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT) &&
+ !device->classes.any(InputDeviceClass::ALPHAKEY) &&
std::any_of(STYLUS_BUTTON_KEYCODES.begin(), STYLUS_BUTTON_KEYCODES.end(),
[&](int32_t keycode) { return device->hasKeycodeLocked(keycode); })) {
device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index bacc720..fb32f96 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -66,23 +66,38 @@
return enabled;
}
-std::list<NotifyArgs> InputDevice::setEnabled(bool enabled, nsecs_t when) {
- std::list<NotifyArgs> out;
- if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
- ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
- "but the corresponding viewport is not found",
- getName().c_str(), *mAssociatedDisplayPort);
- enabled = false;
+std::list<NotifyArgs> InputDevice::updateEnableState(nsecs_t when,
+ const InputReaderConfiguration& readerConfig,
+ bool forceEnable) {
+ bool enable = forceEnable;
+ if (!forceEnable) {
+ // If the device was explicitly disabled by the user, it would be present in the
+ // "disabledDevices" list. This device should be disabled.
+ enable = readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end();
+
+ // If a device is associated with a specific display but there is no
+ // associated DisplayViewport, don't enable the device.
+ if (enable && (mAssociatedDisplayPort || mAssociatedDisplayUniqueId) &&
+ !mAssociatedViewport) {
+ const std::string desc = mAssociatedDisplayPort
+ ? "port " + std::to_string(*mAssociatedDisplayPort)
+ : "uniqueId " + *mAssociatedDisplayUniqueId;
+ ALOGW("Cannot enable input device %s because it is associated "
+ "with %s, but the corresponding viewport is not found",
+ getName().c_str(), desc.c_str());
+ enable = false;
+ }
}
- if (isEnabled() == enabled) {
+ std::list<NotifyArgs> out;
+ if (isEnabled() == enable) {
return out;
}
// When resetting some devices, the driver needs to be queried to ensure that a proper reset is
// performed. The querying must happen when the device is enabled, so we reset after enabling
// but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
- if (enabled) {
+ if (enable) {
for_each_subdevice([](auto& context) { context.enableDevice(); });
out += reset(when);
} else {
@@ -158,18 +173,25 @@
mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
}
-void InputDevice::addEventHubDevice(int32_t eventHubId,
- const InputReaderConfiguration& readerConfig) {
+[[nodiscard]] std::list<NotifyArgs> InputDevice::addEventHubDevice(
+ nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig) {
if (mDevices.find(eventHubId) != mDevices.end()) {
- return;
+ return {};
}
- std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
- std::vector<std::unique_ptr<InputMapper>> mappers = createMappers(*contextPtr, readerConfig);
- // insert the context into the devices set
- mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+ // Add an empty device configure and keep it enabled to allow mapper population with correct
+ // configuration/context,
+ // Note: we need to ensure device is kept enabled till mappers are configured
+ // TODO: b/281852638 refactor tests to remove this flag and reliance on the empty device
+ addEmptyEventHubDevice(eventHubId);
+ std::list<NotifyArgs> out = configureInternal(when, readerConfig, {}, /*forceEnable=*/true);
+
+ DevicePair& devicePair = mDevices[eventHubId];
+ devicePair.second = createMappers(*devicePair.first, readerConfig);
+
// Must change generation to flag this device as changed
bumpGeneration();
+ return out;
}
void InputDevice::removeEventHubDevice(int32_t eventHubId) {
@@ -183,6 +205,12 @@
std::list<NotifyArgs> InputDevice::configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
ConfigurationChanges changes) {
+ return configureInternal(when, readerConfig, changes);
+}
+std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when,
+ const InputReaderConfiguration& readerConfig,
+ ConfigurationChanges changes,
+ bool forceEnable) {
std::list<NotifyArgs> out;
mSources = 0;
mClasses = ftl::Flags<InputDeviceClass>(0);
@@ -235,15 +263,6 @@
}
}
- if (changes.test(Change::ENABLED_STATE)) {
- // Do not execute this code on the first configure, because 'setEnabled' would call
- // InputMapper::reset, and you can't reset a mapper before it has been configured.
- // The mappers are configured for the first time at the bottom of this function.
- auto it = readerConfig.disabledDevices.find(mId);
- bool enabled = it == readerConfig.disabledDevices.end();
- out += setEnabled(enabled, when);
- }
-
if (!changes.any() || changes.test(Change::DISPLAY_INFO)) {
// In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
@@ -267,12 +286,8 @@
}
}
- // If the device was explicitly disabled by the user, it would be present in the
- // "disabledDevices" list. If it is associated with a specific display, and it was not
- // explicitly disabled, then enable/disable the device based on whether we can find the
- // corresponding viewport.
- bool enabled =
- (readerConfig.disabledDevices.find(mId) == readerConfig.disabledDevices.end());
+ // If it is associated with a specific display, then find the corresponding viewport
+ // which will be used to enable/disable the device.
if (mAssociatedDisplayPort) {
mAssociatedViewport =
readerConfig.getDisplayViewportByPort(*mAssociatedDisplayPort);
@@ -280,7 +295,6 @@
ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
"but the corresponding viewport is not found.",
getName().c_str(), *mAssociatedDisplayPort);
- enabled = false;
}
} else if (mAssociatedDisplayUniqueId != std::nullopt) {
mAssociatedViewport =
@@ -289,16 +303,8 @@
ALOGW("Input device %s should be associated with display %s but the "
"corresponding viewport cannot be found",
getName().c_str(), mAssociatedDisplayUniqueId->c_str());
- enabled = false;
}
}
-
- if (changes.any()) {
- // For first-time configuration, only allow device to be disabled after mappers have
- // finished configuring. This is because we need to read some of the properties from
- // the device's open fd.
- out += setEnabled(enabled, when);
- }
}
for_each_mapper([this, when, &readerConfig, changes, &out](InputMapper& mapper) {
@@ -306,12 +312,11 @@
mSources |= mapper.getSources();
});
- // If a device is just plugged but it might be disabled, we need to update some info like
- // axis range of touch from each InputMapper first, then disable it.
- if (!changes.any()) {
- out += setEnabled(readerConfig.disabledDevices.find(mId) ==
- readerConfig.disabledDevices.end(),
- when);
+ if (!changes.any() || changes.test(Change::ENABLED_STATE) ||
+ changes.test(Change::DISPLAY_INFO)) {
+ // Whether a device is enabled can depend on the display association,
+ // so update the enabled state when there is a change in display info.
+ out += updateEnableState(when, readerConfig, forceEnable);
}
}
return out;
@@ -505,9 +510,9 @@
classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
- mappers.push_back(std::make_unique<MultiTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH)) {
- mappers.push_back(std::make_unique<SingleTouchInputMapper>(contextPtr, readerConfig));
+ mappers.push_back(createInputMapper<SingleTouchInputMapper>(contextPtr, readerConfig));
}
// Joystick-like devices.
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 08600b2..0aea0b3 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -77,7 +77,7 @@
: mContext(this),
mEventHub(eventHub),
mPolicy(policy),
- mQueuedListener(listener),
+ mNextListener(listener),
mGlobalMetaState(AMETA_NONE),
mLedMetaState(AMETA_NONE),
mGeneration(1),
@@ -140,7 +140,7 @@
mReaderIsAliveCondition.notify_all();
if (!events.empty()) {
- notifyArgs += processEventsLocked(events.data(), events.size());
+ mPendingArgs += processEventsLocked(events.data(), events.size());
}
if (mNextTimeout != LLONG_MAX) {
@@ -150,16 +150,18 @@
ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
}
mNextTimeout = LLONG_MAX;
- notifyArgs += timeoutExpiredLocked(now);
+ mPendingArgs += timeoutExpiredLocked(now);
}
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
inputDevices = getInputDevicesLocked();
- notifyArgs.emplace_back(
+ mPendingArgs.emplace_back(
NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices});
}
+
+ std::swap(notifyArgs, mPendingArgs);
} // release lock
// Send out a message that the describes the changed input devices.
@@ -175,8 +177,6 @@
}
}
- notifyAll(std::move(notifyArgs));
-
// Flush queued events out to the listener.
// This must happen outside of the lock because the listener could potentially call
// back into the InputReader's methods, such as getScanCodeState, or become blocked
@@ -184,7 +184,9 @@
// resulting in a deadlock. This situation is actually quite plausible because the
// listener is actually the input dispatcher, which calls into the window manager,
// which occasionally calls into the input reader.
- mQueuedListener.flush();
+ for (const NotifyArgs& args : notifyArgs) {
+ mNextListener.notify(args);
+ }
}
std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
@@ -234,10 +236,10 @@
}
InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
- std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
+ std::shared_ptr<InputDevice> device = createDeviceLocked(when, eventHubId, identifier);
- notifyAll(device->configure(when, mConfig, /*changes=*/{}));
- notifyAll(device->reset(when));
+ mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
+ mPendingArgs += device->reset(when);
if (device->isIgnored()) {
ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
@@ -310,16 +312,14 @@
notifyExternalStylusPresenceChangedLocked();
}
- std::list<NotifyArgs> resetEvents;
if (device->hasEventHubDevices()) {
- resetEvents += device->configure(when, mConfig, /*changes=*/{});
+ mPendingArgs += device->configure(when, mConfig, /*changes=*/{});
}
- resetEvents += device->reset(when);
- notifyAll(std::move(resetEvents));
+ mPendingArgs += device->reset(when);
}
std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
- int32_t eventHubId, const InputDeviceIdentifier& identifier) {
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) {
auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
const InputDeviceIdentifier identifier2 =
devicePair.second->getDeviceInfo().getIdentifier();
@@ -334,7 +334,7 @@
device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
identifier);
}
- device->addEventHubDevice(eventHubId, mConfig);
+ mPendingArgs += device->addEventHubDevice(when, eventHubId, mConfig);
return device;
}
@@ -387,7 +387,7 @@
updateGlobalMetaStateLocked();
// Enqueue configuration changed.
- mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when});
+ mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when});
}
void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) {
@@ -409,7 +409,7 @@
} else {
for (auto& devicePair : mDevices) {
std::shared_ptr<InputDevice>& device = devicePair.second;
- notifyAll(device->configure(now, mConfig, changes));
+ mPendingArgs += device->configure(now, mConfig, changes);
}
}
@@ -419,18 +419,13 @@
"There was no change in the pointer capture state.");
} else {
mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
- mQueuedListener.notifyPointerCaptureChanged(
- {mContext.getNextId(), now, mCurrentPointerCaptureRequest});
+ mPendingArgs.emplace_back(
+ NotifyPointerCaptureChangedArgs{mContext.getNextId(), now,
+ mCurrentPointerCaptureRequest});
}
}
}
-void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) {
- for (const NotifyArgs& args : argsList) {
- mQueuedListener.notify(args);
- }
-}
-
void InputReader::updateGlobalMetaStateLocked() {
mGlobalMetaState = 0;
@@ -690,7 +685,7 @@
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- notifyAll(device->vibrate(sequence, repeat, token));
+ mPendingArgs += device->vibrate(sequence, repeat, token);
}
}
@@ -699,7 +694,7 @@
InputDevice* device = findInputDeviceLocked(deviceId);
if (device) {
- notifyAll(device->cancelVibrate(token));
+ mPendingArgs += device->cancelVibrate(token);
}
}
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 1cbcbf4..31dcb2e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -77,11 +77,11 @@
inline bool isIgnored() { return !getMapperCount() && !mController; }
bool isEnabled();
- [[nodiscard]] std::list<NotifyArgs> setEnabled(bool enabled, nsecs_t when);
void dump(std::string& dump, const std::string& eventHubDevStr);
void addEmptyEventHubDevice(int32_t eventHubId);
- void addEventHubDevice(int32_t eventHubId, const InputReaderConfiguration& readerConfig);
+ [[nodiscard]] std::list<NotifyArgs> addEventHubDevice(
+ nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig);
void removeEventHubDevice(int32_t eventHubId);
[[nodiscard]] std::list<NotifyArgs> configure(nsecs_t when,
const InputReaderConfiguration& readerConfig,
@@ -206,6 +206,13 @@
std::vector<std::unique_ptr<InputMapper>> createMappers(
InputDeviceContext& contextPtr, const InputReaderConfiguration& readerConfig);
+ [[nodiscard]] std::list<NotifyArgs> configureInternal(
+ nsecs_t when, const InputReaderConfiguration& readerConfig,
+ ConfigurationChanges changes, bool forceEnable = false);
+
+ [[nodiscard]] std::list<NotifyArgs> updateEnableState(
+ nsecs_t when, const InputReaderConfiguration& readerConfig, bool forceEnable = false);
+
PropertyMap mConfiguration;
// Runs logic post a `process` call. This can be used to update the generated `NotifyArgs` as
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 01ec7c1..9a297c9 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -121,7 +121,7 @@
protected:
// These members are protected so they can be instrumented by test cases.
- virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
const InputDeviceIdentifier& identifier)
REQUIRES(mLock);
@@ -174,7 +174,14 @@
// in parallel to passing it to the InputReader.
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
- QueuedInputListener mQueuedListener;
+
+ // The next stage that should receive the events generated inside InputReader.
+ InputListenerInterface& mNextListener;
+ // As various events are generated inside InputReader, they are stored inside this list. The
+ // list can only be accessed with the lock, so the events inside it are well-ordered.
+ // Once the reader is done working, these events will be swapped into a temporary storage and
+ // sent to the 'mNextListener' without holding the lock.
+ std::list<NotifyArgs> mPendingArgs GUARDED_BY(mLock);
InputReaderConfiguration mConfig GUARDED_BY(mLock);
@@ -242,8 +249,6 @@
ConfigurationChanges mConfigurationChangesToRefresh GUARDED_BY(mLock);
void refreshConfigurationLocked(ConfigurationChanges changes) REQUIRES(mLock);
- void notifyAll(std::list<NotifyArgs>&& argsList);
-
PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock);
// state queries
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index f300ee1..1d788df 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -27,8 +27,6 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
- explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
~MultiTouchInputMapper() override;
@@ -41,6 +39,8 @@
bool hasStylus() const override;
private:
+ explicit MultiTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
// simulate_stylus_with_touch is a debug mode that converts all finger pointers reported by this
// mapper's touchscreen into stylus pointers, and adds SOURCE_STYLUS to the input device.
// It is used to simulate stylus events for debugging and testing on a device that does not
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index dac53cf..7726bfb 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,8 +27,6 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
- explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig);
~SingleTouchInputMapper() override;
@@ -42,6 +40,8 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
+ explicit SingleTouchInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index eca0f86..6ea004d 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -33,6 +33,7 @@
#include <statslog.h>
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
+#include "gestures/HardwareProperties.h"
#include "ui/Rotation.h"
namespace android {
@@ -119,57 +120,6 @@
return output;
}
-short getMaxTouchCount(const InputDeviceContext& context) {
- if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
- if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
- if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
- if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
- if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
- return 0;
-}
-
-HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
- HardwareProperties props;
- RawAbsoluteAxisInfo absMtPositionX;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
- props.left = absMtPositionX.minValue;
- props.right = absMtPositionX.maxValue;
- props.res_x = absMtPositionX.resolution;
-
- RawAbsoluteAxisInfo absMtPositionY;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
- props.top = absMtPositionY.minValue;
- props.bottom = absMtPositionY.maxValue;
- props.res_y = absMtPositionY.resolution;
-
- RawAbsoluteAxisInfo absMtOrientation;
- context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
- props.orientation_minimum = absMtOrientation.minValue;
- props.orientation_maximum = absMtOrientation.maxValue;
-
- RawAbsoluteAxisInfo absMtSlot;
- context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
- props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
- props.max_touch_cnt = getMaxTouchCount(context);
-
- // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
- // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
- // that did this, so assume false.
- props.supports_t5r2 = false;
-
- props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
- props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
-
- // Mouse-only properties, which will always be false.
- props.has_wheel = false;
- props.wheel_is_hi_res = false;
-
- // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
- // are haptic.
- props.is_haptic_pad = false;
- return props;
-}
-
void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
mapper->consumeGesture(gesture);
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
new file mode 100644
index 0000000..04655dc
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 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.
+ */
+
+#include "HardwareProperties.h"
+
+namespace android {
+
+namespace {
+
+unsigned short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+} // namespace
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+
+ RawAbsoluteAxisInfo absMtPressure;
+ context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
+ props.reports_pressure = absMtPressure.valid;
+ return props;
+}
+
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Image.h b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h
similarity index 67%
rename from libs/renderengine/include/renderengine/Image.h
rename to services/inputflinger/reader/mapper/gestures/HardwareProperties.h
index 3bb4731..672f8c1 100644
--- a/libs/renderengine/include/renderengine/Image.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 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.
@@ -16,16 +16,14 @@
#pragma once
-struct ANativeWindowBuffer;
+#include "InputDevice.h"
+
+#include "include/gestures.h"
namespace android {
-namespace renderengine {
-class Image {
-public:
- virtual ~Image() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
-};
+// Creates a Gestures library HardwareProperties struct for the touchpad described by the
+// InputDeviceContext.
+HardwareProperties createHardwareProperties(const InputDeviceContext& context);
-} // namespace renderengine
} // namespace android
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
new file mode 100644
index 0000000..23c1691
--- /dev/null
+++ b/services/inputflinger/rust/Android.bp
@@ -0,0 +1,52 @@
+// Copyright 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.
+
+// Generate the C++ code that Rust calls into.
+genrule {
+ name: "inputflinger_rs_bootstrap_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["inputflinger_rs_bootstrap_cxx_generated.cc"],
+}
+
+// Generate a C++ header containing the C++ bindings
+// to the Rust exported functions in lib.rs.
+genrule {
+ name: "inputflinger_rs_bootstrap_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["lib.rs"],
+ out: ["inputflinger_bootstrap.rs.h"],
+}
+
+rust_ffi_static {
+ name: "libinputflinger_rs",
+ crate_name: "inputflinger",
+ srcs: ["lib.rs"],
+ rustlibs: [
+ "libcxx",
+ "com.android.server.inputflinger-rust",
+ "libbinder_rs",
+ "liblog_rust",
+ "liblogger",
+ ],
+ host_supported: true,
+}
+
+cc_library_headers {
+ name: "inputflinger_rs_bootstrap_cxx_headers",
+ host_supported: true,
+ export_include_dirs: ["ffi"],
+}
diff --git a/libs/renderengine/include/renderengine/Image.h b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h
similarity index 63%
copy from libs/renderengine/include/renderengine/Image.h
copy to services/inputflinger/rust/ffi/InputFlingerBootstrap.h
index 3bb4731..eb79ab5 100644
--- a/libs/renderengine/include/renderengine/Image.h
+++ b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 The Android Open Source Project
+ * Copyright 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.
@@ -16,16 +16,6 @@
#pragma once
-struct ANativeWindowBuffer;
+#include <android/binder_parcel.h>
-namespace android {
-namespace renderengine {
-
-class Image {
-public:
- virtual ~Image() = default;
- virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
-};
-
-} // namespace renderengine
-} // namespace android
+using IInputFlingerRustBootstrapCallbackAIBinder = AIBinder;
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
new file mode 100644
index 0000000..501e435
--- /dev/null
+++ b/services/inputflinger/rust/lib.rs
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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.
+ */
+
+//! # The rust component of InputFlinger
+//!
+//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and
+//! pass it back to C++ as a local AIDL interface.
+
+use binder::{
+ unstable_api::{AIBinder, new_spibinder,},
+ BinderFeatures, Interface, StatusCode, Strong,
+};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{
+ BnInputFlingerRust, IInputFlingerRust,
+ IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback,
+};
+use log::debug;
+
+const LOG_TAG: &str = "inputflinger_bootstrap";
+
+#[cxx::bridge]
+#[allow(unsafe_op_in_unsafe_fn)]
+mod ffi {
+ extern "C++" {
+ include!("InputFlingerBootstrap.h");
+ type IInputFlingerRustBootstrapCallbackAIBinder;
+ }
+
+ extern "Rust" {
+ unsafe fn create_inputflinger_rust(
+ callback: *mut IInputFlingerRustBootstrapCallbackAIBinder,
+ );
+ }
+}
+
+/// Create the IInputFlingerRust implementation.
+/// This is the singular entry point from C++ into Rust.
+/// The `callback` parameter must be a valid pointer to an AIBinder implementation of
+/// the `IInputFlingerRustBootstrapCallback` interface. The IInputFlingerRust implementation that
+/// is created will be passed back through the callback from within this function.
+/// NOTE: This function must not hold a strong reference to the callback beyond its scope.
+///
+/// # Safety
+///
+/// This function is safe iff `callback` is a valid pointer to an `AIBinder` interface of type
+/// `IInputFlingerRustBootstrapCallback`. The pointer must have had its reference count manually
+/// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`.
+unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) {
+ logger::init(
+ logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace),
+ );
+
+ let callback = callback as *mut AIBinder;
+ if callback.is_null() {
+ panic!("create_inputflinger_rust cannot be called with a null callback");
+ }
+
+ // SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its
+ // reference count has been incremented..
+ let Some(callback) = (unsafe { new_spibinder(callback) }) else {
+ panic!("Failed to get SpAIBinder from raw callback pointer");
+ };
+
+ let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> =
+ callback.into_interface();
+ match callback {
+ Ok(callback) => {
+ debug!("Creating InputFlingerRust");
+ let service =
+ BnInputFlingerRust::new_binder(InputFlingerRust {}, BinderFeatures::default());
+ callback.onProvideInputFlingerRust(&service).unwrap();
+ }
+ Err(status) => {
+ panic!("Failed to convert AIBinder into the callback interface: {}", status);
+ }
+ }
+}
+
+struct InputFlingerRust {}
+
+impl Interface for InputFlingerRust {}
+
+impl IInputFlingerRust for InputFlingerRust {}
+
+impl Drop for InputFlingerRust {
+ fn drop(&mut self) {
+ debug!("Destroying InputFlingerRust");
+ }
+}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 3d6df30..6410046 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -47,6 +47,7 @@
"FakePointerController.cpp",
"FocusResolver_test.cpp",
"GestureConverter_test.cpp",
+ "HardwareProperties_test.cpp",
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
"InputMapperTest.cpp",
@@ -57,6 +58,7 @@
"InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
"NotifyArgs_test.cpp",
+ "PointerChoreographer_test.cpp",
"PreferStylusOverTouch_test.cpp",
"PropertyProvider_test.cpp",
"SlopController_test.cpp",
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 78420c0..41c98ef 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -158,7 +158,8 @@
return mConfig;
}
-const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const {
+const std::vector<InputDeviceInfo> FakeInputReaderPolicy::getInputDevices() const {
+ std::scoped_lock lock(mLock);
return mInputDevices;
}
@@ -228,7 +229,7 @@
void FakeInputReaderPolicy::notifyInputDevicesChanged(
const std::vector<InputDeviceInfo>& inputDevices) {
- std::scoped_lock<std::mutex> lock(mLock);
+ std::scoped_lock lock(mLock);
mInputDevices = inputDevices;
mInputDevicesChanged = true;
mDevicesChangedCondition.notify_all();
@@ -256,7 +257,7 @@
}
void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) {
- std::scoped_lock<std::mutex> lock(mLock);
+ std::scoped_lock lock(mLock);
mStylusGestureNotified = deviceId;
}
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index e03d28d..48912a6 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -64,7 +64,7 @@
void removeDisabledDevice(int32_t deviceId);
void setPointerController(std::shared_ptr<FakePointerController> controller);
const InputReaderConfiguration& getReaderConfiguration() const;
- const std::vector<InputDeviceInfo>& getInputDevices() const;
+ const std::vector<InputDeviceInfo> getInputDevices() const;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
ui::Rotation surfaceRotation);
void setTouchAffineTransformation(const TouchAffineTransformation t);
@@ -91,7 +91,7 @@
void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
- std::mutex mLock;
+ mutable std::mutex mLock;
std::condition_variable mDevicesChangedCondition;
InputReaderConfiguration mConfig;
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
new file mode 100644
index 0000000..8dfa8c8
--- /dev/null
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 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.
+ */
+#include <gestures/HardwareProperties.h>
+
+#include <memory>
+#include <set>
+
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InterfaceMocks.h"
+#include "TestConstants.h"
+#include "include/gestures.h"
+
+namespace android {
+
+using testing::Return;
+
+class HardwarePropertiesTest : public testing::Test {
+public:
+ HardwarePropertiesTest() {
+ EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
+ /*generation=*/2, identifier);
+ mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
+ }
+
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
+ .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
+ outAxisInfo->valid = true;
+ outAxisInfo->minValue = min;
+ outAxisInfo->maxValue = max;
+ outAxisInfo->flat = 0;
+ outAxisInfo->fuzz = 0;
+ outAxisInfo->resolution = resolution;
+ return OK;
+ });
+ }
+
+ void setupInvalidAxis(int axis) {
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
+ .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
+ outAxisInfo->valid = false;
+ return -1;
+ });
+ }
+
+ void setProperty(int property, bool value) {
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, property))
+ .WillRepeatedly(Return(value));
+ }
+
+ void setButtonsPresent(std::set<int> buttonCodes, bool present) {
+ for (const auto& buttonCode : buttonCodes) {
+ EXPECT_CALL(mMockEventHub, hasScanCode(EVENTHUB_ID, buttonCode))
+ .WillRepeatedly(Return(present));
+ }
+ }
+
+ MockEventHubInterface mMockEventHub;
+ MockInputReaderContext mMockInputReaderContext;
+ std::unique_ptr<InputDevice> mDevice;
+ std::unique_ptr<InputDeviceContext> mDeviceContext;
+};
+
+TEST_F(HardwarePropertiesTest, FancyTouchpad) {
+ setupValidAxis(ABS_MT_POSITION_X, 0, 2048, 27);
+ setupValidAxis(ABS_MT_POSITION_Y, 0, 1500, 30);
+ setupValidAxis(ABS_MT_ORIENTATION, -3, 4, 0);
+ setupValidAxis(ABS_MT_SLOT, 0, 15, 0);
+ setupValidAxis(ABS_MT_PRESSURE, 0, 256, 0);
+
+ setProperty(INPUT_PROP_SEMI_MT, false);
+ setProperty(INPUT_PROP_BUTTONPAD, true);
+
+ setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP,
+ BTN_TOOL_QUINTTAP},
+ true);
+
+ HardwareProperties hwprops = createHardwareProperties(*mDeviceContext);
+ EXPECT_NEAR(0, hwprops.left, EPSILON);
+ EXPECT_NEAR(0, hwprops.top, EPSILON);
+ EXPECT_NEAR(2048, hwprops.right, EPSILON);
+ EXPECT_NEAR(1500, hwprops.bottom, EPSILON);
+
+ EXPECT_NEAR(27, hwprops.res_x, EPSILON);
+ EXPECT_NEAR(30, hwprops.res_y, EPSILON);
+
+ EXPECT_NEAR(-3, hwprops.orientation_minimum, EPSILON);
+ EXPECT_NEAR(4, hwprops.orientation_maximum, EPSILON);
+
+ EXPECT_EQ(16, hwprops.max_finger_cnt);
+ EXPECT_EQ(5, hwprops.max_touch_cnt);
+
+ EXPECT_FALSE(hwprops.supports_t5r2);
+ EXPECT_FALSE(hwprops.support_semi_mt);
+ EXPECT_TRUE(hwprops.is_button_pad);
+ EXPECT_FALSE(hwprops.has_wheel);
+ EXPECT_FALSE(hwprops.wheel_is_hi_res);
+ EXPECT_FALSE(hwprops.is_haptic_pad);
+ EXPECT_TRUE(hwprops.reports_pressure);
+}
+
+TEST_F(HardwarePropertiesTest, BasicTouchpad) {
+ setupValidAxis(ABS_MT_POSITION_X, 0, 1024, 0);
+ setupValidAxis(ABS_MT_POSITION_Y, 0, 768, 0);
+ setupValidAxis(ABS_MT_SLOT, 0, 7, 0);
+
+ setupInvalidAxis(ABS_MT_ORIENTATION);
+ setupInvalidAxis(ABS_MT_PRESSURE);
+
+ setProperty(INPUT_PROP_SEMI_MT, false);
+ setProperty(INPUT_PROP_BUTTONPAD, false);
+
+ setButtonsPresent({BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP}, true);
+ setButtonsPresent({BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP}, false);
+
+ HardwareProperties hwprops = createHardwareProperties(*mDeviceContext);
+ EXPECT_NEAR(0, hwprops.left, EPSILON);
+ EXPECT_NEAR(0, hwprops.top, EPSILON);
+ EXPECT_NEAR(1024, hwprops.right, EPSILON);
+ EXPECT_NEAR(768, hwprops.bottom, EPSILON);
+
+ EXPECT_NEAR(0, hwprops.res_x, EPSILON);
+ EXPECT_NEAR(0, hwprops.res_y, EPSILON);
+
+ EXPECT_NEAR(0, hwprops.orientation_minimum, EPSILON);
+ EXPECT_NEAR(0, hwprops.orientation_maximum, EPSILON);
+
+ EXPECT_EQ(8, hwprops.max_finger_cnt);
+ EXPECT_EQ(3, hwprops.max_touch_cnt);
+
+ EXPECT_FALSE(hwprops.supports_t5r2);
+ EXPECT_FALSE(hwprops.support_semi_mt);
+ EXPECT_FALSE(hwprops.is_button_pad);
+ EXPECT_FALSE(hwprops.has_wheel);
+ EXPECT_FALSE(hwprops.wheel_is_hi_res);
+ EXPECT_FALSE(hwprops.is_haptic_pad);
+ EXPECT_FALSE(hwprops.reports_pressure);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 6d9cd87..dc281a3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1131,10 +1131,7 @@
mInfo.name = name;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
mInfo.alpha = 1.0;
- mInfo.frameLeft = 0;
- mInfo.frameTop = 0;
- mInfo.frameRight = WIDTH;
- mInfo.frameBottom = HEIGHT;
+ mInfo.frame = Rect(0, 0, WIDTH, HEIGHT);
mInfo.transform.set(0, 0);
mInfo.globalScaleFactor = 1.0;
mInfo.touchableRegion.clear();
@@ -1215,10 +1212,7 @@
void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
- mInfo.frameLeft = frame.left;
- mInfo.frameTop = frame.top;
- mInfo.frameRight = frame.right;
- mInfo.frameBottom = frame.bottom;
+ mInfo.frame = frame;
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
@@ -9727,12 +9721,11 @@
mPolicyFlags);
}
- sp<FakeWindowHandle> createWindow() const {
+ sp<FakeWindowHandle> createWindow(const char* name) const {
std::shared_ptr<FakeApplicationHandle> overlayApplication =
std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(overlayApplication, mDispatcher, "Owned Window",
- ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
+ name, ADISPLAY_ID_DEFAULT);
window->setOwnerInfo(mPid, mUid);
return window;
}
@@ -9742,7 +9735,7 @@
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -9759,7 +9752,7 @@
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
@@ -9776,8 +9769,8 @@
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedSpyWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
- auto spy = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
+ auto spy = owner.createWindow("Owned spy");
spy->setSpy(true);
spy->setTrustedOverlay(true);
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -9790,10 +9783,10 @@
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedSpyWindow) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
- auto randosSpy = rando.createWindow();
+ auto randosSpy = rando.createWindow("Rando's spy");
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -9808,10 +9801,10 @@
TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTargeting) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
- auto randosSpy = rando.createWindow();
+ auto randosSpy = rando.createWindow("Rando's spy");
randosSpy->setSpy(true);
randosSpy->setTrustedOverlay(true);
mDispatcher->onWindowInfosChanged({{*randosSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -9833,10 +9826,10 @@
TEST_F(InputDispatcherTargetedInjectionTest, CannotGenerateActionOutsideToOtherUids) {
auto owner = User(mDispatcher, gui::Pid{10}, gui::Uid{11});
- auto window = owner.createWindow();
+ auto window = owner.createWindow("Owned window");
auto rando = User(mDispatcher, gui::Pid{20}, gui::Uid{21});
- auto randosWindow = rando.createWindow();
+ auto randosWindow = rando.createWindow("Rando's window");
randosWindow->setFrame(Rect{-10, -10, -5, -5});
randosWindow->setWatchOutsideTouch(true);
mDispatcher->onWindowInfosChanged({{*randosWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 02c2f65..bce0937 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1343,20 +1343,8 @@
mFakePolicy = sp<FakeInputReaderPolicy>::make();
mFakePointerController = std::make_shared<FakePointerController>();
mFakePolicy->setPointerController(mFakePointerController);
- mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
- /*eventDidNotHappenTimeout=*/30ms);
- mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
- *mTestListener);
- ASSERT_EQ(mReader->start(), OK);
-
- // Since this test is run on a real device, all the input devices connected
- // to the test device will show up in mReader. We wait for those input devices to
- // show up before beginning the tests.
- ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
- mTestListener->clearNotifyDeviceResetCalls();
+ setupInputReader();
}
void TearDown() override {
@@ -1377,6 +1365,22 @@
});
return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt;
}
+
+ void setupInputReader() {
+ mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
+ /*eventDidNotHappenTimeout=*/30ms);
+
+ mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
+ *mTestListener);
+ ASSERT_EQ(mReader->start(), OK);
+
+ // Since this test is run on a real device, all the input devices connected
+ // to the test device will show up in mReader. We wait for those input devices to
+ // show up before beginning the tests.
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ }
};
TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
@@ -1486,6 +1490,46 @@
AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
}
+TEST_F(InputReaderIntegrationTest, KeyboardWithStylusButtons) {
+ std::unique_ptr<UinputKeyboard> keyboard =
+ createUinputDevice<UinputKeyboard>("KeyboardWithStylusButtons", /*productId=*/99,
+ std::initializer_list<int>{KEY_Q, KEY_W, KEY_E,
+ KEY_R, KEY_T, KEY_Y,
+ BTN_STYLUS, BTN_STYLUS2,
+ BTN_STYLUS3});
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ const auto device = findDeviceByName(keyboard->getName());
+ ASSERT_TRUE(device.has_value());
+
+ // An alphabetical keyboard that reports stylus buttons should not be recognized as a stylus.
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources())
+ << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
+ ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, device->getKeyboardType());
+}
+
+TEST_F(InputReaderIntegrationTest, HidUsageKeyboardIsNotAStylus) {
+ // Create a Uinput keyboard that simulates a keyboard that can report HID usage codes. The
+ // hid-input driver reports HID usage codes using the value for EV_MSC MSC_SCAN event.
+ std::unique_ptr<UinputKeyboardWithHidUsage> keyboard =
+ createUinputDevice<UinputKeyboardWithHidUsage>(
+ std::initializer_list<int>{KEY_VOLUMEUP, KEY_VOLUMEDOWN});
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ const auto device = findDeviceByName(keyboard->getName());
+ ASSERT_TRUE(device.has_value());
+
+ ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources())
+ << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
+
+ // If a device supports reporting HID usage codes, it shouldn't automatically support
+ // stylus keys.
+ const std::vector<int> keycodes{AKEYCODE_STYLUS_BUTTON_PRIMARY};
+ uint8_t outFlags[] = {0};
+ ASSERT_TRUE(mReader->hasKeys(device->getId(), AINPUT_SOURCE_KEYBOARD, keycodes, outFlags));
+ ASSERT_EQ(0, outFlags[0]) << "Keyboard should not have stylus button";
+}
+
/**
* The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
* on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
@@ -1510,7 +1554,7 @@
// --- TouchIntegrationTest ---
-class TouchIntegrationTest : public InputReaderIntegrationTest {
+class BaseTouchIntegrationTest : public InputReaderIntegrationTest {
protected:
const std::string UNIQUE_ID = "local:0";
@@ -1555,7 +1599,55 @@
InputDeviceInfo mDeviceInfo;
};
-TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) {
+enum class TouchIntegrationTestDisplays { DISPLAY_INTERNAL, DISPLAY_INPUT_PORT, DISPLAY_UNIQUE_ID };
+
+class TouchIntegrationTest : public BaseTouchIntegrationTest,
+ public testing::WithParamInterface<TouchIntegrationTestDisplays> {
+protected:
+ static constexpr std::optional<uint8_t> DISPLAY_PORT = 0;
+ const std::string INPUT_PORT = "uinput_touch/input0";
+
+ void SetUp() override {
+#if !defined(__ANDROID__)
+ GTEST_SKIP();
+#endif
+ if (GetParam() == TouchIntegrationTestDisplays::DISPLAY_INTERNAL) {
+ BaseTouchIntegrationTest::SetUp();
+ return;
+ }
+
+ // setup policy with a input-port or UniqueId association to the display
+ bool isInputPortAssociation =
+ GetParam() == TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT;
+
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ if (isInputPortAssociation) {
+ mFakePolicy->addInputPortAssociation(INPUT_PORT, DISPLAY_PORT.value());
+ } else {
+ mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID);
+ }
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+
+ InputReaderIntegrationTest::setupInputReader();
+
+ mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ INPUT_PORT);
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+ // Add a display linked to a physical port or UniqueId.
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT,
+ ViewportType::INTERNAL);
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ const auto info = findDeviceByName(mDevice->getName());
+ ASSERT_TRUE(info);
+ mDeviceInfo = *info;
+ }
+};
+
+TEST_P(TouchIntegrationTest, MultiTouchDeviceSource) {
// The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus
// buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button
// presses).
@@ -1563,7 +1655,7 @@
mDeviceInfo.getSources());
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1587,7 +1679,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1643,7 +1735,7 @@
* palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without
* losing information about non-palm pointers.
*/
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1686,7 +1778,7 @@
* In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never
* gets sent to the listener.
*/
-TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
+TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1726,7 +1818,7 @@
assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)});
}
-TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+TEST_P(TouchIntegrationTest, InputEvent_ProcessPalm) {
NotifyMotionArgs args;
const Point centerPoint = mDevice->getCenterPoint();
@@ -1777,7 +1869,39 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
+/**
+ * Some drivers historically have reported axis values outside of the range specified in the
+ * evdev axis info. Ensure we don't crash when this happens. For example, a driver may report a
+ * pressure value greater than the reported maximum, since it unclear what specific meaning the
+ * maximum value for pressure has (beyond the maximum value that can be produced by a sensor),
+ * and no units for pressure (resolution) is specified by the evdev documentation.
+ */
+TEST_P(TouchIntegrationTest, AcceptsAxisValuesOutsideReportedRange) {
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // Down with pressure outside the reported range
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendTrackingId(FIRST_TRACKING_ID);
+ mDevice->sendDown(centerPoint);
+ mDevice->sendPressure(UinputTouchScreen::RAW_PRESSURE_MAX + 2);
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Move to a point outside the reported range
+ mDevice->sendMove(Point(DISPLAY_WIDTH, DISPLAY_HEIGHT) + Point(1, 1));
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Up
+ mDevice->sendUp();
+ mDevice->sendSync();
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+}
+
+TEST_P(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
const Point centerPoint = mDevice->getCenterPoint();
// Send down with the pen tool selected. The policy should be notified of the stylus presence.
@@ -1829,7 +1953,7 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
}
-TEST_F(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) {
+TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) {
const Point centerPoint = mDevice->getCenterPoint();
// Down
@@ -1874,19 +1998,24 @@
ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
}
+INSTANTIATE_TEST_SUITE_P(TouchIntegrationTestDisplayVariants, TouchIntegrationTest,
+ testing::Values(TouchIntegrationTestDisplays::DISPLAY_INTERNAL,
+ TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT,
+ TouchIntegrationTestDisplays::DISPLAY_UNIQUE_ID));
+
// --- StylusButtonIntegrationTest ---
// Verify the behavior of button presses reported by various kinds of styluses, including buttons
// reported by the touchscreen's device, by a fused external stylus, and by an un-fused external
// stylus.
template <typename UinputStylusDevice>
-class StylusButtonIntegrationTest : public TouchIntegrationTest {
+class StylusButtonIntegrationTest : public BaseTouchIntegrationTest {
protected:
void SetUp() override {
#if !defined(__ANDROID__)
GTEST_SKIP();
#endif
- TouchIntegrationTest::SetUp();
+ BaseTouchIntegrationTest::SetUp();
mTouchscreen = mDevice.get();
mTouchscreenInfo = mDeviceInfo;
@@ -1924,8 +2053,8 @@
std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{};
// Hide the base class's device to expose it with a different name for readability.
- using TouchIntegrationTest::mDevice;
- using TouchIntegrationTest::mDeviceInfo;
+ using BaseTouchIntegrationTest::mDevice;
+ using BaseTouchIntegrationTest::mDeviceInfo;
};
using StylusButtonIntegrationTestTypes =
@@ -2177,7 +2306,7 @@
// Verify the behavior of an external stylus. An external stylus can report pressure or button
// data independently of the touchscreen, which is then sent as a MotionEvent as part of an
// ongoing stylus gesture that is being emitted by the touchscreen.
-using ExternalStylusIntegrationTest = TouchIntegrationTest;
+using ExternalStylusIntegrationTest = BaseTouchIntegrationTest;
TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) {
const Point centerPoint = mDevice->getCenterPoint();
@@ -2599,7 +2728,7 @@
// A single input device is associated with a specific display. Check that:
// 1. Device is disabled if the viewport corresponding to the associated display is not found
-// 2. Device is disabled when setEnabled API is called
+// 2. Device is disabled when configure API is called
TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
AINPUT_SOURCE_TOUCHSCREEN);
@@ -2706,7 +2835,8 @@
mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY);
InputDevice device(mReader->getContext(), /*id=*/1, /*generation=*/2, /*identifier=*/{});
- device.addEventHubDevice(TEST_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
+ auto _ = device.addEventHubDevice(ARBITRARY_TIME, TEST_EVENTHUB_ID,
+ mFakePolicy->getReaderConfiguration());
device.removeEventHubDevice(TEST_EVENTHUB_ID);
std::string dumpStr, eventHubDevStr;
device.dump(dumpStr, eventHubDevStr);
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
index 1f8cd12..110ca5f 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.cpp
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -38,13 +38,13 @@
}
std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
- int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
if (!mNextDevices.empty()) {
std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
mNextDevices.pop();
return device;
}
- return InputReader::createDeviceLocked(eventHubId, identifier);
+ return InputReader::createDeviceLocked(when, eventHubId, identifier);
}
} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
index fef58ec..ca85558 100644
--- a/services/inputflinger/tests/InstrumentedInputReader.h
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -44,7 +44,7 @@
protected:
virtual std::shared_ptr<InputDevice> createDeviceLocked(
- int32_t eventHubId, const InputDeviceIdentifier& identifier);
+ nsecs_t when, int32_t eventHubId, const InputDeviceIdentifier& identifier);
class FakeInputReaderContext : public ContextImpl {
public:
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
new file mode 100644
index 0000000..7237424
--- /dev/null
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 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.
+ */
+
+#include "../PointerChoreographer.h"
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include "TestInputListener.h"
+
+namespace android {
+
+// Helpers to std::visit with lambdas.
+template <typename... V>
+struct Visitor : V... {};
+template <typename... V>
+Visitor(V...) -> Visitor<V...>;
+
+// --- PointerChoreographerTest ---
+
+class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
+protected:
+ TestInputListener mTestListener;
+ PointerChoreographer mChoreographer{mTestListener, *this};
+
+ std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; }
+};
+
+TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
+ const std::vector<NotifyArgs> allArgs{NotifyInputDevicesChangedArgs{},
+ NotifyConfigurationChangedArgs{},
+ NotifyKeyArgs{},
+ NotifyMotionArgs{},
+ NotifySensorArgs{},
+ NotifySwitchArgs{},
+ NotifyDeviceResetArgs{},
+ NotifyPointerCaptureChangedArgs{},
+ NotifyVibratorStateArgs{}};
+
+ for (auto notifyArgs : allArgs) {
+ mChoreographer.notify(notifyArgs);
+ EXPECT_NO_FATAL_FAILURE(
+ std::visit(Visitor{
+ [&](const NotifyInputDevicesChangedArgs& args) {
+ mTestListener.assertNotifyInputDevicesChangedWasCalled();
+ },
+ [&](const NotifyConfigurationChangedArgs& args) {
+ mTestListener.assertNotifyConfigurationChangedWasCalled();
+ },
+ [&](const NotifyKeyArgs& args) {
+ mTestListener.assertNotifyKeyWasCalled();
+ },
+ [&](const NotifyMotionArgs& args) {
+ mTestListener.assertNotifyMotionWasCalled();
+ },
+ [&](const NotifySensorArgs& args) {
+ mTestListener.assertNotifySensorWasCalled();
+ },
+ [&](const NotifySwitchArgs& args) {
+ mTestListener.assertNotifySwitchWasCalled();
+ },
+ [&](const NotifyDeviceResetArgs& args) {
+ mTestListener.assertNotifyDeviceResetWasCalled();
+ },
+ [&](const NotifyPointerCaptureChangedArgs& args) {
+ mTestListener.assertNotifyCaptureWasCalled();
+ },
+ [&](const NotifyVibratorStateArgs& args) {
+ mTestListener.assertNotifyVibratorStateWasCalled();
+ },
+ },
+ notifyArgs));
+ }
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 97a2614..acc7023 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -157,12 +157,25 @@
injectEvent(EV_SYN, SYN_REPORT, 0);
}
+// --- UinputKeyboardWithHidUsage ---
+
+UinputKeyboardWithHidUsage::UinputKeyboardWithHidUsage(std::initializer_list<int> keys)
+ : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, keys) {}
+
+void UinputKeyboardWithHidUsage::configureDevice(int fd, uinput_user_dev* device) {
+ UinputKeyboard::configureDevice(fd, device);
+
+ ioctl(fd, UI_SET_EVBIT, EV_MSC);
+ ioctl(fd, UI_SET_MSCBIT, MSC_SCAN);
+}
+
// --- UinputTouchScreen ---
-UinputTouchScreen::UinputTouchScreen(const Rect& size)
+UinputTouchScreen::UinputTouchScreen(const Rect& size, const std::string& physicalPort)
: UinputKeyboard(DEVICE_NAME, PRODUCT_ID,
{BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}),
- mSize(size) {}
+ mSize(size),
+ mPhysicalPort(physicalPort) {}
void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
UinputKeyboard::configureDevice(fd, device);
@@ -176,7 +189,11 @@
ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ if (!mPhysicalPort.empty()) {
+ ioctl(fd, UI_SET_PHYS, mPhysicalPort.c_str());
+ }
device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
@@ -190,6 +207,8 @@
device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER;
device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX;
+ device->absmin[ABS_MT_PRESSURE] = RAW_PRESSURE_MIN;
+ device->absmax[ABS_MT_PRESSURE] = RAW_PRESSURE_MAX;
}
void UinputTouchScreen::sendSlot(int32_t slot) {
@@ -202,6 +221,7 @@
void UinputTouchScreen::sendDown(const Point& point) {
injectEvent(EV_KEY, BTN_TOUCH, 1);
+ injectEvent(EV_ABS, ABS_MT_PRESSURE, RAW_PRESSURE_MAX);
injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
}
@@ -211,6 +231,10 @@
injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
}
+void UinputTouchScreen::sendPressure(int32_t pressure) {
+ injectEvent(EV_ABS, ABS_MT_PRESSURE, pressure);
+}
+
void UinputTouchScreen::sendPointerUp() {
sendTrackingId(0xffffffff);
}
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 51e331d..d4b4e77 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -165,13 +165,30 @@
explicit UinputExternalStylusWithPressure();
};
+// --- UinputKeyboardWithUsage ---
+// A keyboard that supports EV_MSC MSC_SCAN through which it can report HID usage codes.
+
+class UinputKeyboardWithHidUsage : public UinputKeyboard {
+public:
+ static constexpr const char* DEVICE_NAME = "Test Uinput Keyboard With Usage";
+ static constexpr int16_t PRODUCT_ID = 47;
+
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+ explicit UinputKeyboardWithHidUsage(std::initializer_list<int> keys);
+
+ void configureDevice(int fd, uinput_user_dev* device) override;
+};
+
// --- UinputTouchScreen ---
// A multi-touch touchscreen device with specific size that also supports styluses.
class UinputTouchScreen : public UinputKeyboard {
public:
static constexpr const char* DEVICE_NAME = "Test Uinput Touch Screen";
- static constexpr int16_t PRODUCT_ID = 47;
+ static constexpr int16_t PRODUCT_ID = 48;
static const int32_t RAW_TOUCH_MIN = 0;
static const int32_t RAW_TOUCH_MAX = 31;
@@ -189,6 +206,7 @@
void sendTrackingId(int32_t trackingId);
void sendDown(const Point& point);
void sendMove(const Point& point);
+ void sendPressure(int32_t pressure);
void sendPointerUp();
void sendUp();
void sendToolType(int32_t toolType);
@@ -197,11 +215,12 @@
const Point getCenterPoint();
protected:
- explicit UinputTouchScreen(const Rect& size);
+ explicit UinputTouchScreen(const Rect& size, const std::string& physicalPort = "");
private:
void configureDevice(int fd, uinput_user_dev* device) override;
const Rect mSize;
+ const std::string mPhysicalPort;
};
} // namespace android
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index d7980f5..9313a89 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -56,6 +56,15 @@
],
fuzz_config: {
cc: ["android-framework-input@google.com"],
+ componentid: 155276,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libinputflinger library",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 2523f3b..8b16890 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -9,7 +9,7 @@
cc_library_shared {
name: "libpowermanager",
-
+ defaults: ["android.hardware.power-ndk_export_shared"],
srcs: [
"BatterySaverPolicyConfig.cpp",
"CoolingDevice.cpp",
@@ -41,7 +41,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
export_shared_lib_headers: [
@@ -49,7 +48,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
cflags: [
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 03fc38d..2b5ddb1 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -23,6 +23,7 @@
cc_benchmark {
name: "libpowermanager_benchmarks",
+ defaults: ["android.hardware.power-ndk_shared"],
srcs: [
"main.cpp",
"PowerHalAidlBenchmarks.cpp",
@@ -41,7 +42,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 08fcdc8..6fc96c0 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -23,6 +23,10 @@
cc_test {
name: "libpowermanager_test",
+ defaults: [
+ "android.hardware.power-ndk_shared",
+ "android.hardware.power-ndk_shared",
+ ],
test_suites: ["device-tests"],
srcs: [
"IThermalManagerTest.cpp",
@@ -52,7 +56,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
],
static_libs: [
"libgmock",
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index d7ca6e1..47fa8b3 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -83,7 +83,7 @@
}
buffer.append("\n");
}
- return std::string(buffer.string());
+ return std::string(buffer.c_str());
}
/**
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 10ca990..3155b4c 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -298,7 +298,7 @@
result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
}
- return result.string();
+ return result.c_str();
}
/**
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 4fff8bb..555b80a 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -63,7 +63,7 @@
void SensorService::SensorDirectConnection::dump(String8& result) const {
Mutex::Autolock _l(mConnectionLock);
result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
- String8(mOpPackageName).string(), getHalChannelHandle(), mActivated.size());
+ String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size());
for (auto &i : mActivated) {
result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
}
@@ -79,7 +79,7 @@
void SensorService::SensorDirectConnection::dump(ProtoOutputStream* proto) const {
using namespace service::SensorDirectConnectionProto;
Mutex::Autolock _l(mConnectionLock);
- proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).string()));
+ proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).c_str()));
proto->write(HAL_CHANNEL_HANDLE, getHalChannelHandle());
proto->write(NUM_SENSOR_ACTIVATED, int(mActivated.size()));
for (auto &i : mActivated) {
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index dc5070c..d469ff4 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -90,12 +90,12 @@
result.append("NORMAL\n");
}
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
- "max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
+ "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
for (auto& it : mSensorInfo) {
const FlushInfo& flushInfo = it.second;
result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(it.first).string(),
+ mService->getSensorName(it.first).c_str(),
it.first,
flushInfo.mFirstFlushPending ? "First flush pending" :
"active",
@@ -131,7 +131,7 @@
} else {
proto->write(OPERATING_MODE, OP_MODE_NORMAL);
}
- proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+ proto->write(PACKAGE_NAME, std::string(mPackageName.c_str()));
proto->write(WAKE_LOCK_REF_COUNT, int32_t(mWakeLockRefCount));
proto->write(UID, int32_t(mUid));
proto->write(CACHE_SIZE, int32_t(mCacheSize));
@@ -848,7 +848,7 @@
if (numBytesRead == sizeof(sensors_event_t)) {
if (!mDataInjectionMode) {
ALOGE("Data injected in normal mode, dropping event"
- "package=%s uid=%d", mPackageName.string(), mUid);
+ "package=%s uid=%d", mPackageName.c_str(), mUid);
// Unregister call backs.
return 0;
}
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index daff4d0..7e30206 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -150,12 +150,12 @@
"%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
") | perm: %s | flags: 0x%08x\n",
s.getHandle(),
- s.getName().string(),
- s.getVendor().string(),
+ s.getName().c_str(),
+ s.getVendor().c_str(),
s.getVersion(),
- s.getStringType().string(),
+ s.getStringType().c_str(),
s.getType(),
- s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a",
+ s.getRequiredPermission().size() ? s.getRequiredPermission().c_str() : "n/a",
static_cast<int>(s.getFlags()));
result.append("\t");
@@ -224,7 +224,7 @@
}
return true;
});
- return std::string(result.string());
+ return std::string(result.c_str());
}
/**
@@ -241,13 +241,13 @@
forEachSensor([&proto] (const Sensor& s) -> bool {
const uint64_t token = proto->start(SENSORS);
proto->write(HANDLE, s.getHandle());
- proto->write(NAME, std::string(s.getName().string()));
- proto->write(VENDOR, std::string(s.getVendor().string()));
+ proto->write(NAME, std::string(s.getName().c_str()));
+ proto->write(VENDOR, std::string(s.getVendor().c_str()));
proto->write(VERSION, s.getVersion());
- proto->write(STRING_TYPE, std::string(s.getStringType().string()));
+ proto->write(STRING_TYPE, std::string(s.getStringType().c_str()));
proto->write(TYPE, s.getType());
proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ?
- s.getRequiredPermission().string() : ""));
+ s.getRequiredPermission().c_str() : ""));
proto->write(FLAGS, int(s.getFlags()));
switch (s.getReportingMode()) {
case AREPORTING_MODE_CONTINUOUS:
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index a34a65b..dc9e821 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -93,7 +93,7 @@
using namespace service::SensorRegistrationInfoProto;
proto->write(TIMESTAMP_SEC, int64_t(mRealtimeSec));
proto->write(SENSOR_HANDLE, mSensorHandle);
- proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+ proto->write(PACKAGE_NAME, std::string(mPackageName.c_str()));
proto->write(PID, int32_t(mPid));
proto->write(UID, int32_t(mUid));
proto->write(SAMPLING_RATE_US, mSamplingRateUs);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index cfafc69..9e6f563 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -629,7 +629,7 @@
i.second->setFormat("mask_data");
}
// if there is events and sensor does not need special permission.
- result.appendFormat("%s: ", s->getSensor().getName().string());
+ result.appendFormat("%s: ", s->getSensor().getName().c_str());
result.append(i.second->dump().c_str());
}
}
@@ -640,7 +640,7 @@
int handle = mActiveSensors.keyAt(i);
if (dev.isSensorActive(handle)) {
result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
- getSensorName(handle).string(),
+ getSensorName(handle).c_str(),
handle,
mActiveSensors.valueAt(i)->getNumConnections());
}
@@ -656,14 +656,14 @@
result.appendFormat(" NORMAL\n");
break;
case RESTRICTED:
- result.appendFormat(" RESTRICTED : %s\n", mAllowListedPackage.string());
+ result.appendFormat(" RESTRICTED : %s\n", mAllowListedPackage.c_str());
break;
case DATA_INJECTION:
- result.appendFormat(" DATA_INJECTION : %s\n", mAllowListedPackage.string());
+ result.appendFormat(" DATA_INJECTION : %s\n", mAllowListedPackage.c_str());
break;
case REPLAY_DATA_INJECTION:
result.appendFormat(" REPLAY_DATA_INJECTION : %s\n",
- mAllowListedPackage.string());
+ mAllowListedPackage.c_str());
break;
default:
result.appendFormat(" UNKNOWN\n");
@@ -705,7 +705,7 @@
} while(startIndex != currentIndex);
}
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return NO_ERROR;
}
@@ -753,7 +753,7 @@
"normal" : "mask_data");
const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
proto.write(service::SensorEventsProto::RecentEventsLog::NAME,
- std::string(s->getSensor().getName().string()));
+ std::string(s->getSensor().getName().c_str()));
i.second->dump(&proto);
proto.end(mToken);
}
@@ -767,7 +767,7 @@
if (dev.isSensorActive(handle)) {
token = proto.start(ACTIVE_SENSORS);
proto.write(service::ActiveSensorProto::NAME,
- std::string(getSensorName(handle).string()));
+ std::string(getSensorName(handle).c_str()));
proto.write(service::ActiveSensorProto::HANDLE, handle);
proto.write(service::ActiveSensorProto::NUM_CONNECTIONS,
int(mActiveSensors.valueAt(i)->getNumConnections()));
@@ -785,11 +785,11 @@
break;
case RESTRICTED:
proto.write(OPERATING_MODE, OP_MODE_RESTRICTED);
- proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string()));
+ proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.c_str()));
break;
case DATA_INJECTION:
proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
- proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.string()));
+ proto.write(WHITELISTED_PACKAGE, std::string(mAllowListedPackage.c_str()));
break;
default:
proto.write(OPERATING_MODE, OP_MODE_UNKNOWN);
@@ -932,8 +932,8 @@
PermissionController pc;
uid = pc.getPackageUid(packageName, 0);
if (uid <= 0) {
- ALOGE("Unknown package: '%s'", String8(packageName).string());
- dprintf(err, "Unknown package: '%s'\n", String8(packageName).string());
+ ALOGE("Unknown package: '%s'", String8(packageName).c_str());
+ dprintf(err, "Unknown package: '%s'\n", String8(packageName).c_str());
return BAD_VALUE;
}
@@ -958,7 +958,7 @@
if (args[2] == String16("active")) {
active = true;
} else if ((args[2] != String16("idle"))) {
- ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
+ ALOGE("Expected active or idle but got: '%s'", String8(args[2]).c_str());
return BAD_VALUE;
}
@@ -2217,10 +2217,10 @@
!isAudioServerOrSystemServerUid(IPCThreadState::self()->getCallingUid())) {
if (!mHtRestricted) {
ALOGI("Permitting access to HT sensor type outside system (%s)",
- String8(opPackageName).string());
+ String8(opPackageName).c_str());
} else {
- ALOGW("%s %s a sensor (%s) as a non-system client", String8(opPackageName).string(),
- operation, sensor.getName().string());
+ ALOGW("%s %s a sensor (%s) as a non-system client", String8(opPackageName).c_str(),
+ operation, sensor.getName().c_str());
return false;
}
}
@@ -2253,8 +2253,8 @@
}
if (!canAccess) {
- ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
- operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+ ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).c_str(),
+ operation, sensor.getName().c_str(), sensor.getRequiredPermission().c_str());
}
return canAccess;
@@ -2434,7 +2434,7 @@
}
bool SensorService::isAllowListedPackage(const String8& packageName) {
- return (packageName.contains(mAllowListedPackage.string()));
+ return (packageName.contains(mAllowListedPackage.c_str()));
}
bool SensorService::isOperationRestrictedLocked(const String16& opPackageName) {
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 5fa594d..d338d02 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -32,8 +32,8 @@
SensorInfo dst;
const String8& name = src.getName();
const String8& vendor = src.getVendor();
- dst.name = hidl_string{name.string(), name.size()};
- dst.vendor = hidl_string{vendor.string(), vendor.size()};
+ dst.name = hidl_string{name.c_str(), name.size()};
+ dst.vendor = hidl_string{vendor.c_str(), vendor.size()};
dst.version = src.getVersion();
dst.sensorHandle = src.getHandle();
dst.type = static_cast<::android::hardware::sensors::V1_0::SensorType>(
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1baf397..92956d6 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -116,7 +116,7 @@
Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
printf("accelerometer=%p (%s)\n",
- accelerometer, accelerometer->getName().string());
+ accelerometer, accelerometer->getName().c_str());
sStartTime = systemTime();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 326645e..0101c17 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,6 +26,7 @@
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
@@ -48,7 +49,6 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.power-V4-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index b56b252..fefc040 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -29,7 +29,9 @@
#include <set>
#include <unordered_map>
-#define BUFFER_CACHE_MAX_SIZE 64
+// 4096 is based on 64 buffers * 64 layers. Once this limit is reached, the least recently used
+// buffer is uncached before the new buffer is cached.
+#define BUFFER_CACHE_MAX_SIZE 4096
namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 3426495..06c5e4c 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
name: "libcompositionengine_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
@@ -27,7 +28,6 @@
"android.hardware.graphics.composer@2.4",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
"libbase",
"libcutils",
"libgui",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
index 9f6141a..d607c75 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h
@@ -66,7 +66,7 @@
TexturePool(renderengine::RenderEngine& renderEngine)
: mRenderEngine(renderEngine), mEnabled(false) {}
- virtual ~TexturePool();
+ virtual ~TexturePool() = default;
// Sets the display size for the texture pool.
// This will trigger a reallocation for all remaining textures in the pool.
@@ -83,10 +83,11 @@
// be held by the pool. This is useful when the active display changes.
void setEnabled(bool enable);
- void dump(std::string& out) const EXCLUDES(mMutex);
+ void dump(std::string& out) const;
protected:
// Proteted visibility so that they can be used for testing
+ const static constexpr size_t kMinPoolSize = 3;
const static constexpr size_t kMaxPoolSize = 4;
struct Entry {
@@ -95,20 +96,16 @@
};
std::deque<Entry> mPool;
- std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture;
private:
- std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size);
+ std::shared_ptr<renderengine::ExternalTexture> genTexture();
// Returns a previously borrowed texture to the pool.
void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture,
const sp<Fence>& fence);
- void genTextureAsyncIfNeeded() REQUIRES(mMutex);
- void resetPool() REQUIRES(mMutex);
- renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex);
- ui::Size mSize GUARDED_BY(mMutex);
+ void allocatePool();
+ renderengine::RenderEngine& mRenderEngine;
+ ui::Size mSize;
bool mEnabled;
- mutable std::mutex mMutex;
- mutable std::mutex mRenderEngineMutex;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index 752257b..bdaa1d0 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -41,8 +41,7 @@
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
- return lhs.textureName == rhs.textureName &&
- lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ return lhs.useTextureFiltering == rhs.useTextureFiltering &&
lhs.textureTransform == rhs.textureTransform &&
lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
lhs.isOpaque == rhs.isOpaque && lhs.maxLuminanceNits == rhs.maxLuminanceNits;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 0b11e74..78c23da 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1315,17 +1315,9 @@
});
const nsecs_t renderEngineStart = systemTime();
- // Only use the framebuffer cache when rendering to an internal display
- // TODO(b/173560331): This is only to help mitigate memory leaks from virtual displays because
- // right now we don't have a concrete eviction policy for output buffers: GLESRenderEngine
- // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is
- // probably to encapsulate the output buffer into a structure that dispatches resource cleanup
- // over to RenderEngine, in which case this flag can be removed from the drawLayers interface.
- const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
-
auto fenceResult = renderEngine
.drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex,
- useFramebufferCache, std::move(fd))
+ std::move(fd))
.get();
if (mClientCompositionRequestCache && fenceStatus(fenceResult) != NO_ERROR) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b492b6a..22db247 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include <DisplayHardware/Hal.h>
#include <android-base/stringprintf.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -26,7 +25,7 @@
#include <cstdint>
#include "system/graphics-base-v1.0.h"
-#include <ui/DataspaceUtils.h>
+#include <ui/HdrRenderTypeUtils.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -312,12 +311,19 @@
}
}
+ auto pixelFormat = layerFEState->buffer ? std::make_optional(static_cast<ui::PixelFormat>(
+ layerFEState->buffer->getPixelFormat()))
+ : std::nullopt;
+
+ auto hdrRenderType =
+ getHdrRenderType(outputState.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+
// Determine the output dependent dataspace for this layer. If it is
// colorspace agnostic, it just uses the dataspace chosen for the output to
// avoid the need for color conversion.
// For now, also respect the colorspace agnostic flag if we're drawing to HDR, to avoid drastic
// luminance shift. TODO(b/292162273): we should check if that's true though.
- state.dataspace = layerFEState->isColorspaceAgnostic && !isHdrDataspace(outputState.dataspace)
+ state.dataspace = layerFEState->isColorspaceAgnostic && hdrRenderType == HdrRenderType::SDR
? outputState.dataspace
: layerFEState->dataspace;
@@ -332,10 +338,14 @@
(state.dataspace & HAL_DATASPACE_RANGE_MASK) | HAL_DATASPACE_TRANSFER_SRGB);
}
+ // re-get HdrRenderType after the dataspace gets changed.
+ hdrRenderType =
+ getHdrRenderType(state.dataspace, pixelFormat, layerFEState->desiredHdrSdrRatio);
+
// For hdr content, treat the white point as the display brightness - HDR content should not be
// boosted or dimmed.
// If the layer explicitly requests to disable dimming, then don't dim either.
- if (isHdrDataspace(state.dataspace) ||
+ if (hdrRenderType == HdrRenderType::GENERIC_HDR ||
getOutput().getState().displayBrightnessNits == getOutput().getState().sdrWhitePointNits ||
getOutput().getState().displayBrightnessNits == 0.f || !layerFEState->dimmingEnabled) {
state.dimmingRatio = 1.f;
@@ -344,8 +354,7 @@
float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
// RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
// range that we may need to re-adjust to the current display conditions
- if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
- layerFEState->currentHdrSdrRatio > 1.01f) {
+ if (hdrRenderType == HdrRenderType::DISPLAY_HDR) {
layerBrightnessNits *= layerFEState->currentHdrSdrRatio;
}
state.dimmingRatio =
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 7547be9..579c6ba 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -275,11 +275,9 @@
bufferFence.reset(texture->getReadyFence()->dup());
}
- constexpr bool kUseFramebufferCache = false;
-
auto fenceResult = renderEngine
.drawLayers(displaySettings, layerSettings, texture->get(),
- kUseFramebufferCache, std::move(bufferFence))
+ std::move(bufferFence))
.get();
if (fenceStatus(fenceResult) == NO_ERROR) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index f439caf..8dab6ce 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -34,7 +34,7 @@
[](const mat4& mat) {
using namespace std::string_literals;
std::vector<std::string> split =
- base::Split(std::string(mat.asString().string()), "\n"s);
+ base::Split(std::string(mat.asString().c_str()), "\n"s);
split.pop_back(); // Strip the last (empty) line
return split;
}}) {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 54133d9..5e6cade 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -216,32 +216,32 @@
base::StringAppendF(&result,
"Expected two layer stack hashes, e.g. '--planner %s "
"<left_hash> <right_hash>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 4) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <left_hash> "
"<right_hash>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 leftHashString(args[2]);
size_t leftHash = 0;
- int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash);
+ int fieldsRead = sscanf(leftHashString.c_str(), "%zx", &leftHash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- leftHashString.string());
+ leftHashString.c_str());
return;
}
const String8 rightHashString(args[3]);
size_t rightHash = 0;
- fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash);
+ fieldsRead = sscanf(rightHashString.c_str(), "%zx", &rightHash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- rightHashString.string());
+ rightHashString.c_str());
return;
}
@@ -252,22 +252,22 @@
if (args.size() < 3) {
base::StringAppendF(&result,
"Expected a layer stack hash, e.g. '--planner %s <hash>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 3) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <hash>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 hashString(args[2]);
size_t hash = 0;
- const int fieldsRead = sscanf(hashString.string(), "%zx", &hash);
+ const int fieldsRead = sscanf(hashString.c_str(), "%zx", &hash);
if (fieldsRead != 1) {
base::StringAppendF(&result, "Failed to parse %s as a size_t\n",
- hashString.string());
+ hashString.c_str());
return;
}
@@ -279,20 +279,20 @@
} else if (command == "--similar" || command == "-s") {
if (args.size() < 3) {
base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n",
- command.string());
+ command.c_str());
return;
}
if (args.size() > 3) {
base::StringAppendF(&result,
"Too many arguments found, expected '--planner %s <plan>'\n",
- command.string());
+ command.c_str());
return;
}
const String8 planString(args[2]);
- std::optional<Plan> plan = Plan::fromString(std::string(planString.string()));
+ std::optional<Plan> plan = Plan::fromString(std::string(planString.c_str()));
if (!plan) {
- base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string());
+ base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.c_str());
return;
}
@@ -302,7 +302,7 @@
} else if (command == "--layers" || command == "-l") {
mFlattener.dumpLayers(result);
} else {
- base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string());
+ base::StringAppendF(&result, "Unknown command '%s'\n\n", command.c_str());
dumpUsage(result);
}
return;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
index 10f58ce..54ecb56 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp
@@ -25,61 +25,31 @@
namespace android::compositionengine::impl::planner {
-TexturePool::~TexturePool() {
- if (mGenTextureFuture.valid()) {
- mGenTextureFuture.get();
- }
-}
-
-void TexturePool::resetPool() {
- if (mGenTextureFuture.valid()) {
- mGenTextureFuture.get();
- }
+void TexturePool::allocatePool() {
mPool.clear();
- genTextureAsyncIfNeeded();
-}
-
-// Generate a new texture asynchronously so it will not require allocation on the main
-// thread.
-void TexturePool::genTextureAsyncIfNeeded() {
- if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) {
- mGenTextureFuture = std::async(
- std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize);
+ if (mEnabled && mSize.isValid()) {
+ mPool.resize(kMinPoolSize);
+ std::generate_n(mPool.begin(), kMinPoolSize, [&]() {
+ return Entry{genTexture(), nullptr};
+ });
}
}
void TexturePool::setDisplaySize(ui::Size size) {
- std::lock_guard lock(mMutex);
if (mSize == size) {
return;
}
mSize = size;
- resetPool();
+ allocatePool();
}
std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() {
if (mPool.empty()) {
- std::lock_guard lock(mMutex);
- std::shared_ptr<TexturePool::AutoTexture> tex;
- if (mGenTextureFuture.valid()) {
- tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr);
- } else {
- tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr);
- }
- // Speculatively generate a new texture, so that the next call does not need
- // to wait for allocation.
- genTextureAsyncIfNeeded();
- return tex;
+ return std::make_shared<AutoTexture>(*this, genTexture(), nullptr);
}
const auto entry = mPool.front();
mPool.pop_front();
- if (mPool.empty()) {
- std::lock_guard lock(mMutex);
- // Similiarly generate a new texture when lending out the last entry, so that
- // the next call does not need to wait for allocation.
- genTextureAsyncIfNeeded();
- }
return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence);
}
@@ -90,8 +60,6 @@
return;
}
- std::lock_guard lock(mMutex);
-
// Or the texture on the floor if the pool is no longer tracking textures of the same size.
if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() ||
static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) {
@@ -112,14 +80,13 @@
mPool.push_back({std::move(texture), fence});
}
-std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) {
- std::lock_guard lock(mRenderEngineMutex);
- LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size");
+std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() {
+ LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size");
return std::make_shared<
renderengine::impl::
ExternalTexture>(sp<GraphicBuffer>::
- make(static_cast<uint32_t>(size.getWidth()),
- static_cast<uint32_t>(size.getHeight()),
+ make(static_cast<uint32_t>(mSize.getWidth()),
+ static_cast<uint32_t>(mSize.getHeight()),
HAL_PIXEL_FORMAT_RGBA_8888, 1U,
static_cast<uint64_t>(
GraphicBuffer::USAGE_HW_RENDER |
@@ -133,16 +100,13 @@
void TexturePool::setEnabled(bool enabled) {
mEnabled = enabled;
-
- std::lock_guard lock(mMutex);
- resetPool();
+ allocatePool();
}
void TexturePool::dump(std::string& out) const {
- std::lock_guard lock(mMutex);
base::StringAppendF(&out,
"TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n",
mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height);
}
-} // namespace android::compositionengine::impl::planner
+} // namespace android::compositionengine::impl::planner
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ebf9a2b..ee6998a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3421,10 +3421,10 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
return ftl::yield<FenceResult>(Fence::NO_FENCE);
});
@@ -3452,10 +3452,10 @@
}));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
return ftl::yield<FenceResult>(Fence::NO_FENCE);
});
@@ -3486,10 +3486,10 @@
}));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, true, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
return ftl::yield<FenceResult>(Fence::NO_FENCE);
});
@@ -3515,7 +3515,7 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.Times(2)
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
@@ -3545,7 +3545,7 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -3581,10 +3581,10 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
.WillOnce(Return(mOutputBuffer))
.WillOnce(Return(otherOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
return ftl::yield<FenceResult>(Fence::NO_FENCE);
});
@@ -3616,9 +3616,9 @@
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
verify().execute().expectAFenceWasReturned();
@@ -3695,7 +3695,7 @@
struct ExpectDisplaySettingsState
: public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
- EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
+ EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
return nextState<ExecuteState>();
}
@@ -3948,11 +3948,11 @@
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
const std::shared_ptr<renderengine::ExternalTexture>&,
- const bool, base::unique_fd&&) -> ftl::Future<FenceResult> {
+ base::unique_fd&&) -> ftl::Future<FenceResult> {
return ftl::yield<FenceResult>(Fence::NO_FENCE);
});
}
@@ -3987,7 +3987,7 @@
EXPECT_CALL(*mRenderSurface, setProtected(true));
// Must happen after setting the protected content state.
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
base::unique_fd fd;
@@ -4046,7 +4046,7 @@
InSequence seq;
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
base::unique_fd fd;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index bd030d0..d61d7ba 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -353,7 +353,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
@@ -369,7 +369,7 @@
.WillOnce(Return(clientComp1));
EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false)))
.WillOnce(Return(clientComp2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = false;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
@@ -402,7 +402,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
@@ -419,7 +419,7 @@
.WillOnce(Return(clientComp1));
EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true)))
.WillOnce(Return(clientComp2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
@@ -452,7 +452,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
return ftl::yield<FenceResult>(Fence::NO_FENCE);
@@ -466,7 +466,7 @@
prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
mOutputState.displayBrightnessNits)))
.WillOnce(Return(clientComp2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
@@ -502,7 +502,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
return ftl::yield<FenceResult>(Fence::NO_FENCE);
@@ -516,7 +516,7 @@
prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq(
mOutputState.displayBrightnessNits)))
.WillOnce(Return(clientComp2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false);
expectReadyBuffer(cachedSet);
@@ -551,7 +551,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
@@ -566,7 +566,7 @@
EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1));
EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
expectReadyBuffer(cachedSet);
@@ -815,7 +815,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
@@ -839,7 +839,7 @@
return ftl::yield<FenceResult>(Fence::NO_FENCE);
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
@@ -875,7 +875,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
@@ -900,7 +900,7 @@
return ftl::yield<FenceResult>(Fence::NO_FENCE);
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
@@ -1026,7 +1026,7 @@
const auto drawLayers = [&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
@@ -1039,7 +1039,7 @@
return ftl::yield<FenceResult>(Fence::NO_FENCE);
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 778a0a8..00590e6 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -168,7 +168,7 @@
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -419,7 +419,7 @@
// caleed for Layer2 and Layer3
layerState1->resetFramesSinceBufferUpdate();
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -442,7 +442,7 @@
layerState1->incrementFramesSinceBufferUpdate();
mTime += 200ms;
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
@@ -494,7 +494,7 @@
// called for Layer1 and Layer2
layerState3->resetFramesSinceBufferUpdate();
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -508,7 +508,7 @@
EXPECT_EQ(nullptr, overrideBuffer5);
// Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
@@ -537,7 +537,7 @@
layerState3->incrementFramesSinceBufferUpdate();
mTime += 200ms;
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
@@ -592,7 +592,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -603,7 +603,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -656,7 +656,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -667,7 +667,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -728,7 +728,7 @@
// This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
// exception that there would be a hole punch above it.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -736,7 +736,7 @@
EXPECT_EQ(nullptr, overrideBuffer0);
// This time we merge the CachedSet in and we should still have only two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -798,7 +798,7 @@
// This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
// exception that there would be a hole punch above it.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -806,7 +806,7 @@
EXPECT_EQ(nullptr, overrideBuffer0);
// This time we merge the CachedSet in and we should still have only two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -849,7 +849,7 @@
layerState3->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -894,7 +894,7 @@
layerState1->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillRepeatedly(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -947,7 +947,7 @@
layerState1->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -995,7 +995,7 @@
layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -1037,7 +1037,7 @@
// Mark the layers inactive
mTime += 200ms;
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
initializeOverrideBuffer(layers);
@@ -1050,7 +1050,7 @@
// Simulate attempting to render prior to merging the new cached set with the layer stack.
// Here we should not try to re-render.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
// We provide the override buffer now that it's rendered
@@ -1097,14 +1097,14 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) {
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
mFlattener->renderCachedSets(mOutputState,
std::chrono::steady_clock::now() -
(kCachedSetRenderDuration + 10ms),
true);
}
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState,
std::chrono::steady_clock::now() -
@@ -1139,7 +1139,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1150,7 +1150,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1189,7 +1189,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1200,7 +1200,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1239,7 +1239,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1250,7 +1250,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1289,7 +1289,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1300,7 +1300,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1342,7 +1342,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1354,7 +1354,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -1394,7 +1394,7 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
.WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
@@ -1405,7 +1405,7 @@
// This time we merge the CachedSet in, so we have a new hash, and we should
// only have two sets.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
index 494a9f4..6fc90fe 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp
@@ -32,9 +32,9 @@
public:
TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {}
+ size_t getMinPoolSize() const { return kMinPoolSize; }
size_t getMaxPoolSize() const { return kMaxPoolSize; }
size_t getPoolSize() const { return mPool.size(); }
- size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); }
};
struct TexturePoolTest : public testing::Test {
@@ -56,8 +56,16 @@
TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine);
};
-TEST_F(TexturePoolTest, preallocatesZeroSizePool) {
- EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+TEST_F(TexturePoolTest, preallocatesMinPool) {
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
+}
+
+TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) {
+ for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) {
+ auto texture = mTexturePool.borrowTexture();
+ }
+
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
}
TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) {
@@ -111,10 +119,10 @@
static_cast<int32_t>(texture->get()->getBuffer()->getHeight()));
mTexturePool.setDisplaySize(kDisplaySizeTwo);
- EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
texture.reset();
// When the texture is returned to the pool, the pool now destroys it.
- EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
+ EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize());
texture = mTexturePool.borrowTexture();
EXPECT_EQ(kDisplaySizeTwo.getWidth(),
@@ -124,11 +132,14 @@
}
TEST_F(TexturePoolTest, freesBuffersWhenDisabled) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
+
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < 2; i++) {
+ for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
+ EXPECT_EQ(mTexturePool.getPoolSize(), 1u);
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
@@ -137,11 +148,12 @@
}
TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures;
- for (size_t i = 0; i < 2; i++) {
+ for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) {
textures.emplace_back(mTexturePool.borrowTexture());
}
@@ -150,13 +162,12 @@
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
}
-TEST_F(TexturePoolTest, genFutureWhenReEnabled) {
+TEST_F(TexturePoolTest, reallocatesWhenReEnabled) {
+ EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
mTexturePool.setEnabled(false);
EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
- EXPECT_FALSE(mTexturePool.isGenTextureFutureValid());
mTexturePool.setEnabled(true);
- EXPECT_EQ(mTexturePool.getPoolSize(), 0u);
- EXPECT_TRUE(mTexturePool.isGenTextureFutureValid());
+ EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize());
}
} // namespace
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index c26edb5..0788d1a 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -54,7 +54,6 @@
gui::LayerMetadata metadata;
pid_t ownerPid;
uid_t ownerUid;
- uint32_t textureName;
uint32_t sequence;
bool addToRoot = true;
wp<IBinder> parentHandle = nullptr;
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index ab4c15d..962dc09 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -60,9 +60,9 @@
return;
}
}
- if (traversalPath.hasRelZLoop()) {
- LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
- }
+
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(traversalPath.hasRelZLoop(), "Found relative z loop layerId:%d",
+ traversalPath.invalidRelativeRootId);
for (auto& [child, childVariant] : mChildren) {
ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
childVariant);
@@ -104,9 +104,7 @@
[child](const std::pair<LayerHierarchy*, Variant>& x) {
return x.first == child;
});
- if (it == mChildren.end()) {
- LOG_ALWAYS_FATAL("Could not find child!");
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mChildren.end(), "Could not find child!");
mChildren.erase(it);
}
@@ -119,11 +117,8 @@
[hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
return child.first == hierarchy;
});
- if (it == mChildren.end()) {
- LOG_ALWAYS_FATAL("Could not find child!");
- } else {
- it->second = variant;
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mChildren.end(), "Could not find child!");
+ it->second = variant;
}
const RequestedLayerState* LayerHierarchy::getLayer() const {
@@ -422,9 +417,8 @@
LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
auto it = mLayerIdToHierarchy.find(layerId);
if (it == mLayerIdToHierarchy.end()) {
- if (crashOnFailure) {
- LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(crashOnFailure, "Could not find hierarchy for layer id %d",
+ layerId);
return nullptr;
};
@@ -460,7 +454,7 @@
}
LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::getMirrorRoot() const {
- LOG_ALWAYS_FATAL_IF(!isClone(), "Cannot get mirror root of a non cloned node");
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!isClone(), "Cannot get mirror root of a non cloned node");
TraversalPath mirrorRootPath = *this;
mirrorRootPath.id = mirrorRootId;
return mirrorRootPath;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 1712137..a826ec1 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -45,11 +45,11 @@
for (auto& newLayer : newLayers) {
RequestedLayerState& layer = *newLayer.get();
auto [it, inserted] = mIdToLayer.try_emplace(layer.id, References{.owner = layer});
- if (!inserted) {
- LOG_ALWAYS_FATAL("Duplicate layer id found. New layer: %s Existing layer: %s",
- layer.getDebugString().c_str(),
- it->second.owner.getDebugString().c_str());
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!inserted,
+ "Duplicate layer id found. New layer: %s Existing layer: "
+ "%s",
+ layer.getDebugString().c_str(),
+ it->second.owner.getDebugString().c_str());
mAddedLayers.push_back(newLayer.get());
mChangedLayers.push_back(newLayer.get());
layer.parentId = linkLayer(layer.parentId, layer.id);
@@ -85,14 +85,15 @@
}
}
-void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& destroyedHandles,
- bool ignoreUnknownHandles) {
+void LayerLifecycleManager::onHandlesDestroyed(
+ const std::vector<std::pair<uint32_t, std::string /* debugName */>>& destroyedHandles,
+ bool ignoreUnknownHandles) {
std::vector<uint32_t> layersToBeDestroyed;
- for (const auto& layerId : destroyedHandles) {
+ for (const auto& [layerId, name] : destroyedHandles) {
auto it = mIdToLayer.find(layerId);
if (it == mIdToLayer.end()) {
- LOG_ALWAYS_FATAL_IF(!ignoreUnknownHandles, "%s Layerid not found %d", __func__,
- layerId);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!ignoreUnknownHandles, "%s Layerid not found %s[%d]",
+ __func__, name.c_str(), layerId);
continue;
}
RequestedLayerState& layer = it->second.owner;
@@ -113,10 +114,8 @@
for (size_t i = 0; i < layersToBeDestroyed.size(); i++) {
uint32_t layerId = layersToBeDestroyed[i];
auto it = mIdToLayer.find(layerId);
- if (it == mIdToLayer.end()) {
- LOG_ALWAYS_FATAL("%s Layer with id %d not found", __func__, layerId);
- continue;
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mIdToLayer.end(), "%s Layer with id %d not found",
+ __func__, layerId);
RequestedLayerState& layer = it->second.owner;
@@ -135,11 +134,9 @@
auto& references = it->second.references;
for (uint32_t linkedLayerId : references) {
RequestedLayerState* linkedLayer = getLayerFromId(linkedLayerId);
- if (!linkedLayer) {
- LOG_ALWAYS_FATAL("%s Layerid reference %d not found for %d", __func__,
- linkedLayerId, layer.id);
- continue;
- };
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!linkedLayer,
+ "%s Layerid reference %d not found for %d", __func__,
+ linkedLayerId, layer.id);
if (linkedLayer->parentId == layer.id) {
linkedLayer->parentId = UNASSIGNED_LAYER_ID;
if (linkedLayer->canBeDestroyed()) {
@@ -191,17 +188,17 @@
RequestedLayerState* layer = getLayerFromId(layerId);
if (layer == nullptr) {
- LOG_ALWAYS_FATAL_IF(!ignoreUnknownLayers, "%s Layer with layerid=%d not found",
- __func__, layerId);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!ignoreUnknownLayers,
+ "%s Layer with layerid=%d not found", __func__,
+ layerId);
continue;
}
- if (!layer->handleAlive) {
- LOG_ALWAYS_FATAL("%s Layer's with layerid=%d) is not alive. Possible out of "
- "order LayerLifecycleManager updates",
- __func__, layerId);
- continue;
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(!layer->handleAlive,
+ "%s Layer's with layerid=%d) is not alive. Possible "
+ "out of "
+ "order LayerLifecycleManager updates",
+ __func__, layerId);
if (layer->changes.get() == 0) {
mChangedLayers.push_back(layer);
@@ -241,7 +238,7 @@
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
layer->bgColorLayerId = UNASSIGNED_LAYER_ID;
bgColorLayer->parentId = unlinkLayer(bgColorLayer->parentId, bgColorLayer->id);
- onHandlesDestroyed({bgColorLayer->id});
+ onHandlesDestroyed({{bgColorLayer->id, bgColorLayer->debugName}});
} else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) {
RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId);
bgColorLayer->color = layer->bgColor;
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 48571bf..9aff78e 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -47,7 +47,8 @@
// Ignore unknown handles when iteroping with legacy front end. In the old world, we
// would create child layers which are not necessary with the new front end. This means
// we will get notified for handle changes that don't exist in the new front end.
- void onHandlesDestroyed(const std::vector<uint32_t>&, bool ignoreUnknownHandles = false);
+ void onHandlesDestroyed(const std::vector<std::pair<uint32_t, std::string /* debugName */>>&,
+ bool ignoreUnknownHandles = false);
// Detaches the layer from its relative parent to prevent a loop in the
// layer hierarchy. This overrides the RequestedLayerState and leaves
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
index 4943483..3845dfe 100644
--- a/services/surfaceflinger/FrontEnd/LayerLog.h
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -16,6 +16,8 @@
#pragma once
+#include "Tracing/TransactionTracing.h"
+
// Uncomment to trace layer updates for a single layer
// #define LOG_LAYER 1
@@ -27,3 +29,17 @@
#endif
#define LLOGD(LAYER_ID, x, ...) ALOGD("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
+
+#define LLOG_ALWAYS_FATAL_WITH_TRACE(...) \
+ do { \
+ TransactionTraceWriter::getInstance().invoke(__func__, /* overwrite= */ false); \
+ LOG_ALWAYS_FATAL(##__VA_ARGS__); \
+ } while (false)
+
+#define LLOG_ALWAYS_FATAL_WITH_TRACE_IF(cond, ...) \
+ do { \
+ if (__predict_false(cond)) { \
+ TransactionTraceWriter::getInstance().invoke(__func__, /* overwrite= */ false); \
+ } \
+ LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__); \
+ } while (false)
\ No newline at end of file
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 5d41fdd..80bedf4 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -19,6 +19,7 @@
#define LOG_TAG "SurfaceFlinger"
#include "LayerSnapshot.h"
+#include "Layer.h"
namespace android::surfaceflinger::frontend {
@@ -117,7 +118,6 @@
sequence = static_cast<int32_t>(state.id);
name = state.name;
debugName = state.debugName;
- textureName = state.textureName;
premultipliedAlpha = state.premultipliedAlpha;
inputInfo.name = state.name;
inputInfo.id = static_cast<int32_t>(uniqueSequence);
@@ -363,7 +363,7 @@
geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
}
if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) {
- dataspace = requested.dataspace;
+ dataspace = Layer::translateDataspace(requested.dataspace);
}
if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) {
currentHdrSdrRatio = requested.currentHdrSdrRatio;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 92d23e2..7537a39 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -64,7 +64,6 @@
int32_t sequence;
std::string name;
std::string debugName;
- uint32_t textureName;
bool contentOpaque;
bool layerOpaqueFlagSet;
RoundedCornerState roundedCorner;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 159d0f0..da84e44 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -178,12 +178,7 @@
info.touchableRegion.clear();
}
- const Rect roundedFrameInDisplay =
- getInputBoundsInDisplaySpace(snapshot, inputBounds, screenToDisplay);
- info.frameLeft = roundedFrameInDisplay.left;
- info.frameTop = roundedFrameInDisplay.top;
- info.frameRight = roundedFrameInDisplay.right;
- info.frameBottom = roundedFrameInDisplay.bottom;
+ info.frame = getInputBoundsInDisplaySpace(snapshot, inputBounds, screenToDisplay);
ui::Transform inputToLayer;
inputToLayer.set(inputBounds.left, inputBounds.top);
@@ -523,12 +518,9 @@
const Args& args, const LayerHierarchy& hierarchy,
LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot,
int depth) {
- if (depth > 50) {
- TransactionTraceWriter::getInstance().invoke("layer_builder_stack_overflow_",
- /*overwrite=*/false);
- LOG_ALWAYS_FATAL("Cycle detected in LayerSnapshotBuilder. See "
- "builder_stack_overflow_transactions.winscope");
- }
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
+ "Cycle detected in LayerSnapshotBuilder. See "
+ "builder_stack_overflow_transactions.winscope");
const RequestedLayerState* layer = hierarchy.getLayer();
LayerSnapshot* snapshot = getSnapshot(traversalPath);
@@ -675,8 +667,7 @@
}
using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
- if (snapshot.frameRate.rate.isValid() ||
- snapshot.frameRate.type == FrameRateCompatibility::NoVote) {
+ if (snapshot.frameRate.isValid()) {
// we already have a valid framerate.
return;
}
@@ -684,15 +675,17 @@
// We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes
// for the same reason we are allowing touch boost for those layers. See
// RefreshRateSelector::rankFrameRates for details.
- const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.vote.rate.isValid() &&
+ childSnapshot.frameRate.vote.type == FrameRateCompatibility::Default;
const auto layerVotedWithNoVote =
- childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
- const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
- childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+ childSnapshot.frameRate.vote.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithCategory =
+ childSnapshot.frameRate.category != FrameRateCategory::Default;
+ const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.vote.rate.isValid() &&
+ childSnapshot.frameRate.vote.type == FrameRateCompatibility::Exact;
bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
- layerVotedWithExactCompatibility;
+ layerVotedWithCategory || layerVotedWithExactCompatibility;
// If we don't have a valid frame rate, but the children do, we set this
// layer as NoVote to allow the children to control the refresh rate
@@ -820,11 +813,8 @@
RequestedLayerState::Changes::Hierarchy) ||
snapshot.changes.any(RequestedLayerState::Changes::FrameRate |
RequestedLayerState::Changes::Hierarchy)) {
- snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
- (requested.requestedFrameRate.type ==
- scheduler::LayerInfo::FrameRateCompatibility::NoVote))
- ? requested.requestedFrameRate
- : parentSnapshot.frameRate;
+ snapshot.frameRate = requested.requestedFrameRate.isValid() ? requested.requestedFrameRate
+ : parentSnapshot.frameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index d979c46..453b51e 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -24,6 +24,8 @@
#include <private/android_filesystem_config.h>
#include <sys/types.h>
+#include <scheduler/Fps.h>
+
#include "Layer.h"
#include "LayerCreationArgs.h"
#include "LayerLog.h"
@@ -51,7 +53,6 @@
name(args.name + "#" + std::to_string(args.sequence)),
canBeRoot(args.addToRoot),
layerCreationFlags(args.flags),
- textureName(args.textureName),
ownerUid(args.ownerUid),
ownerPid(args.ownerPid),
parentId(args.parentId),
@@ -123,6 +124,7 @@
dimmingEnabled = true;
defaultFrameRateCompatibility =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+ frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
dataspace = ui::Dataspace::V0_SRGB;
gameMode = gui::GameMode::Unsupported;
requestedFrameRate = {};
@@ -151,7 +153,7 @@
const bool hadSideStream = sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
- const bool hadBlur = hasBlur();
+ const bool hadSomethingToDraw = hasSomethingToDraw();
uint64_t clientChanges = what | layer_state_t::diff(clientState);
layer_state_t::merge(clientState);
what = clientChanges;
@@ -229,11 +231,10 @@
RequestedLayerState::Changes::VisibleRegion;
}
}
- if (what & (layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBlurRegionsChanged)) {
- if (hadBlur != hasBlur()) {
- changes |= RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::VisibleRegion;
- }
+
+ if (hadSomethingToDraw != hasSomethingToDraw()) {
+ changes |= RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion;
}
if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
changes |= RequestedLayerState::Changes::Hierarchy;
@@ -319,8 +320,14 @@
Layer::FrameRate::convertCompatibility(clientState.frameRateCompatibility);
const auto strategy = Layer::FrameRate::convertChangeFrameRateStrategy(
clientState.changeFrameRateStrategy);
- requestedFrameRate =
- Layer::FrameRate(Fps::fromValue(clientState.frameRate), compatibility, strategy);
+ requestedFrameRate.vote =
+ Layer::FrameRate::FrameRateVote(Fps::fromValue(clientState.frameRate),
+ compatibility, strategy);
+ changes |= RequestedLayerState::Changes::FrameRate;
+ }
+ if (clientState.what & layer_state_t::eFrameRateCategoryChanged) {
+ const auto category = Layer::FrameRate::convertCategory(clientState.frameRateCategory);
+ requestedFrameRate.category = category;
changes |= RequestedLayerState::Changes::FrameRate;
}
}
@@ -580,6 +587,12 @@
return externalTexture && externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
}
+bool RequestedLayerState::hasSomethingToDraw() const {
+ return externalTexture != nullptr || sidebandStream != nullptr || shadowRadius > 0.f ||
+ backgroundBlurRadius > 0 || blurRegions.size() > 0 ||
+ (color.r >= 0.0_hf && color.g >= 0.0_hf && color.b >= 0.0_hf);
+}
+
void RequestedLayerState::clearChanges() {
what = 0;
changes.clear();
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0309302..09f33de 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -87,6 +87,7 @@
bool backpressureEnabled() const;
bool isSimpleBufferUpdate(const layer_state_t&) const;
bool isProtected() const;
+ bool hasSomethingToDraw() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
@@ -95,7 +96,6 @@
const std::string name;
bool canBeRoot = false;
const uint32_t layerCreationFlags;
- const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
// If created from a system process, the value can be passed in.
const gui::Uid ownerUid;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 0d3c6eb..d3d9509 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -22,6 +22,7 @@
#include <cutils/trace.h>
#include <utils/Log.h>
#include <utils/Trace.h>
+#include "FrontEnd/LayerLog.h"
#include "TransactionHandler.h"
@@ -87,8 +88,8 @@
}
auto it = mPendingTransactionQueues.find(flushState.queueWithUnsignaledBuffer);
- LOG_ALWAYS_FATAL_IF(it == mPendingTransactionQueues.end(),
- "Could not find queue with unsignaled buffer!");
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mPendingTransactionQueues.end(),
+ "Could not find queue with unsignaled buffer!");
auto& queue = it->second;
popTransactionFromPending(transactions, flushState, queue);
@@ -188,21 +189,36 @@
}
void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId,
- sp<ITransactionCompletedListener>& listener,
- const std::string& reason) {
- if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) !=
- mStalledTransactions.end()) {
- return;
- }
-
- mStalledTransactions.push_back(transactionId);
- listener->onTransactionQueueStalled(String8(reason.c_str()));
+ StalledTransactionInfo stalledTransactionInfo) {
+ std::lock_guard lock{mStalledMutex};
+ mStalledTransactions.emplace(transactionId, std::move(stalledTransactionInfo));
}
-void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
- auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id);
- if (it != mStalledTransactions.end()) {
- mStalledTransactions.erase(it);
+void TransactionHandler::removeFromStalledTransactions(uint64_t transactionId) {
+ std::lock_guard lock{mStalledMutex};
+ mStalledTransactions.erase(transactionId);
+}
+
+std::optional<TransactionHandler::StalledTransactionInfo>
+TransactionHandler::getStalledTransactionInfo(pid_t pid) {
+ std::lock_guard lock{mStalledMutex};
+ for (auto [_, stalledTransactionInfo] : mStalledTransactions) {
+ if (pid == stalledTransactionInfo.pid) {
+ return stalledTransactionInfo;
+ }
+ }
+ return std::nullopt;
+}
+
+void TransactionHandler::onLayerDestroyed(uint32_t layerId) {
+ std::lock_guard lock{mStalledMutex};
+ for (auto it = mStalledTransactions.begin(); it != mStalledTransactions.end();) {
+ if (it->second.layerId == layerId) {
+ it = mStalledTransactions.erase(it);
+ } else {
+ it++;
+ }
}
}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index 04183bc..00f6bce 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -18,6 +18,7 @@
#include <semaphore.h>
#include <cstdint>
+#include <optional>
#include <vector>
#include <LocklessQueue.h>
@@ -63,9 +64,18 @@
std::vector<TransactionState> flushTransactions();
void addTransactionReadyFilter(TransactionFilter&&);
void queueTransaction(TransactionState&&);
- void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&,
- const std::string& reason);
+
+ struct StalledTransactionInfo {
+ pid_t pid;
+ uint32_t layerId;
+ std::string layerName;
+ uint64_t bufferId;
+ uint64_t frameNumber;
+ };
+ void onTransactionQueueStalled(uint64_t transactionId, StalledTransactionInfo);
void removeFromStalledTransactions(uint64_t transactionId);
+ std::optional<StalledTransactionInfo> getStalledTransactionInfo(pid_t pid);
+ void onLayerDestroyed(uint32_t layerId);
private:
// For unit tests
@@ -81,7 +91,10 @@
LocklessQueue<TransactionState> mLocklessTransactionQueue;
std::atomic<size_t> mPendingTransactionCount = 0;
ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters;
- std::vector<uint64_t> mStalledTransactions;
+
+ std::mutex mStalledMutex;
+ std::unordered_map<uint64_t /* transactionId */, StalledTransactionInfo> mStalledTransactions
+ GUARDED_BY(mStalledMutex);
};
} // namespace surfaceflinger::frontend
} // namespace android
diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h
index e1449b6..e5cca8f 100644
--- a/services/surfaceflinger/FrontEnd/Update.h
+++ b/services/surfaceflinger/FrontEnd/Update.h
@@ -46,7 +46,7 @@
std::vector<LayerCreatedState> layerCreatedStates;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers;
std::vector<LayerCreationArgs> layerCreationArgs;
- std::vector<uint32_t> destroyedHandles;
+ std::vector<std::pair<uint32_t, std::string /* debugName */>> destroyedHandles;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp
index 9eefbe4..2788332 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.cpp
+++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp
@@ -18,14 +18,22 @@
#define LOG_TAG "HdrLayerInfoReporter"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
+#include <inttypes.h>
#include <utils/Trace.h>
#include "HdrLayerInfoReporter.h"
namespace android {
+using base::StringAppendF;
+
void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
ATRACE_CALL();
+ if (mHdrInfoHistory.size() == 0 || mHdrInfoHistory.back().info != info) {
+ mHdrInfoHistory.next() = EventHistoryEntry{info};
+ }
+
std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
{
std::scoped_lock lock(mMutex);
@@ -62,4 +70,15 @@
mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
}
+void HdrLayerInfoReporter::dump(std::string& result) const {
+ for (size_t i = 0; i < mHdrInfoHistory.size(); i++) {
+ const auto& event = mHdrInfoHistory[i];
+ const auto& info = event.info;
+ StringAppendF(&result,
+ "%" PRId64 ": numHdrLayers(%d), size(%dx%d), flags(%X), desiredRatio(%.2f)\n",
+ event.timestamp, info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
+ info.maxDesiredHdrSdrRatio);
+ }
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index bf7c775..614f33f 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -19,9 +19,11 @@
#include <android-base/thread_annotations.h>
#include <android/gui/IHdrLayerInfoListener.h>
#include <binder/IBinder.h>
+#include <utils/Timers.h>
#include <unordered_map>
+#include "Utils/RingBuffer.h"
#include "WpHash.h"
namespace android {
@@ -79,6 +81,8 @@
return !mListeners.empty();
}
+ void dump(std::string& result) const;
+
private:
mutable std::mutex mMutex;
@@ -88,6 +92,17 @@
};
std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+
+ struct EventHistoryEntry {
+ nsecs_t timestamp = -1;
+ HdrLayerInfo info;
+
+ EventHistoryEntry() {}
+
+ EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); }
+ };
+
+ utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory;
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index 2c0f518..186e878 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -42,28 +42,37 @@
}
sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
- ui::Transform::RotationFlags rotation) {
- SkMatrix canvasTransform = SkMatrix();
- const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
- switch (rotation) {
- case ui::Transform::ROT_90:
- canvasTransform.setTranslate(kBufferHeight, 0);
- canvasTransform.preRotate(90.f);
- return {kBufferHeight, kBufferWidth};
- case ui::Transform::ROT_270:
- canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
- return {kBufferHeight, kBufferWidth};
- default:
- return {kBufferWidth, kBufferHeight};
- }
- }();
+ ui::Transform::RotationFlags rotation,
+ sp<GraphicBuffer>& ringBuffer) {
+ const int32_t bufferWidth = kBufferWidth;
+ const int32_t bufferHeight = kBufferWidth;
const auto kUsageFlags = static_cast<uint64_t>(
GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
- sp<GraphicBuffer> buffer =
- sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
- static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
- 1u, kUsageFlags, "HdrSdrRatioOverlay");
+
+ // ring buffers here to do double-buffered rendering to avoid
+ // possible tearing and also to reduce memory take-up.
+ if (ringBuffer == nullptr) {
+ ringBuffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+ static_cast<uint32_t>(bufferHeight),
+ HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags,
+ "HdrSdrRatioOverlayBuffer");
+ }
+
+ auto& buffer = ringBuffer;
+
+ SkMatrix canvasTransform = SkMatrix();
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ canvasTransform.setTranslate(bufferHeight, 0);
+ canvasTransform.preRotate(90.f);
+ break;
+ case ui::Transform::ROT_270:
+ canvasTransform.setRotate(270.f, bufferWidth / 2.f, bufferWidth / 2.f);
+ break;
+ default:
+ break;
+ }
const status_t bufferStatus = buffer->initCheck();
LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
@@ -163,13 +172,13 @@
const SkColor color = colorBase.toSkColor();
- auto buffer = draw(currentHdrSdrRatio, color, transformHint);
+ auto buffer = draw(currentHdrSdrRatio, color, transformHint, mRingBuffer[mIndex]);
+ mIndex = (mIndex + 1) % 2;
return buffer;
}
void HdrSdrRatioOverlay::animate() {
if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
-
SurfaceComposerClient::Transaction()
.setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
.apply();
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 8a2586e..69f95ec 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -35,11 +35,15 @@
private:
float mCurrentHdrSdrRatio = 1.f;
- static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags);
+ static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags,
+ sp<GraphicBuffer>& ringBufer);
static void drawNumber(float number, int left, SkColor, SkCanvas&);
const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
+
+ size_t mIndex = 0;
+ std::array<sp<GraphicBuffer>, 2> mRingBuffer;
};
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1737bdf..50f24a7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -27,7 +27,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
-#include <android/native_window.h>
#include <binder/IPCThreadState.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
@@ -50,10 +49,10 @@
#include <stdlib.h>
#include <sys/types.h>
#include <system/graphics-base-v1.0.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Transform.h>
@@ -101,7 +100,7 @@
using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
const auto frameRateCompatibility = [frameRate] {
- switch (frameRate.type) {
+ switch (frameRate.vote.type) {
case Layer::FrameRateCompatibility::Default:
return FrameRateCompatibility::Default;
case Layer::FrameRateCompatibility::ExactOrMultiple:
@@ -112,7 +111,7 @@
}();
const auto seamlessness = [frameRate] {
- switch (frameRate.seamlessness) {
+ switch (frameRate.vote.seamlessness) {
case scheduler::Seamlessness::OnlySeamless:
return Seamlessness::ShouldBeSeamless;
case scheduler::Seamlessness::SeamedAndSeamless:
@@ -122,7 +121,7 @@
}
}();
- return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.vote.rate.getValue(),
.frameRateCompatibility = frameRateCompatibility,
.seamlessness = seamlessness};
}
@@ -149,7 +148,6 @@
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
- mTextureName(args.textureName),
mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
@@ -214,7 +212,6 @@
mSnapshot->sequence = sequence;
mSnapshot->name = getDebugName();
- mSnapshot->textureName = mTextureName;
mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
mSnapshot->parentTransform = {};
}
@@ -236,13 +233,6 @@
mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
mBufferInfo.mFence);
}
- if (!isClone()) {
- // The original layer and the clone layer share the same texture. Therefore, only one of
- // the layers, in this case the original layer, needs to handle the deletion. The original
- // layer and the clone should be removed at the same time so there shouldn't be any issue
- // with the clone layer trying to use the deleted texture.
- mFlinger->deleteTextureAsync(mTextureName);
- }
const int32_t layerId = getSequence();
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
@@ -835,12 +825,12 @@
mFlinger->mUpdateInputInfo = true;
}
- commitTransaction(mDrawingState);
+ commitTransaction();
return flags;
}
-void Layer::commitTransaction(State&) {
+void Layer::commitTransaction() {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) {
@@ -1264,8 +1254,7 @@
bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
// The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
const auto frameRate = [&] {
- if (mDrawingState.frameRate.rate.isValid() ||
- mDrawingState.frameRate.type == FrameRateCompatibility::NoVote) {
+ if (mDrawingState.frameRate.isValid()) {
return mDrawingState.frameRate;
}
@@ -1281,23 +1270,23 @@
child->propagateFrameRateForLayerTree(frameRate, transactionNeeded);
}
- // If we don't have a valid frame rate, but the children do, we set this
+ // If we don't have a valid frame rate specification, but the children do, we set this
// layer as NoVote to allow the children to control the refresh rate
- if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
- childrenHaveFrameRate) {
+ if (!frameRate.isValid() && childrenHaveFrameRate) {
*transactionNeeded |=
setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
}
- // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
+ // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
// the same reason we are allowing touch boost for those layers. See
// RefreshRateSelector::rankFrameRates for details.
const auto layerVotedWithDefaultCompatibility =
- frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Default;
- const auto layerVotedWithNoVote = frameRate.type == FrameRateCompatibility::NoVote;
+ frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote = frameRate.vote.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithCategory = frameRate.category != FrameRateCategory::Default;
const auto layerVotedWithExactCompatibility =
- frameRate.rate.isValid() && frameRate.type == FrameRateCompatibility::Exact;
- return layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Exact;
+ return layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory ||
layerVotedWithExactCompatibility || childrenHaveFrameRate;
}
@@ -1319,13 +1308,28 @@
}
}
-bool Layer::setFrameRate(FrameRate frameRate) {
- if (mDrawingState.frameRate == frameRate) {
+bool Layer::setFrameRate(FrameRate::FrameRateVote frameRateVote) {
+ if (mDrawingState.frameRate.vote == frameRateVote) {
return false;
}
mDrawingState.sequence++;
- mDrawingState.frameRate = frameRate;
+ mDrawingState.frameRate.vote = frameRateVote;
+ mDrawingState.modified = true;
+
+ updateTreeHasFrameRateVote();
+
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
+bool Layer::setFrameRateCategory(FrameRateCategory category) {
+ if (mDrawingState.frameRate.category == category) {
+ return false;
+ }
+
+ mDrawingState.sequence++;
+ mDrawingState.frameRate.category = category;
mDrawingState.modified = true;
updateTreeHasFrameRateVote();
@@ -1651,10 +1655,10 @@
StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
crop.bottom);
const auto frameRate = getFrameRateForLayerTree();
- if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
- ftl::enum_string(frameRate.type).c_str(),
- ftl::enum_string(frameRate.seamlessness).c_str());
+ if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
+ StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ ftl::enum_string(frameRate.vote.seamlessness).c_str());
} else {
result.append(41, ' ');
}
@@ -1686,10 +1690,10 @@
StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
crop.bottom);
const auto frameRate = snapshot.frameRate;
- if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
- ftl::enum_string(frameRate.type).c_str(),
- ftl::enum_string(frameRate.seamlessness).c_str());
+ if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
+ StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ ftl::enum_string(frameRate.vote.seamlessness).c_str());
} else {
result.append(41, ' ');
}
@@ -2398,11 +2402,7 @@
info.touchableRegion.clear();
}
- const Rect roundedFrameInDisplay = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
- info.frameLeft = roundedFrameInDisplay.left;
- info.frameTop = roundedFrameInDisplay.top;
- info.frameRight = roundedFrameInDisplay.right;
- info.frameBottom = roundedFrameInDisplay.bottom;
+ info.frame = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
ui::Transform inputToLayer;
inputToLayer.set(inputBounds.left, inputBounds.top);
@@ -2823,36 +2823,6 @@
layer->mDrawingParent = sp<Layer>::fromExisting(this);
}
-Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) {
- switch (compatibility) {
- case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
- return FrameRateCompatibility::Default;
- case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
- return FrameRateCompatibility::ExactOrMultiple;
- case ANATIVEWINDOW_FRAME_RATE_EXACT:
- return FrameRateCompatibility::Exact;
- case ANATIVEWINDOW_FRAME_RATE_MIN:
- return FrameRateCompatibility::Min;
- case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
- return FrameRateCompatibility::NoVote;
- default:
- LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
- return FrameRateCompatibility::Default;
- }
-}
-
-scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
- switch (strategy) {
- case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
- return Seamlessness::OnlySeamless;
- case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
- return Seamlessness::SeamedAndSeamless;
- default:
- LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
- return Seamlessness::Default;
- }
-}
-
bool Layer::isInternalDisplayOverlay() const {
const State& s(mDrawingState);
if (s.flags & layer_state_t::eLayerSkipScreenshot) {
@@ -3624,7 +3594,6 @@
sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
- args.textureName = mTextureName;
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
return layer;
@@ -4366,13 +4335,6 @@
mLastLatchTime = latchTime;
}
-// ---------------------------------------------------------------------------
-
-std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
- return stream << "{rate=" << rate.rate << " type=" << ftl::enum_string(rate.type)
- << " seamlessness=" << ftl::enum_string(rate.seamlessness) << '}';
-}
-
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 25a6845..1f2485f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -23,8 +23,6 @@
#include <gui/WindowInfo.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/vec4.h>
-#include <renderengine/Mesh.h>
-#include <renderengine/Texture.h>
#include <sys/types.h>
#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
@@ -427,7 +425,7 @@
bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
// from graphics API
- ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+ static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
void updateCloneBufferInfo();
uint64_t mPreviousFrameNumber = 0;
@@ -778,7 +776,8 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate);
+ bool setFrameRate(FrameRate::FrameRateVote);
+ bool setFrameRateCategory(FrameRateCategory);
virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
@@ -910,6 +909,7 @@
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
mTransformHint = transformHint;
}
+ void commitTransaction();
// Keeps track of the previously presented layer stacks. This is used to get
// the release fences from the correct displays when we release the last buffer
// from the layer.
@@ -930,7 +930,6 @@
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
- virtual void commitTransaction(State& stateToCommit);
void gatherBufferInfo();
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&);
@@ -1193,8 +1192,6 @@
void setTransformHintLegacy(ui::Transform::RotationFlags);
void resetDrawingStateBufferInfo();
- const uint32_t mTextureName;
-
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 5ae52ab..a0024d5 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -223,7 +223,6 @@
layerSettings.source.buffer.buffer = mSnapshot->externalTexture;
layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque;
layerSettings.source.buffer.fence = mSnapshot->acquireFence;
- layerSettings.source.buffer.textureName = mSnapshot->textureName;
layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha;
bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086;
bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 1c7581b..341f041 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -185,8 +185,8 @@
static_assert(std::is_same_v<U, int32_t>);
proto->set_layout_params_type(static_cast<U>(inputInfo.layoutParamsType));
- LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight,
- inputInfo.frameBottom},
+ LayerProtoHelper::writeToProto({inputInfo.frame.left, inputInfo.frame.top,
+ inputInfo.frame.right, inputInfo.frame.bottom},
[&]() { return proto->mutable_frame(); });
LayerProtoHelper::writeToProto(inputInfo.touchableRegion,
[&]() { return proto->mutable_touchable_region(); });
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 5d00a26..565a490 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -40,8 +40,9 @@
namespace {
bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
- // Layers with an explicit vote are always kept active
- if (info.getSetFrameRateVote().rate.isValid()) {
+ // Layers with an explicit frame rate or frame rate category are always kept active,
+ // but ignore NoVote/NoPreference.
+ if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) {
return true;
}
@@ -70,6 +71,7 @@
traceType(LayerHistory::LayerVoteType::ExplicitExact, fps);
traceType(LayerHistory::LayerVoteType::Min, 1);
traceType(LayerHistory::LayerVoteType::Max, 1);
+ traceType(LayerHistory::LayerVoteType::ExplicitCategory, 1);
ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
@@ -171,27 +173,32 @@
layerFocused ? "" : "not");
ATRACE_FORMAT("%s", info->getName().c_str());
- const auto vote = info->getRefreshRateVote(selector, now);
- // Skip NoVote layer as those don't have any requirements
- if (vote.type == LayerVoteType::NoVote) {
- continue;
- }
+ const auto votes = info->getRefreshRateVote(selector, now);
+ for (LayerInfo::LayerVote vote : votes) {
+ if (vote.isNoVote()) {
+ continue;
+ }
- // Compute the layer's position on the screen
- const Rect bounds = Rect(info->getBounds());
- const ui::Transform transform = info->getTransform();
- constexpr bool roundOutwards = true;
- Rect transformed = transform.transform(bounds, roundOutwards);
+ // Compute the layer's position on the screen
+ const Rect bounds = Rect(info->getBounds());
+ const ui::Transform transform = info->getTransform();
+ constexpr bool roundOutwards = true;
+ Rect transformed = transform.transform(bounds, roundOutwards);
- const float layerArea = transformed.getWidth() * transformed.getHeight();
- float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
- ATRACE_FORMAT_INSTANT("%s %s (%d%)", ftl::enum_string(vote.type).c_str(),
- to_string(vote.fps).c_str(), weight * 100);
- summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
- vote.seamlessness, weight, layerFocused});
+ const float layerArea = transformed.getWidth() * transformed.getHeight();
+ float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+ const std::string categoryString = vote.category == FrameRateCategory::Default
+ ? base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str())
+ : "";
+ ATRACE_FORMAT_INSTANT("%s %s %s (%d%)", ftl::enum_string(vote.type).c_str(),
+ to_string(vote.fps).c_str(), categoryString.c_str(),
+ weight * 100);
+ summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps,
+ vote.seamlessness, vote.category, weight, layerFocused});
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(*info, vote.type, vote.fps.getIntValue());
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, vote.type, vote.fps.getIntValue());
+ }
}
}
@@ -228,7 +235,7 @@
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
const auto voteType = [&]() {
- switch (frameRate.type) {
+ switch (frameRate.vote.type) {
case Layer::FrameRateCompatibility::Default:
return LayerVoteType::ExplicitDefault;
case Layer::FrameRateCompatibility::Min:
@@ -242,9 +249,10 @@
}
}();
- if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) {
+ if (frameRate.isValid()) {
const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.rate, frameRate.seamlessness});
+ info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
+ frameRate.category});
} else {
info->resetLayerVote();
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index bae3739..750803b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -26,10 +26,12 @@
#include <algorithm>
#include <utility>
+#include <android/native_window.h>
#include <cutils/compiler.h>
#include <cutils/trace.h>
#include <ftl/enum.h>
#include <gui/TraceUtils.h>
+#include <system/window.h>
#undef LOG_TAG
#define LOG_TAG "LayerInfo"
@@ -265,19 +267,34 @@
: std::nullopt;
}
-LayerInfo::LayerVote LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
- nsecs_t now) {
+LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelector& selector,
+ nsecs_t now) {
ATRACE_CALL();
+ LayerInfo::RefreshRateVotes votes;
+
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
- ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
- return mLayerVote;
+ if (mLayerVote.category != FrameRateCategory::Default) {
+ ALOGV("%s uses frame rate category: %d", mName.c_str(),
+ static_cast<int>(mLayerVote.category));
+ votes.push_back({LayerHistory::LayerVoteType::ExplicitCategory, mLayerVote.fps,
+ Seamlessness::Default, mLayerVote.category});
+ }
+
+ if (mLayerVote.fps.isValid() ||
+ mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) {
+ ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+ votes.push_back(mLayerVote);
+ }
+
+ return votes;
}
if (isAnimating(now)) {
ATRACE_FORMAT_INSTANT("animating");
ALOGV("%s is animating", mName.c_str());
mLastRefreshRate.animating = true;
- return {LayerHistory::LayerVoteType::Max, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+ return votes;
}
const LayerInfo::Frequent frequent = isFrequent(now);
@@ -288,7 +305,8 @@
mLastRefreshRate.infrequent = true;
// Infrequent layers vote for minimal refresh rate for
// battery saving purposes and also to prevent b/135718869.
- return {LayerHistory::LayerVoteType::Min, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Min, Fps()});
+ return votes;
}
if (frequent.clearHistory) {
@@ -298,11 +316,13 @@
auto refreshRate = calculateRefreshRateIfPossible(selector, now);
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str());
- return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+ votes.push_back({LayerHistory::LayerVoteType::Heuristic, refreshRate.value()});
+ return votes;
}
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, Fps()};
+ votes.push_back({LayerHistory::LayerVoteType::Max, Fps()});
+ return votes;
}
const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
@@ -391,6 +411,68 @@
return consistent;
}
+LayerInfo::FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
+ switch (compatibility) {
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
+ return FrameRateCompatibility::Default;
+ case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+ return FrameRateCompatibility::ExactOrMultiple;
+ case ANATIVEWINDOW_FRAME_RATE_EXACT:
+ return FrameRateCompatibility::Exact;
+ case ANATIVEWINDOW_FRAME_RATE_MIN:
+ return FrameRateCompatibility::Min;
+ case ANATIVEWINDOW_FRAME_RATE_NO_VOTE:
+ return FrameRateCompatibility::NoVote;
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
+ return FrameRateCompatibility::Default;
+ }
+}
+
+Seamlessness LayerInfo::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+ return Seamlessness::OnlySeamless;
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+ return Seamlessness::SeamedAndSeamless;
+ default:
+ LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+ return Seamlessness::Default;
+ }
+}
+
+FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) {
+ switch (category) {
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT:
+ return FrameRateCategory::Default;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE:
+ return FrameRateCategory::NoPreference;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_LOW:
+ return FrameRateCategory::Low;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL:
+ return FrameRateCategory::Normal;
+ case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH:
+ return FrameRateCategory::High;
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate category value %d", category);
+ return FrameRateCategory::Default;
+ }
+}
+
+bool LayerInfo::FrameRate::isNoVote() const {
+ return vote.type == FrameRateCompatibility::NoVote ||
+ category == FrameRateCategory::NoPreference;
+}
+
+bool LayerInfo::FrameRate::isValid() const {
+ return isNoVote() || vote.rate.isValid() || category != FrameRateCategory::Default;
+}
+
+std::ostream& operator<<(std::ostream& stream, const LayerInfo::FrameRate& rate) {
+ return stream << "{rate=" << rate.vote.rate << " type=" << ftl::enum_string(rate.vote.type)
+ << " seamlessness=" << ftl::enum_string(rate.vote.seamlessness) << '}';
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index c5a6057..7d2444c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -25,6 +25,7 @@
#include <ui/Transform.h>
#include <utils/Timers.h>
+#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
#include "LayerHistory.h"
@@ -67,8 +68,15 @@
LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
Fps fps;
Seamlessness seamlessness = Seamlessness::Default;
+ // Category is in effect if fps is not specified.
+ FrameRateCategory category = FrameRateCategory::Default;
+
+ // Returns true if the layer explicitly should contribute to frame rate scoring.
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(type, category); }
};
+ using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
+
// FrameRateCompatibility specifies how we should interpret the frame rate associated with
// the layer.
enum class FrameRateCompatibility {
@@ -87,24 +95,40 @@
ftl_last = NoVote
};
- // Encapsulates the frame rate and compatibility of the layer. This information will be used
+ // Encapsulates the frame rate specifications of the layer. This information will be used
// when the display refresh rate is determined.
struct FrameRate {
using Seamlessness = scheduler::Seamlessness;
- Fps rate;
- FrameRateCompatibility type = FrameRateCompatibility::Default;
- Seamlessness seamlessness = Seamlessness::Default;
+ // Information related to a specific desired frame rate vote.
+ struct FrameRateVote {
+ Fps rate;
+ FrameRateCompatibility type = FrameRateCompatibility::Default;
+ Seamlessness seamlessness = Seamlessness::Default;
+
+ bool operator==(const FrameRateVote& other) const {
+ return isApproxEqual(rate, other.rate) && type == other.type &&
+ seamlessness == other.seamlessness;
+ }
+
+ FrameRateVote() = default;
+
+ FrameRateVote(Fps rate, FrameRateCompatibility type,
+ Seamlessness seamlessness = Seamlessness::OnlySeamless)
+ : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+ } vote;
+
+ FrameRateCategory category = FrameRateCategory::Default;
FrameRate() = default;
FrameRate(Fps rate, FrameRateCompatibility type,
- Seamlessness seamlessness = Seamlessness::OnlySeamless)
- : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
+ Seamlessness seamlessness = Seamlessness::OnlySeamless,
+ FrameRateCategory category = FrameRateCategory::Default)
+ : vote(FrameRateVote(rate, type, seamlessness)), category(category) {}
bool operator==(const FrameRate& other) const {
- return isApproxEqual(rate, other.rate) && type == other.type &&
- seamlessness == other.seamlessness;
+ return vote == other.vote && category == other.category;
}
bool operator!=(const FrameRate& other) const { return !(*this == other); }
@@ -112,8 +136,22 @@
// Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
// Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+
+ // Convert an ANATIVEWINDOW_CHANGE_FRAME_RATE_* value to a scheduler::Seamlessness.
+ // Logs fatal if the compatibility value is invalid.
static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
+ // Convert an ANATIVEWINDOW_FRAME_RATE_CATEGORY_* value to a FrameRateCategory.
+ // Logs fatal if the compatibility value is invalid.
+ static FrameRateCategory convertCategory(int8_t category);
+
+ // True if the FrameRate has explicit frame rate specifications.
+ bool isValid() const;
+
+ // Returns true if the FrameRate explicitly instructs to not contribute to frame rate
+ // selection.
+ bool isNoVote() const;
+
private:
static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
if (!rate.isValid()) {
@@ -148,13 +186,15 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }
+ void resetLayerVote() {
+ mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default, FrameRateCategory::Default};
+ }
std::string getName() const { return mName; }
uid_t getOwnerUid() const { return mOwnerUid; }
- LayerVote getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
+ RefreshRateVotes getRefreshRateVote(const RefreshRateSelector&, nsecs_t now);
// Return the last updated time. If the present time is farther in the future than the
// updated time, the updated time is the present time.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index fb985f7..7e77bbe 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -148,8 +148,8 @@
} // namespace
auto RefreshRateSelector::createFrameRateModes(
- std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const
- -> std::vector<FrameRateMode> {
+ const Policy& policy, std::function<bool(const DisplayMode&)>&& filterModes,
+ const FpsRange& renderRange) const -> std::vector<FrameRateMode> {
struct Key {
Fps fps;
int32_t group;
@@ -202,11 +202,25 @@
ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(),
to_string(mode->getFps()).c_str());
} else {
- // We might need to update the map as we found a lower refresh rate
- if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) {
+ // If the primary physical range is a single rate, prefer to stay in that rate
+ // even if there is a lower physical refresh rate available. This would cause more
+ // cases to stay within the primary physical range
+ const Fps existingModeFps = existingIter->second->second->getFps();
+ const bool existingModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
+ policy.primaryRanges.physical.includes(existingModeFps);
+ const bool newModeIsPrimaryRange = policy.primaryRangeIsSingleRate() &&
+ policy.primaryRanges.physical.includes(mode->getFps());
+ if (newModeIsPrimaryRange == existingModeIsPrimaryRange) {
+ // We might need to update the map as we found a lower refresh rate
+ if (isStrictlyLess(mode->getFps(), existingModeFps)) {
+ existingIter->second = it;
+ ALOGV("%s: changing %s (%s) as we found a lower physical rate", __func__,
+ to_string(fps).c_str(), to_string(mode->getFps()).c_str());
+ }
+ } else if (newModeIsPrimaryRange) {
existingIter->second = it;
- ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(),
- to_string(mode->getFps()).c_str());
+ ALOGV("%s: changing %s (%s) to stay in the primary range", __func__,
+ to_string(fps).c_str(), to_string(mode->getFps()).c_str());
}
}
}
@@ -275,6 +289,25 @@
return {quotient, remainder};
}
+float RefreshRateSelector::calculateNonExactMatchingDefaultLayerScoreLocked(
+ nsecs_t displayPeriod, nsecs_t layerPeriod) const {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal period to render a frame.
+ // For example if layerPeriod is 20ms and displayPeriod is 16ms,
+ // then the actualLayerPeriod will be 32ms, because it is the
+ // smallest multiple of the display period which is >= layerPeriod.
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
+ }
+
+ // Because of the threshold we used above it's possible that score is slightly
+ // above 1.
+ return std::min(1.0f, static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+}
+
float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer,
Fps refreshRate) const {
constexpr float kScoreForFractionalPairs = .8f;
@@ -282,22 +315,7 @@
const auto displayPeriod = refreshRate.getPeriodNsecs();
const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
if (layer.vote == LayerVoteType::ExplicitDefault) {
- // Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal period to render a frame.
- // For example if layerPeriod is 20ms and displayPeriod is 16ms,
- // then the actualLayerPeriod will be 32ms, because it is the
- // smallest multiple of the display period which is >= layerPeriod.
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
-
- // Because of the threshold we used above it's possible that score is slightly
- // above 1.
- return std::min(1.0f,
- static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
+ return calculateNonExactMatchingDefaultLayerScoreLocked(displayPeriod, layerPeriod);
}
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
@@ -372,6 +390,22 @@
constexpr float kSeamedSwitchPenalty = 0.95f;
const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty;
+ if (layer.vote == LayerVoteType::ExplicitCategory) {
+ if (getFrameRateCategoryRange(layer.frameRateCategory).includes(refreshRate)) {
+ return 1.f;
+ }
+
+ FpsRange categoryRange = getFrameRateCategoryRange(layer.frameRateCategory);
+ using fps_approx_ops::operator<;
+ if (refreshRate < categoryRange.min) {
+ return calculateNonExactMatchingDefaultLayerScoreLocked(refreshRate.getPeriodNsecs(),
+ categoryRange.min
+ .getPeriodNsecs());
+ }
+ return calculateNonExactMatchingDefaultLayerScoreLocked(refreshRate.getPeriodNsecs(),
+ categoryRange.max.getPeriodNsecs());
+ }
+
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
return calculateDistanceScoreFromMax(refreshRate);
@@ -391,7 +425,8 @@
// If the layer frame rate is a divisor of the refresh rate it should score
// the highest score.
- if (getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
+ if (layer.desiredRefreshRate.isValid() &&
+ getFrameRateDivisor(refreshRate, layer.desiredRefreshRate) > 0) {
return 1.0f * seamlessness;
}
@@ -441,6 +476,7 @@
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
int explicitExact = 0;
+ int explicitCategoryVoteLayers = 0;
int seamedFocusedLayers = 0;
for (const auto& layer : layers) {
@@ -463,6 +499,9 @@
case LayerVoteType::ExplicitExact:
explicitExact++;
break;
+ case LayerVoteType::ExplicitCategory:
+ explicitCategoryVoteLayers++;
+ break;
case LayerVoteType::Heuristic:
break;
}
@@ -473,7 +512,8 @@
}
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
- explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+ explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0 ||
+ explicitCategoryVoteLayers > 0;
const Policy* policy = getCurrentPolicyLocked();
const auto& defaultMode = mDisplayModes.get(policy->defaultMode)->get();
@@ -500,10 +540,8 @@
// If the primary range consists of a single refresh rate then we can only
// move out the of range if layers explicitly request a different refresh
// rate.
- const bool primaryRangeIsSingleRate =
- isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max);
-
- if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+ if (!signals.touch && signals.idle &&
+ !(policy->primaryRangeIsSingleRate() && hasExplicitVoteLayers)) {
ALOGV("Idle");
const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending);
ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str());
@@ -536,10 +574,11 @@
}
for (const auto& layer : layers) {
- ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
- ftl::enum_string(layer.vote).c_str(), layer.weight,
- layer.desiredRefreshRate.getValue());
- if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
+ ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f, category %s) ",
+ layer.name.c_str(), ftl::enum_string(layer.vote).c_str(), layer.weight,
+ layer.desiredRefreshRate.getValue(),
+ ftl::enum_string(layer.frameRateCategory).c_str());
+ if (layer.isNoVote() || layer.vote == LayerVoteType::Min) {
continue;
}
@@ -577,8 +616,11 @@
continue;
}
- const bool inPrimaryRange = policy->primaryRanges.render.includes(fps);
- if ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+ const bool inPrimaryPhysicalRange =
+ policy->primaryRanges.physical.includes(modePtr->getFps());
+ const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps);
+ if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) ||
+ !inPrimaryRenderRange) &&
!(layer.focused &&
(layer.vote == LayerVoteType::ExplicitDefault ||
layer.vote == LayerVoteType::ExplicitExact))) {
@@ -607,6 +649,7 @@
case LayerVoteType::Max:
case LayerVoteType::ExplicitDefault:
case LayerVoteType::ExplicitExact:
+ case LayerVoteType::ExplicitCategory:
return false;
}
}(layer.vote);
@@ -689,7 +732,7 @@
return score.overallScore == 0;
});
- if (primaryRangeIsSingleRate) {
+ if (policy->primaryRangeIsSingleRate()) {
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (noLayerScore) {
@@ -722,7 +765,8 @@
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
- if (signals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
+ if (signals.touch && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 &&
+ touchBoostForExplicitExact &&
scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
ALOGV("Touch Boost");
ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
@@ -838,8 +882,10 @@
}
LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault &&
- layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
- layer->vote != LayerVoteType::ExplicitExact);
+ layer->vote != LayerVoteType::ExplicitExactOrMultiple &&
+ layer->vote != LayerVoteType::ExplicitExact &&
+ layer->vote != LayerVoteType::ExplicitCategory,
+ "Invalid layer vote type for frame rate overrides");
for (auto& [fps, score] : scoredFrameRates) {
constexpr bool isSeamlessSwitch = true;
const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch);
@@ -1236,14 +1282,14 @@
(supportsFrameRateOverride() || ranges.render.includes(mode.getFps()));
};
- auto frameRateModes = createFrameRateModes(filterModes, ranges.render);
+ auto frameRateModes = createFrameRateModes(*policy, filterModes, ranges.render);
if (frameRateModes.empty()) {
ALOGW("No matching frame rate modes for %s range. policy: %s", rangeName,
policy->toString().c_str());
// TODO(b/292105422): Ideally DisplayManager should not send render ranges smaller than
// the min supported. See b/292047939.
// For not we just ignore the render ranges.
- frameRateModes = createFrameRateModes(filterModes, {});
+ frameRateModes = createFrameRateModes(*policy, filterModes, {});
}
LOG_ALWAYS_FATAL_IF(frameRateModes.empty(),
"No matching frame rate modes for %s range even after ignoring the "
@@ -1380,6 +1426,27 @@
return mConfig.idleTimerTimeout;
}
+// TODO(b/293651105): Extract category FpsRange mapping to OEM-configurable config.
+FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) {
+ switch (category) {
+ case FrameRateCategory::High:
+ return FpsRange{90_Hz, 120_Hz};
+ case FrameRateCategory::Normal:
+ return FpsRange{60_Hz, 90_Hz};
+ case FrameRateCategory::Low:
+ return FpsRange{30_Hz, 60_Hz};
+ case FrameRateCategory::NoPreference:
+ case FrameRateCategory::Default:
+ LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
+ ftl::enum_string(category).c_str());
+ return FpsRange{0_Hz, 0_Hz};
+ default:
+ LOG_ALWAYS_FATAL("Invalid frame rate category for range: %s",
+ ftl::enum_string(category).c_str());
+ return FpsRange{0_Hz, 0_Hz};
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 7af8d03..73e1d38 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -101,6 +101,11 @@
}
bool operator!=(const Policy& other) const { return !(*this == other); }
+
+ bool primaryRangeIsSingleRate() const {
+ return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max);
+ }
+
std::string toString() const;
};
@@ -147,8 +152,9 @@
// ExactOrMultiple compatibility
ExplicitExact, // Specific refresh rate that was provided by the app with
// Exact compatibility
+ ExplicitCategory, // Specific frame rate category was provided by the app
- ftl_last = ExplicitExact
+ ftl_last = ExplicitCategory
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -164,6 +170,8 @@
Fps desiredRefreshRate;
// If a seamless mode switch is required.
Seamlessness seamlessness = Seamlessness::Default;
+ // Layer frame rate category. Superseded by desiredRefreshRate.
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
// would have on choosing the refresh rate.
float weight = 0.0f;
@@ -174,12 +182,20 @@
return name == other.name && vote == other.vote &&
isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
seamlessness == other.seamlessness && weight == other.weight &&
- focused == other.focused;
+ focused == other.focused && frameRateCategory == other.frameRateCategory;
}
bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+
+ bool isNoVote() const { return RefreshRateSelector::isNoVote(vote, frameRateCategory); }
};
+ // Returns true if the layer explicitly instructs to not contribute to refresh rate selection.
+ // In other words, true if the layer should be ignored.
+ static bool isNoVote(LayerVoteType vote, FrameRateCategory category) {
+ return vote == LayerVoteType::NoVote || category == FrameRateCategory::NoPreference;
+ }
+
// Global state describing signals that affect refresh rate choice.
struct GlobalSignals {
// Whether the user touched the screen recently. Used to apply touch boost.
@@ -344,6 +360,9 @@
Fps displayFrameRate, GlobalSignals) const
EXCLUDES(mLock);
+ // Gets the FpsRange that the FrameRateCategory represents.
+ static FpsRange getFrameRateCategoryRange(FrameRateCategory category);
+
std::optional<KernelIdleTimerController> kernelIdleTimerController() {
return mConfig.kernelIdleTimerController;
}
@@ -447,6 +466,11 @@
float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&, Fps refreshRate) const
REQUIRES(mLock);
+ // Calculates the score for non-exact matching layer that has LayerVoteType::ExplicitDefault.
+ float calculateNonExactMatchingDefaultLayerScoreLocked(nsecs_t displayPeriod,
+ nsecs_t layerPeriod) const
+ REQUIRES(mLock);
+
void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
REQUIRES(kMainThreadContext);
@@ -468,8 +492,8 @@
}
std::vector<FrameRateMode> createFrameRateModes(
- std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const
- REQUIRES(mLock);
+ const Policy&, std::function<bool(const DisplayMode&)>&& filterModes,
+ const FpsRange&) const REQUIRES(mLock);
// The display modes of the active display. The DisplayModeIterators below are pointers into
// this container, so must be invalidated whenever the DisplayModes change. The Policy below
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index d6329e2..19e951a 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
#include <scheduler/Time.h>
namespace android {
@@ -81,6 +82,17 @@
bool valid() const;
};
+// The frame rate category of a Layer.
+enum class FrameRateCategory {
+ Default,
+ NoPreference,
+ Low,
+ Normal,
+ High,
+
+ ftl_last = High
+};
+
static_assert(std::is_trivially_copyable_v<Fps>);
constexpr Fps operator""_Hz(unsigned long long frequency) {
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 0103843..ef9b457 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -45,10 +45,9 @@
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) {
std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated<
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
- const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine,
- args.renderArea,
- args.colorProfile,
- args.regionSampling);
+ const compositionengine::Output::ColorProfile&,
+ bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
+ args.dimInGammaSpaceForEnhancedScreenshots);
output->editState().isSecure = args.renderArea.isSecure();
output->setCompositionEnabled(true);
output->setLayerFilter({args.layerStack});
@@ -81,8 +80,11 @@
ScreenCaptureOutput::ScreenCaptureOutput(
const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling)
- : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling) {}
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots)
+ : mRenderArea(renderArea),
+ mColorProfile(colorProfile),
+ mRegionSampling(regionSampling),
+ mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {}
void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
auto& outputState = editState();
@@ -95,6 +97,14 @@
auto clientCompositionDisplay =
compositionengine::impl::Output::generateClientCompositionDisplaySettings();
clientCompositionDisplay.clip = mRenderArea.getSourceCrop();
+
+ auto renderIntent = static_cast<ui::RenderIntent>(clientCompositionDisplay.renderIntent);
+ if (mDimInGammaSpaceForEnhancedScreenshots && renderIntent != ui::RenderIntent::COLORIMETRIC &&
+ renderIntent != ui::RenderIntent::TONE_MAP_COLORIMETRIC) {
+ clientCompositionDisplay.dimmingStage =
+ aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
+ }
+
return clientCompositionDisplay;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 159c2bf..fc095de 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -37,6 +37,7 @@
float targetBrightness;
bool regionSampling;
bool treat170mAsSrgb;
+ bool dimInGammaSpaceForEnhancedScreenshots;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
@@ -47,7 +48,7 @@
public:
ScreenCaptureOutput(const RenderArea& renderArea,
const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling);
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots);
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -63,6 +64,7 @@
const RenderArea& mRenderArea;
const compositionengine::Output::ColorProfile& mColorProfile;
const bool mRegionSampling;
+ const bool mDimInGammaSpaceForEnhancedScreenshots;
};
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e459f03..cb8edc3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -81,7 +81,6 @@
#include <scheduler/FrameTargeter.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
-#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -89,6 +88,7 @@
#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <ui/GraphicBufferAllocator.h>
+#include <ui/HdrRenderTypeUtils.h>
#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/StaticDisplayInfo.h>
@@ -134,6 +134,7 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerLog.h"
#include "FrontEnd/LayerSnapshot.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
@@ -452,6 +453,9 @@
property_get("debug.sf.treat_170m_as_sRGB", value, "0");
mTreat170mAsSrgb = atoi(value);
+ property_get("debug.sf.dim_in_gamma_in_enhanced_screenshots", value, 0);
+ mDimInGammaSpaceForEnhancedScreenshots = atoi(value);
+
mIgnoreHwcPhysicalDisplayOrientation =
base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false);
@@ -484,7 +488,7 @@
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
mLayerLifecycleManagerEnabled =
- base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false);
+ base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
}
@@ -644,14 +648,6 @@
return getPhysicalDisplayTokenLocked(displayId);
}
-status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const {
- if (!outGetColorManagement) {
- return BAD_VALUE;
- }
- *outGetColorManagement = useColorManagement;
- return NO_ERROR;
-}
-
HWComposer& SurfaceFlinger::getHwComposer() const {
return mCompositionEngine->getHwComposer();
}
@@ -738,52 +734,12 @@
}));
}
-uint32_t SurfaceFlinger::getNewTexture() {
- {
- std::lock_guard lock(mTexturePoolMutex);
- if (!mTexturePool.empty()) {
- uint32_t name = mTexturePool.back();
- mTexturePool.pop_back();
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- return name;
- }
-
- // The pool was too small, so increase it for the future
- ++mTexturePoolSize;
- }
-
- // The pool was empty, so we need to get a new texture name directly using a
- // blocking call to the main thread
- auto genTextures = [this] {
- uint32_t name = 0;
- getRenderEngine().genTextures(1, &name);
- return name;
- };
- if (std::this_thread::get_id() == mMainThreadId) {
- return genTextures();
- } else {
- return mScheduler->schedule(genTextures).get();
- }
-}
-
-void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
- std::lock_guard lock(mTexturePoolMutex);
- // We don't change the pool size, so the fix-up logic in postComposition will decide whether
- // to actually delete this or not based on mTexturePoolSize
- mTexturePool.push_back(texture);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
-}
-
static std::optional<renderengine::RenderEngine::RenderEngineType>
chooseRenderEngineTypeViaSysProp() {
char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded");
- if (strcmp(prop, "gles") == 0) {
- return renderengine::RenderEngine::RenderEngineType::GLES;
- } else if (strcmp(prop, "threaded") == 0) {
- return renderengine::RenderEngine::RenderEngineType::THREADED;
- } else if (strcmp(prop, "skiagl") == 0) {
+ if (strcmp(prop, "skiagl") == 0) {
return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
} else if (strcmp(prop, "skiaglthreaded") == 0) {
return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
@@ -812,7 +768,6 @@
auto builder = renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
.setSupportsBackgroundBlur(mSupportsBlur)
@@ -909,30 +864,32 @@
ALOGE("Run StartPropertySetThread failed!");
}
- if (mTransactionTracing) {
- TransactionTraceWriter::getInstance().setWriterFunction([&](const std::string& prefix,
- bool overwrite) {
- auto writeFn = [&]() {
- const std::string filename =
- TransactionTracing::DIR_NAME + prefix + TransactionTracing::FILE_NAME;
- if (overwrite && access(filename.c_str(), F_OK) == 0) {
- ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
- return;
- }
- mTransactionTracing->flush();
- mTransactionTracing->writeToFile(filename);
- };
- if (std::this_thread::get_id() == mMainThreadId) {
- writeFn();
- } else {
- mScheduler->schedule(writeFn).get();
- }
- });
- }
-
+ initTransactionTraceWriter();
ALOGV("Done initializing");
}
+void SurfaceFlinger::initTransactionTraceWriter() {
+ if (!mTransactionTracing) {
+ return;
+ }
+ TransactionTraceWriter::getInstance().setWriterFunction(
+ [&](const std::string& filename, bool overwrite) {
+ auto writeFn = [&]() {
+ if (!overwrite && access(filename.c_str(), F_OK) == 0) {
+ ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
+ return;
+ }
+ mTransactionTracing->flush();
+ mTransactionTracing->writeToFile(filename);
+ };
+ if (std::this_thread::get_id() == mMainThreadId) {
+ writeFn();
+ } else {
+ mScheduler->schedule(writeFn).get();
+ }
+ });
+}
+
void SurfaceFlinger::readPersistentProperties() {
Mutex::Autolock _l(mStateLock);
@@ -2322,7 +2279,7 @@
mUpdateInputInfo = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy |
- Changes::Visibility)) {
+ Changes::Visibility | Changes::Geometry)) {
mVisibleRegionsDirty = true;
}
if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) {
@@ -2336,7 +2293,8 @@
bool newDataLatched = false;
if (!mLegacyFrontEndEnabled) {
ATRACE_NAME("DisplayCallbackAndStatsUpdates");
- applyTransactions(update.transactions, vsyncId);
+ mustComposite |= applyTransactions(update.transactions, vsyncId);
+ traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
const nsecs_t latchTime = systemTime();
bool unused = false;
@@ -2350,11 +2308,22 @@
mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
}
const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
- if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) continue;
auto it = mLegacyLayers.find(layer->id);
LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
layer->getDebugString().c_str());
+ if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
+ if (!it->second->hasBuffer()) {
+ // The last latch time is used to classify a missed frame as buffer stuffing
+ // instead of a missed frame. This is used to identify scenarios where we
+ // could not latch a buffer or apply a transaction due to backpressure.
+ // We only update the latch time for buffer less layers here, the latch time
+ // is updated for buffer layers when the buffer is latched.
+ it->second->updateLastLatchTime(latchTime);
+ }
+ continue;
+ }
+
const bool bgColorOnly =
!layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
if (willReleaseBufferOnLatch) {
@@ -2362,6 +2331,7 @@
}
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
newDataLatched = true;
+
mLayersWithQueuedFrames.emplace(it->second);
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
@@ -2620,9 +2590,7 @@
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
- refreshArgs.outputColorSetting = useColorManagement
- ? mDisplayColorSetting
- : compositionengine::OutputColorSetting::kUnmanaged;
+ refreshArgs.outputColorSetting = mDisplayColorSetting;
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
@@ -2664,6 +2632,28 @@
constexpr bool kCursorOnly = false;
const auto layers = moveSnapshotsToCompositionArgs(refreshArgs, kCursorOnly);
+ if (mLayerLifecycleManagerEnabled && !mVisibleRegionsDirty) {
+ for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
+ auto compositionDisplay = display->getCompositionDisplay();
+ if (!compositionDisplay->getState().isEnabled) continue;
+ for (auto outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) {
+ if (outputLayer->getLayerFE().getCompositionState() == nullptr) {
+ // This is unexpected but instead of crashing, capture traces to disk
+ // and recover gracefully by forcing CE to rebuild layer stack.
+ ALOGE("Output layer %s for display %s %" PRIu64 " has a null "
+ "snapshot. Forcing mVisibleRegionsDirty",
+ outputLayer->getLayerFE().getDebugName(),
+ compositionDisplay->getName().c_str(), compositionDisplay->getId().value);
+
+ TransactionTraceWriter::getInstance().invoke(__func__, /* overwrite= */ false);
+ mVisibleRegionsDirty = true;
+ refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
+ refreshArgs.updatingGeometryThisFrame = mVisibleRegionsDirty;
+ }
+ }
+ }
+ }
+
mCompositionEngine->present(refreshArgs);
moveSnapshotsFromCompositionArgs(refreshArgs, layers);
@@ -2800,7 +2790,14 @@
return false;
}
}
- if (isHdrDataspace(snapshot.dataspace)) {
+ // RANGE_EXTENDED layer may identify themselves as being "HDR"
+ // via a desired hdr/sdr ratio
+ auto pixelFormat = snapshot.buffer
+ ? std::make_optional(static_cast<ui::PixelFormat>(snapshot.buffer->getPixelFormat()))
+ : std::nullopt;
+
+ if (getHdrRenderType(snapshot.dataspace, pixelFormat, snapshot.desiredHdrSdrRatio) !=
+ HdrRenderType::SDR) {
return true;
}
// If the layer is not allowed to be dimmed, treat it as HDR. WindowManager may disable
@@ -2810,12 +2807,6 @@
if (!snapshot.dimmingEnabled) {
return true;
}
- // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
- if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
- (int32_t)Dataspace::RANGE_EXTENDED &&
- snapshot.desiredHdrSdrRatio > 1.01f) {
- return true;
- }
return false;
}
@@ -3073,23 +3064,6 @@
// Cleanup any outstanding resources due to rendering a prior frame.
getRenderEngine().cleanupPostRender();
- {
- std::lock_guard lock(mTexturePoolMutex);
- if (mTexturePool.size() < mTexturePoolSize) {
- const size_t refillCount = mTexturePoolSize - mTexturePool.size();
- const size_t offset = mTexturePool.size();
- mTexturePool.resize(mTexturePoolSize);
- getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- } else if (mTexturePool.size() > mTexturePoolSize) {
- const size_t deleteCount = mTexturePool.size() - mTexturePoolSize;
- const size_t offset = mTexturePoolSize;
- getRenderEngine().deleteTextures(deleteCount, mTexturePool.data() + offset);
- mTexturePool.resize(mTexturePoolSize);
- ATRACE_INT("TexturePoolSize", mTexturePool.size());
- }
- }
-
if (mNumTrustedPresentationListeners > 0) {
// We avoid any reverse traversal upwards so this shouldn't be too expensive
traverseLegacyLayers([&](Layer* layer) {
@@ -3393,18 +3367,16 @@
creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
- if (useColorManagement) {
- mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::snapshotRef)
- .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
- for (const auto mode : snapshot.colorModes()) {
- creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
- creationArgs.hwcColorModes
- .emplace(mode,
- getHwComposer().getRenderIntents(physical->id, mode));
- }
- }));
- }
+ mPhysicalDisplays.get(physical->id)
+ .transform(&PhysicalDisplay::snapshotRef)
+ .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
+ for (const auto mode : snapshot.colorModes()) {
+ creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
+ creationArgs.hwcColorModes
+ .emplace(mode,
+ getHwComposer().getRenderIntents(physical->id, mode));
+ }
+ }));
}
if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) {
@@ -3589,8 +3561,6 @@
// Recreate the DisplayDevice if the surface or sequence ID changed.
if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
- getRenderEngine().cleanFramebufferCache();
-
if (const auto display = getDisplayDeviceLocked(displayToken)) {
display->disconnect();
if (display->isVirtual()) {
@@ -4461,9 +4431,13 @@
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
mTransactionHandler
- .onTransactionQueueStalled(transaction.id, listener,
- "Buffer processing hung up due to stuck "
- "fence. Indicates GPU hang");
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->getOwnerPid(),
+ .layerId = static_cast<uint32_t>(
+ layer->getSequence()),
+ .layerName = layer->getDebugName(),
+ .bufferId = s.bufferData->getId(),
+ .frameNumber = s.bufferData->frameNumber});
}
ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -4556,9 +4530,12 @@
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
mTransactionHandler
- .onTransactionQueueStalled(transaction.id, listener,
- "Buffer processing hung up due to stuck "
- "fence. Indicates GPU hang");
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->ownerPid.val(),
+ .layerId = layer->id,
+ .layerName = layer->name,
+ .bufferId = s.bufferData->getId(),
+ .frameNumber = s.bufferData->frameNumber});
}
ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
@@ -5196,9 +5173,15 @@
const auto strategy =
Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
- if (layer->setFrameRate(
- Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) {
- flags |= eTraversalNeeded;
+ if (layer->setFrameRate(Layer::FrameRate::FrameRateVote(Fps::fromValue(s.frameRate),
+ compatibility, strategy))) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eFrameRateCategoryChanged) {
+ const FrameRateCategory category = Layer::FrameRate::convertCategory(s.frameRateCategory);
+ if (layer->setFrameRateCategory(category)) {
+ flags |= eTraversalNeeded;
}
}
if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -5386,6 +5369,14 @@
if (what & layer_state_t::eSidebandStreamChanged) {
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eDataspaceChanged) {
+ if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
+ }
+ if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
+ if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eBufferChanged) {
std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
@@ -5560,7 +5551,6 @@
status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- args.textureName = getNewTexture();
*outLayer = getFactory().createBufferStateLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
@@ -5582,9 +5572,11 @@
void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
{
std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- mDestroyedHandles.emplace_back(layerId);
+ mDestroyedHandles.emplace_back(layerId, layer->getDebugName());
}
+ mTransactionHandler.onLayerDestroyed(layerId);
+
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
layer->onHandleDestroyed();
@@ -5712,18 +5704,22 @@
// Turn off the display
if (displayId == mActiveDisplayId) {
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
- strerror(errno));
- }
- if (setSchedAttr(false) != NO_ERROR) {
- ALOGW("Failed set uclamp.min after powering off active display: %s",
- strerror(errno));
- }
+ if (const auto display = getActivatableDisplay()) {
+ onActiveDisplayChangedLocked(activeDisplay.get(), *display);
+ } else {
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Failed to set SCHED_OTHER after powering off active display: %s",
+ strerror(errno));
+ }
+ if (setSchedAttr(false) != NO_ERROR) {
+ ALOGW("Failed set uclamp.min after powering off active display: %s",
+ strerror(errno));
+ }
- if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(displayId, true);
- mScheduler->enableSyntheticVsync();
+ if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ mScheduler->enableSyntheticVsync();
+ }
}
}
@@ -5801,6 +5797,7 @@
{"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
{"--events"s, dumper(&SurfaceFlinger::dumpEvents)},
{"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)},
+ {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
{"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)},
{"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
{"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
@@ -5926,7 +5923,7 @@
const auto name = String8(args[1]);
mCurrentState.traverseInZOrder([&](Layer* layer) {
- if (layer->getName() == name.string()) {
+ if (layer->getName() == name.c_str()) {
layer->dumpFrameStats(result);
}
});
@@ -5937,7 +5934,7 @@
const auto name = clearAll ? String8() : String8(args[1]);
mCurrentState.traverse([&](Layer* layer) {
- if (clearAll || layer->getName() == name.string()) {
+ if (clearAll || layer->getName() == name.c_str()) {
layer->clearFrameStats();
}
});
@@ -6089,7 +6086,6 @@
void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor);
- StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
StringAppendF(&result, "DisplayColorSetting: %s\n",
decodeDisplayColorSetting(mDisplayColorSetting).c_str());
@@ -6110,6 +6106,14 @@
result.append("\n");
}
+void SurfaceFlinger::dumpHdrInfo(std::string& result) const {
+ for (const auto& [displayId, listener] : mHdrLayerInfoListeners) {
+ StringAppendF(&result, "HDR events for display %" PRIu64 "\n", displayId.value);
+ listener->dump(result);
+ result.append("\n");
+ }
+}
+
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
std::unordered_set<uint64_t> stackIdsToSkip;
@@ -6274,6 +6278,8 @@
result.append("\nWide-Color information:\n");
dumpWideColorInfo(result);
+ dumpHdrInfo(result);
+
colorizer.bold(result);
result.append("Sync configuration: ");
colorizer.reset(result);
@@ -6582,21 +6588,14 @@
case 1001:
return NAME_NOT_FOUND;
case 1002: // Toggle flashing on surface damage.
- if (const int delay = data.readInt32(); delay > 0) {
- mDebugFlashDelay = delay;
- } else {
- mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
- }
- scheduleRepaint();
+ sfdo_setDebugFlash(data.readInt32());
return NO_ERROR;
case 1004: // Force composite ahead of next VSYNC.
case 1006:
- scheduleComposite(FrameHint::kActive);
+ sfdo_scheduleComposite();
return NO_ERROR;
case 1005: { // Force commit ahead of next VSYNC.
- Mutex::Autolock lock(mStateLock);
- setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded |
- eTraversalNeeded);
+ sfdo_scheduleCommit();
return NO_ERROR;
}
case 1007: // Unused.
@@ -6764,8 +6763,6 @@
DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
switch (setting) {
case DisplayColorSetting::kManaged:
- reply->writeBool(useColorManagement);
- break;
case DisplayColorSetting::kUnmanaged:
reply->writeBool(true);
break;
@@ -6798,7 +6795,8 @@
}
// Is device color managed?
case 1030: {
- reply->writeBool(useColorManagement);
+ // ColorDisplayManager stil calls this
+ reply->writeBool(true);
return NO_ERROR;
}
// Override default composition data space
@@ -6842,19 +6840,13 @@
return NO_ERROR;
}
case 1034: {
- auto future = mScheduler->schedule(
- [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- switch (n = data.readInt32()) {
- case 0:
- case 1:
- enableRefreshRateOverlay(static_cast<bool>(n));
- break;
- default:
- reply->writeBool(isRefreshRateOverlayEnabled());
- }
- });
-
- future.wait();
+ n = data.readInt32();
+ if (n == 0 || n == 1) {
+ sfdo_enableRefreshRateOverlay(static_cast<bool>(n));
+ } else {
+ Mutex::Autolock lock(mStateLock);
+ reply->writeBool(isRefreshRateOverlayEnabled());
+ }
return NO_ERROR;
}
case 1035: {
@@ -7844,7 +7836,9 @@
.displayBrightnessNits = displayBrightnessNits,
.targetBrightness = targetBrightness,
.regionSampling = regionSampling,
- .treat170mAsSrgb = mTreat170mAsSrgb});
+ .treat170mAsSrgb = mTreat170mAsSrgb,
+ .dimInGammaSpaceForEnhancedScreenshots =
+ mDimInGammaSpaceForEnhancedScreenshots});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -7869,7 +7863,7 @@
const bool renderEngineIsThreaded = [&]() {
using Type = renderengine::RenderEngine::RenderEngineType;
const auto type = mRenderEngine->getRenderEngineType();
- return type == Type::THREADED || type == Type::SKIA_GL_THREADED;
+ return type == Type::SKIA_GL_THREADED;
}();
auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share()
: ftl::yield(present()).share();
@@ -8342,6 +8336,20 @@
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize());
}
+sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const {
+ if (mPhysicalDisplays.size() == 1) return nullptr;
+
+ // TODO(b/255635821): Choose the pacesetter display, considering both internal and external
+ // displays. For now, pick the other internal display, assuming a dual-display foldable.
+ return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) {
+ const auto idOpt = PhysicalDisplayId::tryCast(display.getId());
+ return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() &&
+ mPhysicalDisplays.get(*idOpt)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+ });
+}
+
void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
const DisplayDevice& activeDisplay) {
ATRACE_CALL();
@@ -8390,6 +8398,12 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getStalledTransactionInfo(
+ int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) {
+ result = mTransactionHandler.getStalledTransactionInfo(pid);
+ return NO_ERROR;
+}
+
std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData(
BufferData& bufferData, const char* layerName, uint64_t transactionId) {
if (bufferData.buffer &&
@@ -8457,7 +8471,13 @@
// Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
// accidentally.
sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
- rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(rootMirrorLayer);
+ bool ret = rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
+ if (idx >= 0 && ret) {
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(rootMirrorLayer);
+ }
+
for (const auto& layer : mDrawingState.layersSortedByZ) {
if (layer->getLayerStack() != mirrorDisplay.layerStack ||
layer->isInternalDisplayOverlay()) {
@@ -8760,6 +8780,33 @@
std::move(hwcDump), &displays);
}
+// sfdo functions
+
+void SurfaceFlinger::sfdo_enableRefreshRateOverlay(bool active) {
+ auto future = mScheduler->schedule(
+ [&]() FTL_FAKE_GUARD(mStateLock)
+ FTL_FAKE_GUARD(kMainThreadContext) { enableRefreshRateOverlay(active); });
+ future.wait();
+}
+
+void SurfaceFlinger::sfdo_setDebugFlash(int delay) {
+ if (delay > 0) {
+ mDebugFlashDelay = delay;
+ } else {
+ mDebugFlashDelay = mDebugFlashDelay ? 0 : 1;
+ }
+ scheduleRepaint();
+}
+
+void SurfaceFlinger::sfdo_scheduleComposite() {
+ scheduleComposite(SurfaceFlinger::FrameHint::kActive);
+}
+
+void SurfaceFlinger::sfdo_scheduleCommit() {
+ Mutex::Autolock lock(mStateLock);
+ setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded | eTraversalNeeded);
+}
+
// gui::ISurfaceComposer
binder::Status SurfaceComposerAIDL::bootFinished() {
@@ -9180,11 +9227,6 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getColorManagement(bool* outGetColorManagement) {
- status_t status = mFlinger->getColorManagement(outGetColorManagement);
- return binderStatusFromStatusT(status);
-}
-
binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) {
ui::Dataspace dataspace;
ui::PixelFormat pixelFormat;
@@ -9458,6 +9500,26 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::enableRefreshRateOverlay(bool active) {
+ mFlinger->sfdo_enableRefreshRateOverlay(active);
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::setDebugFlash(int delay) {
+ mFlinger->sfdo_setDebugFlash(delay);
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::scheduleComposite() {
+ mFlinger->sfdo_scheduleComposite();
+ return binder::Status::ok();
+}
+
+binder::Status SurfaceComposerAIDL::scheduleCommit() {
+ mFlinger->sfdo_scheduleCommit();
+ return binder::Status::ok();
+}
+
binder::Status SurfaceComposerAIDL::getGpuContextPriority(int32_t* outPriority) {
*outPriority = mFlinger->getGpuContextPriority();
return binder::Status::ok();
@@ -9499,6 +9561,28 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getStalledTransactionInfo(
+ int pid, std::optional<gui::StalledTransactionInfo>* outInfo) {
+ const int callingPid = IPCThreadState::self()->getCallingPid();
+ const int callingUid = IPCThreadState::self()->getCallingUid();
+ if (!checkPermission(sAccessSurfaceFlinger, callingPid, callingUid)) {
+ return binderStatusFromStatusT(PERMISSION_DENIED);
+ }
+
+ std::optional<TransactionHandler::StalledTransactionInfo> stalledTransactionInfo;
+ status_t status = mFlinger->getStalledTransactionInfo(pid, stalledTransactionInfo);
+ if (stalledTransactionInfo) {
+ gui::StalledTransactionInfo result;
+ result.layerName = String16{stalledTransactionInfo->layerName.c_str()},
+ result.bufferId = stalledTransactionInfo->bufferId,
+ result.frameNumber = stalledTransactionInfo->frameNumber,
+ outInfo->emplace(std::move(result));
+ } else {
+ outInfo->reset();
+ }
+ return binderStatusFromStatusT(status);
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d9c1101..6ff9fd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -236,9 +236,6 @@
static uint32_t maxGraphicsWidth;
static uint32_t maxGraphicsHeight;
- // Indicate if device wants color management on its display.
- static const constexpr bool useColorManagement = true;
-
static bool useContextPriority;
// The data space and pixel format that SurfaceFlinger expects hardware composer
@@ -280,13 +277,6 @@
// The CompositionEngine encapsulates all composition related interfaces and actions.
compositionengine::CompositionEngine& getCompositionEngine() const;
- // Obtains a name from the texture pool, or, if the pool is empty, posts a
- // synchronous message to the main thread to obtain one on the fly
- uint32_t getNewTexture();
-
- // utility function to delete a texture on the main thread
- void deleteTextureAsync(uint32_t texture);
-
renderengine::RenderEngine& getRenderEngine() const;
void onLayerFirstRef(Layer*);
@@ -327,6 +317,11 @@
// on this behavior to increase contrast for some media sources.
bool mTreat170mAsSrgb = false;
+ // If true, then screenshots with an enhanced render intent will dim in gamma space.
+ // The purpose is to ensure that screenshots appear correct during system animations for devices
+ // that require that dimming must occur in gamma space.
+ bool mDimInGammaSpaceForEnhancedScreenshots = false;
+
// Allows to ignore physical orientation provided through hwc API in favour of
// 'ro.surface_flinger.primary_display_orientation'.
// TODO(b/246793311): Clean up a temporary property
@@ -567,7 +562,6 @@
const std::vector<ui::Hdr>& hdrTypes);
status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
- status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
ui::Dataspace* outWideColorGamutDataspace,
ui::PixelFormat* outWideColorGamutPixelFormat) const;
@@ -622,6 +616,9 @@
status_t removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) const;
+ status_t getStalledTransactionInfo(
+ int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result);
+
// Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
@@ -941,7 +938,8 @@
template <typename Predicate>
sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) {
const auto it = std::find_if(mDisplays.begin(), mDisplays.end(),
- [&](const auto& pair) { return p(*pair.second); });
+ [&](const auto& pair)
+ REQUIRES(mStateLock) { return p(*pair.second); });
return it == mDisplays.end() ? nullptr : it->second;
}
@@ -1054,6 +1052,9 @@
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
+ // Returns a display other than `mActiveDisplayId` that can be activated, if any.
+ sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext);
+
void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr,
const DisplayDevice& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
@@ -1085,6 +1086,7 @@
void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
+ void dumpHdrInfo(std::string& result) const REQUIRES(mStateLock);
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
@@ -1132,7 +1134,7 @@
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
-
+ void initTransactionTraceWriter();
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
pid_t mPid;
@@ -1271,13 +1273,6 @@
TransactionCallbackInvoker mTransactionCallbackInvoker;
- // We maintain a pool of pre-generated texture names to hand out to avoid
- // layer creation needing to run on the main thread (which it would
- // otherwise need to do to access RenderEngine).
- std::mutex mTexturePoolMutex;
- uint32_t mTexturePoolSize = 0;
- std::vector<uint32_t> mTexturePool;
-
std::atomic<size_t> mNumLayers = 0;
// to linkToDeath
@@ -1430,7 +1425,7 @@
frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}};
frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
- std::vector<uint32_t> mDestroyedHandles;
+ std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles;
std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
std::vector<LayerCreationArgs> mNewLayerArgs;
// These classes do not store any client state but help with managing transaction callbacks
@@ -1447,6 +1442,11 @@
// Mirroring
// Map of displayid to mirrorRoot
ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug;
+
+ void sfdo_enableRefreshRateOverlay(bool active);
+ void sfdo_setDebugFlash(int delay);
+ void sfdo_scheduleComposite();
+ void sfdo_scheduleCommit();
};
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
@@ -1508,7 +1508,6 @@
const std::vector<int32_t>& hdrTypes) override;
binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
- binder::Status getColorManagement(bool* outGetColorManagement) override;
binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
binder::Status getDisplayedContentSamplingAttributes(
const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override;
@@ -1554,12 +1553,18 @@
const sp<IBinder>& displayToken,
std::optional<gui::DisplayDecorationSupport>* outSupport) override;
binder::Status setOverrideFrameRate(int32_t uid, float frameRate) override;
+ binder::Status enableRefreshRateOverlay(bool active) override;
+ binder::Status setDebugFlash(int delay) override;
+ binder::Status scheduleComposite() override;
+ binder::Status scheduleCommit() override;
binder::Status getGpuContextPriority(int32_t* outPriority) override;
binder::Status getMaxAcquiredBufferCount(int32_t* buffers) override;
binder::Status addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener,
gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
+ binder::Status getStalledTransactionInfo(int pid,
+ std::optional<gui::StalledTransactionInfo>* outInfo);
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index ecdeabe..b92d50b 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -27,12 +27,13 @@
#include <utils/Trace.h>
#include "LayerTracing.h"
-#include "RingBuffer.h"
+#include "TransactionRingBuffer.h"
namespace android {
LayerTracing::LayerTracing()
- : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {}
+ : mBuffer(std::make_unique<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {
+}
LayerTracing::~LayerTracing() = default;
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 40b0fbe..7c0d23d 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -30,7 +30,7 @@
namespace android {
template <typename FileProto, typename EntryProto>
-class RingBuffer;
+class TransactionRingBuffer;
class SurfaceFlinger;
@@ -71,7 +71,7 @@
uint32_t mFlags = TRACE_INPUT;
mutable std::mutex mTraceLock;
bool mEnabled GUARDED_BY(mTraceLock) = false;
- std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
+ std::unique_ptr<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
GUARDED_BY(mTraceLock);
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024;
};
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
similarity index 99%
rename from services/surfaceflinger/Tracing/RingBuffer.h
rename to services/surfaceflinger/Tracing/TransactionRingBuffer.h
index b41c65b..e6f85ca 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h
@@ -32,7 +32,7 @@
class SurfaceFlinger;
template <typename FileProto, typename EntryProto>
-class RingBuffer {
+class TransactionRingBuffer {
public:
size_t size() const { return mSizeInBytes; }
size_t used() const { return mUsedInBytes; }
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 7e330b9..bc69191 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -111,7 +111,7 @@
update.createdLayers = std::move(newUpdate.layerCreationArgs);
newUpdate.layerCreationArgs.clear();
update.destroyedLayerHandles.reserve(newUpdate.destroyedHandles.size());
- for (uint32_t handle : newUpdate.destroyedHandles) {
+ for (auto& [handle, _] : newUpdate.destroyedHandles) {
update.destroyedLayerHandles.push_back(handle);
}
mPendingUpdates.emplace_back(update);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
index a59dc6e..422b5f3 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.h
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -30,8 +30,8 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/Update.h"
#include "LocklessStack.h"
-#include "RingBuffer.h"
#include "TransactionProtoParser.h"
+#include "TransactionRingBuffer.h"
using namespace android::surfaceflinger;
@@ -73,15 +73,19 @@
static constexpr auto TRACING_VERSION = 1;
private:
+ friend class TransactionTraceWriter;
friend class TransactionTracingTest;
friend class SurfaceFlinger;
static constexpr auto DIR_NAME = "/data/misc/wmtrace/";
static constexpr auto FILE_NAME = "transactions_trace.winscope";
static constexpr auto FILE_PATH = "/data/misc/wmtrace/transactions_trace.winscope";
+ static std::string getFilePath(const std::string& prefix) {
+ return DIR_NAME + prefix + FILE_NAME;
+ }
mutable std::mutex mTraceLock;
- RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
+ TransactionRingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer
GUARDED_BY(mTraceLock);
size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
@@ -138,10 +142,16 @@
public:
void setWriterFunction(
- std::function<void(const std::string& prefix, bool overwrite)> function) {
+ std::function<void(const std::string& filename, bool overwrite)> function) {
mWriterFunction = std::move(function);
}
- void invoke(const std::string& prefix, bool overwrite) { mWriterFunction(prefix, overwrite); }
+ void invoke(const std::string& prefix, bool overwrite) {
+ mWriterFunction(TransactionTracing::getFilePath(prefix), overwrite);
+ }
+ /* pass in a complete file path for testing */
+ void invokeForTest(const std::string& filename, bool overwrite) {
+ mWriterFunction(filename, overwrite);
+ }
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 519ef44..321b8ba 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -109,11 +109,11 @@
ALOGV(" destroyedHandles=%d", entry.destroyed_layers(j));
}
- std::vector<uint32_t> destroyedHandles;
+ std::vector<std::pair<uint32_t, std::string>> destroyedHandles;
destroyedHandles.reserve((size_t)entry.destroyed_layer_handles_size());
for (int j = 0; j < entry.destroyed_layer_handles_size(); j++) {
ALOGV(" destroyedHandles=%d", entry.destroyed_layer_handles(j));
- destroyedHandles.push_back(entry.destroyed_layer_handles(j));
+ destroyedHandles.push_back({entry.destroyed_layer_handles(j), ""});
}
bool displayChanged = entry.displays_changed();
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
new file mode 100644
index 0000000..198e7b2
--- /dev/null
+++ b/services/surfaceflinger/Utils/RingBuffer.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 <stddef.h>
+#include <array>
+
+namespace android::utils {
+
+template <class T, size_t SIZE>
+class RingBuffer {
+ RingBuffer(const RingBuffer&) = delete;
+ void operator=(const RingBuffer&) = delete;
+
+public:
+ RingBuffer() = default;
+ ~RingBuffer() = default;
+
+ constexpr size_t capacity() const { return SIZE; }
+
+ size_t size() const { return mCount; }
+
+ T& next() {
+ mHead = static_cast<size_t>(mHead + 1) % SIZE;
+ if (mCount < SIZE) {
+ mCount++;
+ }
+ return mBuffer[static_cast<size_t>(mHead)];
+ }
+
+ T& front() { return (*this)[0]; }
+
+ T& back() { return (*this)[size() - 1]; }
+
+ T& operator[](size_t index) {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ const T& operator[](size_t index) const {
+ return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
+ }
+
+ void clear() {
+ mCount = 0;
+ mHead = -1;
+ }
+
+private:
+ std::array<T, SIZE> mBuffer;
+ int mHead = -1;
+ size_t mCount = 0;
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index b2d4131..ed8bb7f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -144,9 +144,6 @@
mFlinger->scheduleRepaint();
mFlinger->scheduleSample();
- uint32_t texture = mFlinger->getNewTexture();
- mFlinger->deleteTextureAsync(texture);
-
sp<IBinder> handle = defaultServiceManager()->checkService(
String16(mFdp.ConsumeRandomLengthString().c_str()));
LayerHandle::getLayer(handle);
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index ca1af6e..8a050fd 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -295,7 +295,7 @@
// MessageQueue overrides:
void scheduleFrame() override {}
- void postMessage(sp<MessageHandler>&&) override {}
+ void postMessage(sp<MessageHandler>&& handler) override { handler->handleMessage(Message()); }
};
} // namespace scheduler
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 52aa502..92ebb8d 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -120,7 +120,7 @@
const auto canvasSize = 256;
sp<SurfaceControl> leftLayer = createColorLayer("Left", Color::BLUE);
- sp<SurfaceControl> rightLayer = createColorLayer("Right", Color::GREEN);
+ sp<SurfaceControl> rightLayer = createColorLayer("Right", Color::RED);
sp<SurfaceControl> blurLayer;
const auto leftRect = Rect(0, 0, canvasSize / 2, canvasSize);
const auto rightRect = Rect(canvasSize / 2, 0, canvasSize, canvasSize);
@@ -140,12 +140,12 @@
{
auto shot = screenshot();
shot->expectColor(leftRect, Color::BLUE);
- shot->expectColor(rightRect, Color::GREEN);
+ shot->expectColor(rightRect, Color::RED);
}
ASSERT_NO_FATAL_FAILURE(blurLayer = createColorLayer("BackgroundBlur", Color::TRANSPARENT));
- const auto blurRadius = canvasSize / 2;
+ const auto blurRadius = canvasSize / 4;
asTransaction([&](Transaction& t) {
t.setLayer(blurLayer, mLayerZBase + 3);
t.reparent(blurLayer, mParentLayer);
@@ -159,21 +159,26 @@
auto shot = screenshot();
const auto stepSize = 1;
- const auto blurAreaOffset = blurRadius * 0.7f;
- const auto blurAreaStartX = canvasSize / 2 - blurRadius + blurAreaOffset;
- const auto blurAreaEndX = canvasSize / 2 + blurRadius - blurAreaOffset;
+ const auto expectedBlurAreaSize = blurRadius * 1.5f;
+ const auto blurAreaStartX = canvasSize / 2 - expectedBlurAreaSize / 2;
+ const auto blurAreaEndX = canvasSize / 2 + expectedBlurAreaSize / 2;
+ // testAreaEndY is needed because the setBackgroundBlurRadius API blurs everything behind
+ // the surface, which means it samples pixels from outside the canvasSize and we get some
+ // unexpected colors in the screenshot.
+ const auto testAreaEndY = canvasSize - blurRadius * 2;
+
Color previousColor;
Color currentColor;
- for (int y = 0; y < canvasSize; y++) {
+ for (int y = 0; y < testAreaEndY; y++) {
shot->checkPixel(0, y, /* r = */ 0, /* g = */ 0, /* b = */ 255);
previousColor = shot->getPixelColor(0, y);
for (int x = blurAreaStartX; x < blurAreaEndX; x += stepSize) {
currentColor = shot->getPixelColor(x, y);
- ASSERT_GT(currentColor.g, previousColor.g);
+ ASSERT_GT(currentColor.r, previousColor.r);
ASSERT_LT(currentColor.b, previousColor.b);
- ASSERT_EQ(0, currentColor.r);
+ ASSERT_EQ(0, currentColor.g);
}
- shot->checkPixel(canvasSize - 1, y, 0, 255, 0);
+ shot->checkPixel(canvasSize - 1, y, 255, 0, 0);
}
}
}
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index b8068f7..2b1834d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1479,15 +1479,11 @@
matrix[2][2] = 0.11;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrix);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
@@ -1537,15 +1533,11 @@
matrix[2][2] = 0.11;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrix);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
@@ -1608,16 +1600,12 @@
matrixParent[2][2] = 0.10;
// degamma before applying the matrix
- if (mColorManagementUsed) {
- ColorTransformHelper::DegammaColor(expected);
- }
+ ColorTransformHelper::DegammaColor(expected);
ColorTransformHelper::applyMatrix(expected, matrixChild);
ColorTransformHelper::applyMatrix(expected, matrixParent);
- if (mColorManagementUsed) {
- ColorTransformHelper::GammaColor(expected);
- }
+ ColorTransformHelper::GammaColor(expected);
const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
uint8_t(expected.b * 255), 255};
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index badd5be..2bdb8a4 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,9 +47,6 @@
ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
- binder::Status status = sf->getColorManagement(&mColorManagementUsed);
- ASSERT_NO_FATAL_FAILURE(gui::aidl_utils::statusTFromBinderStatus(status));
-
mCaptureArgs.displayToken = mDisplay;
}
@@ -282,7 +279,6 @@
const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
sp<SurfaceControl> mBlackBgSurface;
- bool mColorManagementUsed;
DisplayCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index fa5fa95..7d8796f 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -66,6 +66,7 @@
// option to false temporarily.
address: true,
},
+ static_libs: ["libc++fs"],
srcs: [
":libsurfaceflinger_mock_sources",
":libsurfaceflinger_sources",
@@ -128,6 +129,7 @@
"TransactionFrameTracerTest.cpp",
"TransactionProtoParserTest.cpp",
"TransactionSurfaceFrameTest.cpp",
+ "TransactionTraceWriterTest.cpp",
"TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
"StrongTypingTest.cpp",
@@ -148,6 +150,7 @@
defaults: [
"android.hardware.graphics.common-ndk_static",
"android.hardware.graphics.composer3-ndk_static",
+ "android.hardware.power-ndk_static",
"librenderengine_deps",
],
static_libs: [
@@ -161,7 +164,6 @@
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V4-ndk",
"libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 14fa492..8e13c0d 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -31,8 +31,6 @@
#include <gui/LayerMetadata.h>
#include <log/log.h>
#include <renderengine/mock/FakeExternalTexture.h>
-#include <renderengine/mock/Framebuffer.h>
-#include <renderengine/mock/Image.h>
#include <renderengine/mock/RenderEngine.h>
#include <system/window.h>
#include <utils/String8.h>
@@ -330,7 +328,7 @@
.WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>&,
const std::shared_ptr<renderengine::ExternalTexture>&,
- const bool, base::unique_fd&&) -> std::future<FenceResult> {
+ base::unique_fd&&) -> std::future<FenceResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -381,7 +379,7 @@
.WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>&,
const std::shared_ptr<renderengine::ExternalTexture>&,
- const bool, base::unique_fd&&) -> std::future<FenceResult> {
+ base::unique_fd&&) -> std::future<FenceResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -580,7 +578,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> std::future<FenceResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -599,7 +597,6 @@
const renderengine::LayerSettings layer = layerSettings.back();
EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
- EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
EXPECT_EQ(false, layer.source.buffer.isOpaque);
EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x);
@@ -630,7 +627,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> std::future<FenceResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -712,7 +709,7 @@
EXPECT_CALL(*test->mRenderEngine, drawLayers)
.WillOnce([&](const renderengine::DisplaySettings& displaySettings,
const std::vector<renderengine::LayerSettings>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> std::future<FenceResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
@@ -875,15 +872,11 @@
using FlingerLayerType = sp<Layer>;
static FlingerLayerType createLayer(CompositionTest* test) {
- test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID);
-
- FlingerLayerType layer =
- Base::template createLayerWithFactory<Layer>(test, [test]() {
- LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::LAYER_FLAGS, LayerMetadata());
- args.textureName = test->mFlinger.mutableTexturePool().back();
- return sp<Layer>::make(args);
- });
+ FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() {
+ LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
+ LayerProperties::LAYER_FLAGS, LayerMetadata());
+ return sp<Layer>::make(args);
+ });
LayerProperties::setupLayerState(test, layer);
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index 60ad7a3..2d87ddd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -78,7 +78,7 @@
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
// Setting another mode should be cached but return None
@@ -86,7 +86,7 @@
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
}
@@ -105,7 +105,7 @@
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -136,7 +136,7 @@
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{90_Hz, kMode90}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
hal::VsyncPeriodChangeConstraints constraints{
@@ -154,7 +154,7 @@
mDisplay->setDesiredActiveMode(
{scheduler::FrameRateMode{120_Hz, kMode120}, Event::None}));
ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode());
- EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt);
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt);
EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event);
EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt);
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
index 1c9aee7..d30d5b8 100644
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
@@ -99,8 +99,7 @@
}
void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
+ layer->commitTransaction();
}
namespace {
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 1380555..ff644ba 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -170,7 +170,7 @@
mLifecycleManager.applyTransactions(transactions);
}
- void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({{id, "test"}}); }
void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
@@ -335,6 +335,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateCategoryChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRateCategory = frameRateCategory;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setRoundedCorners(uint32_t id, float radius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -361,6 +372,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDataspace(uint32_t id, ui::Dataspace dataspace) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.dataspace = dataspace;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 85d86a7..51b5b05 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -437,6 +437,117 @@
EXPECT_EQ(0, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, oneLayerExplicitCategory) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
+ Seamlessness::OnlySeamless, FrameRateCategory::High)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
+// the category is NoPreference.
+TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
+ Seamlessness::OnlySeamless,
+ FrameRateCategory::NoPreference)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) {
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
+ Seamlessness::OnlySeamless, FrameRateCategory::High)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There are 2 LayerRequirement's due to the frame rate category.
+ ASSERT_EQ(2, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the layer's category specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // Second LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[1].frameRateCategory);
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(2, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
TEST_F(LayerHistoryTest, multipleLayers) {
auto layer1 = createLayer("A");
auto layer2 = createLayer("B");
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 5c2d2e1..e0133d6 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -24,13 +24,23 @@
#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockSchedulerCallback.h"
namespace android::scheduler {
+using android::mock::createDisplayMode;
+
class LayerInfoTest : public testing::Test {
protected:
using FrameTimeData = LayerInfo::FrameTimeData;
+ static constexpr Fps LO_FPS = 30_Hz;
+ static constexpr Fps HI_FPS = 90_Hz;
+
+ LayerInfoTest() { mFlinger.resetScheduler(mScheduler); }
+
void setFrameTimes(const std::deque<FrameTimeData>& frameTimes) {
layerInfo.mFrameTimes = frameTimes;
}
@@ -43,6 +53,16 @@
auto calculateAverageFrameTime() { return layerInfo.calculateAverageFrameTime(); }
LayerInfo layerInfo{"TestLayerInfo", 0, LayerHistory::LayerVoteType::Heuristic};
+
+ std::shared_ptr<RefreshRateSelector> mSelector =
+ std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+ LO_FPS),
+ createDisplayMode(DisplayModeId(1),
+ HI_FPS)),
+ DisplayModeId(0));
+ mock::SchedulerCallback mSchedulerCallback;
+ TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+ TestableSurfaceFlinger mFlinger;
};
namespace {
@@ -171,5 +191,63 @@
ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
}
+TEST_F(LayerInfoTest, getRefreshRateVote_explicitVote) {
+ LayerInfo::LayerVote vote = {.type = LayerHistory::LayerVoteType::ExplicitDefault,
+ .fps = 20_Hz};
+ layerInfo.setLayerVote(vote);
+
+ auto actualVotes =
+ layerInfo.getRefreshRateVote(*mScheduler->refreshRateSelector(), systemTime());
+ ASSERT_EQ(actualVotes.size(), 1u);
+ ASSERT_EQ(actualVotes[0].type, vote.type);
+ ASSERT_EQ(actualVotes[0].fps, vote.fps);
+ ASSERT_EQ(actualVotes[0].seamlessness, vote.seamlessness);
+ ASSERT_EQ(actualVotes[0].category, vote.category);
+}
+
+TEST_F(LayerInfoTest, getRefreshRateVote_explicitVoteWithCategory) {
+ LayerInfo::LayerVote vote = {.type = LayerHistory::LayerVoteType::ExplicitDefault,
+ .fps = 20_Hz,
+ .category = FrameRateCategory::High};
+ layerInfo.setLayerVote(vote);
+
+ auto actualVotes =
+ layerInfo.getRefreshRateVote(*mScheduler->refreshRateSelector(), systemTime());
+ ASSERT_EQ(actualVotes.size(), 2u);
+ ASSERT_EQ(actualVotes[0].type, LayerHistory::LayerVoteType::ExplicitCategory);
+ ASSERT_EQ(actualVotes[0].category, vote.category);
+ ASSERT_EQ(actualVotes[1].type, vote.type);
+ ASSERT_EQ(actualVotes[1].fps, vote.fps);
+ ASSERT_EQ(actualVotes[1].seamlessness, vote.seamlessness);
+ ASSERT_EQ(actualVotes[1].category, vote.category);
+}
+
+TEST_F(LayerInfoTest, getRefreshRateVote_explicitCategory) {
+ // When a layer only has a category set, the LayerVoteType should be the LayerInfo's default.
+ // The most common case should be Heuristic.
+ LayerInfo::LayerVote vote = {.type = LayerHistory::LayerVoteType::ExplicitDefault,
+ .category = FrameRateCategory::High};
+ layerInfo.setLayerVote(vote);
+
+ auto actualVotes =
+ layerInfo.getRefreshRateVote(*mScheduler->refreshRateSelector(), systemTime());
+ ASSERT_EQ(actualVotes.size(), 1u);
+ ASSERT_EQ(actualVotes[0].type, LayerHistory::LayerVoteType::ExplicitCategory);
+ ASSERT_EQ(actualVotes[0].category, vote.category);
+}
+
+TEST_F(LayerInfoTest, getRefreshRateVote_noData) {
+ LayerInfo::LayerVote vote = {
+ .type = LayerHistory::LayerVoteType::Heuristic,
+ };
+ layerInfo.setLayerVote(vote);
+
+ auto actualVotes =
+ layerInfo.getRefreshRateVote(*mScheduler->refreshRateSelector(), systemTime());
+ ASSERT_EQ(actualVotes.size(), 1u);
+ ASSERT_EQ(actualVotes[0].type, LayerHistory::LayerVoteType::Max);
+ ASSERT_EQ(actualVotes[0].fps, vote.fps);
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index 97ef5a2..d65277a 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -84,7 +84,7 @@
layers.emplace_back(rootLayer(2));
layers.emplace_back(rootLayer(3));
lifecycleManager.addLayers(std::move(layers));
- lifecycleManager.onHandlesDestroyed({1, 2, 3});
+ lifecycleManager.onHandlesDestroyed({{1, "1"}, {2, "2"}, {3, "3"}});
EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
lifecycleManager.commitChanges();
EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
@@ -133,7 +133,7 @@
layers.emplace_back(rootLayer(1));
layers.emplace_back(rootLayer(2));
lifecycleManager.addLayers(std::move(layers));
- lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.onHandlesDestroyed({{1, "1"}});
lifecycleManager.commitChanges();
SCOPED_TRACE("layerWithoutHandleIsDestroyed");
@@ -149,7 +149,7 @@
layers.emplace_back(rootLayer(1));
layers.emplace_back(rootLayer(2));
lifecycleManager.addLayers(std::move(layers));
- lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.onHandlesDestroyed({{1, "1"}});
lifecycleManager.commitChanges();
listener->expectLayersAdded({1, 2});
listener->expectLayersDestroyed({1});
@@ -173,7 +173,7 @@
listener->expectLayersAdded({});
listener->expectLayersDestroyed({});
- lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.onHandlesDestroyed({{3, "3"}});
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
listener->expectLayersDestroyed({3});
@@ -194,7 +194,7 @@
listener->expectLayersDestroyed({});
lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
- lifecycleManager.onHandlesDestroyed({3});
+ lifecycleManager.onHandlesDestroyed({{3, "3"}});
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
listener->expectLayersDestroyed({3});
@@ -215,7 +215,7 @@
listener->expectLayersDestroyed({});
lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID));
- lifecycleManager.onHandlesDestroyed({3, 4});
+ lifecycleManager.onHandlesDestroyed({{3, "3"}, {4, "4"}});
lifecycleManager.commitChanges();
listener->expectLayersAdded({});
listener->expectLayersDestroyed({3, 4});
@@ -376,7 +376,7 @@
transactions.back().states.front().layerId = 1;
transactions.emplace_back();
lifecycleManager.applyTransactions(transactions);
- lifecycleManager.onHandlesDestroyed({1});
+ lifecycleManager.onHandlesDestroyed({{1, "1"}});
ASSERT_EQ(lifecycleManager.getLayers().size(), 0u);
ASSERT_EQ(lifecycleManager.getDestroyedLayers().size(), 2u);
@@ -389,4 +389,52 @@
listener->expectLayersDestroyed({1, bgLayerId});
}
+TEST_F(LayerLifecycleManagerTest, blurSetsVisibilityChangeFlag) {
+ // clear default color on layer so we start with a layer that does not draw anything.
+ setColor(1, {-1.f, -1.f, -1.f});
+ mLifecycleManager.commitChanges();
+
+ // layer has something to draw
+ setBackgroundBlurRadius(1, 2);
+ EXPECT_TRUE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+
+ // layer still has something to draw, so visibility shouldn't change
+ setBackgroundBlurRadius(1, 3);
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+
+ // layer has nothing to draw
+ setBackgroundBlurRadius(1, 0);
+ EXPECT_TRUE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+}
+
+TEST_F(LayerLifecycleManagerTest, colorSetsVisibilityChangeFlag) {
+ // clear default color on layer so we start with a layer that does not draw anything.
+ setColor(1, {-1.f, -1.f, -1.f});
+ mLifecycleManager.commitChanges();
+
+ // layer has something to draw
+ setColor(1, {2.f, 3.f, 4.f});
+ EXPECT_TRUE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+
+ // layer still has something to draw, so visibility shouldn't change
+ setColor(1, {0.f, 0.f, 0.f});
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+
+ // layer has nothing to draw
+ setColor(1, {-1.f, -1.f, -1.f});
+ EXPECT_TRUE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 65bac00..80d913c 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -24,6 +24,7 @@
#include "FrontEnd/LayerSnapshotBuilder.h"
#include "Layer.h"
#include "LayerHierarchyTest.h"
+#include "ui/GraphicTypes.h"
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
@@ -313,13 +314,15 @@
mLifecycleManager.applyTransactions(transactions);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_EQ(getSnapshot(11)->frameRate.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(11)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::Exact);
- EXPECT_EQ(getSnapshot(111)->frameRate.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(111)->frameRate.type,
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Exact);
- EXPECT_EQ(getSnapshot(1)->frameRate.rate.getIntValue(), 0);
- EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
}
TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
@@ -348,16 +351,22 @@
}
TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) {
- static constexpr int blurRadius = 42;
- setBackgroundBlurRadius(1221, blurRadius);
+ int blurRadius = 42;
+ setBackgroundBlurRadius(1221, static_cast<uint32_t>(blurRadius));
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius);
+ blurRadius = 21;
+ setBackgroundBlurRadius(1221, static_cast<uint32_t>(blurRadius));
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius);
+
static constexpr float alpha = 0.5;
setAlpha(12, alpha);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius, blurRadius * alpha);
+ EXPECT_EQ(getSnapshot({.id = 1221})->backgroundBlurRadius,
+ static_cast<int>(static_cast<float>(blurRadius) * alpha));
}
// Display Mirroring Tests
@@ -524,21 +533,21 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
// verify parent is gets no vote
- EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes
- EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
- EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
@@ -548,24 +557,24 @@
std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
// verify parent is gets no vote
- EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::NoVote);
// verify layer and children get the requested votes
- EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
- EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
- EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
@@ -575,34 +584,146 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
// verify old parent has invalid framerate (default)
- EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 1})->frameRate.type,
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify new parent get no vote
- EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 2})->frameRate.type,
+ EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 2})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 2})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes (unchanged)
- EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 11})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
- EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 111})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
- EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.rate.isValid());
- EXPECT_EQ(getSnapshot({.id = 122})->frameRate.rate.getValue(), 244.f);
- EXPECT_EQ(getSnapshot({.id = 122})->frameRate.type,
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
scheduler::LayerInfo::FrameRateCompatibility::Default);
}
+TEST_F(LayerSnapshotTest, translateDataspace) {
+ setDataspace(1, ui::Dataspace::UNKNOWN);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->dataspace, ui::Dataspace::V0_SRGB);
+}
+
+// This test is similar to "frameRate" test case but checks that the setFrameRateCategory API
+// interaction also works correctly with the setFrameRate API within SF frontend.
+TEST_F(LayerSnapshotTest, frameRateWithCategory) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11 (frame rate set to 244.f)
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122 (frame rate category set to Normal)
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+ setFrameRate(11, 244.f, 0, 0);
+ setFrameRateCategory(122, 3 /* Normal */);
+
+ 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::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 11 and children 111 get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify parent 12 gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // verify layer 122 and children 1221 get the requested votes
+ EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
+ EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ // reparent and verify the child does NOT get the new parent's framerate because it already has
+ // the frame rate category specified.
+ // ROOT
+ // ├─1
+ // │ ├─11 (frame rate set to 244.f)
+ // │ │ ├─111
+ // │ │ └─122 (frame rate category set to Normal)
+ // │ │ └─1221
+ // │ ├─12
+ // │ │ └─121
+ // │ └─13
+ // └─2
+ reparentLayer(122, 11);
+
+ std::vector<uint32_t> expected = {1, 11, 111, 122, 1221, 12, 121, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+ // verify parent is gets no vote
+ EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+
+ // verify layer 11 and children 111 get the requested votes
+ EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+
+ // verify layer 122 and children 1221 get the requested category vote (unchanged from
+ // reparenting)
+ EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
+ EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
+ EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+ EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
+ EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Default);
+ EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
+ EXPECT_TRUE(getSnapshot({.id = 1221})->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 aaf55fb..50c1626 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 <scheduler/Fps.h>
#include <scheduler/FrameRateMode.h>
#include "DisplayHardware/HWC2.h"
#include "FpsOps.h"
@@ -1381,6 +1382,120 @@
EXPECT_FALSE(signals.touch);
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_30_60_90_120) {
+ auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.vote = LayerVoteType::ExplicitDefault, .weight = 1.f},
+ {.vote = LayerVoteType::ExplicitCategory,
+ .weight = 1.f}};
+ auto& lr = layers[0];
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::initializer_list<Case> testCases = {
+ // Cases that only have frame rate category requirements, but no desired frame rate.
+ // When frame rates get an equal score, the lower is chosen, unless there are Max votes.
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::Low, 30_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 60_Hz},
+
+ // Cases that have both desired frame rate and frame rate category requirements.
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 90_Hz},
+ {12_Hz, FrameRateCategory::Normal, 60_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 30_Hz},
+
+ // Cases that only have desired frame rate.
+ {30_Hz, FrameRateCategory::Default, 30_Hz},
+ };
+
+ for (auto testCase : testCases) {
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ lr.desiredRefreshRate = testCase.desiredFrameRate;
+
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate)
+ << ", category=" << ftl::enum_string(testCase.frameRateCategory);
+ lr.name = ss.str();
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ layers[1].frameRateCategory = testCase.frameRateCategory;
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getFps())
+ << "did not get expected frame rate for " << lr.name;
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.vote = LayerVoteType::ExplicitDefault, .weight = 1.f},
+ {.vote = LayerVoteType::ExplicitCategory,
+ .weight = 1.f}};
+ auto& lr = layers[0];
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ };
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::initializer_list<Case> testCases = {
+ // Cases that only have frame rate category requirements, but no desired frame rate.
+ // When frame rates get an equal score, the lower is chosen, unless there are Max votes.
+ {0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 60_Hz},
+ {0_Hz, FrameRateCategory::Low, 60_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 60_Hz},
+
+ // Cases that have both desired frame rate and frame rate category requirements.
+ {24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal, 60_Hz},
+ {30_Hz, FrameRateCategory::NoPreference, 60_Hz},
+
+ // Cases that only have desired frame rate.
+ {30_Hz, FrameRateCategory::Default, 60_Hz},
+ };
+
+ for (auto testCase : testCases) {
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ lr.desiredRefreshRate = testCase.desiredFrameRate;
+
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate)
+ << ", category=" << ftl::enum_string(testCase.frameRateCategory);
+ lr.name = ss.str();
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ layers[1].frameRateCategory = testCase.frameRateCategory;
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate, selector.getBestFrameRateMode(layers)->getFps())
+ << "did not get expected frame rate for " << lr.name;
+ }
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitDefault) {
auto selector = createSelector(kModes_60_90_72_120, kModeId60);
@@ -3125,5 +3240,69 @@
{DisplayModeId(kModeId60), kLowerThanMin, kLowerThanMin}));
}
+// b/296079213
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_120) {
+ auto selector = createSelector(kModes_60_120, kModeId120);
+
+ const FpsRange only120 = {120_Hz, 120_Hz};
+ const FpsRange allRange = {0_Hz, 120_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId120, {only120, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "30Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode120, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ const FpsRange only90 = {90_Hz, 90_Hz};
+ const FpsRange allRange = {0_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId90, {only90, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "30Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 30_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode90, 30_Hz,
+ selector.getBestScoredFrameRate(layers).frameRateMode);
+ }
+}
+
+TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90_NonDivisor) {
+ auto selector = createSelector(kModes_60_90, kModeId90);
+
+ const FpsRange only90 = {90_Hz, 90_Hz};
+ const FpsRange allRange = {0_Hz, 90_Hz};
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy(
+ {kModeId90, {only90, allRange}, {allRange, allRange}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "60Hz ExplicitExactOrMultiple";
+ layers[0].desiredRefreshRate = 60_Hz;
+ layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
+
+ EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 44ab569..608fa76 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -85,8 +85,7 @@
void SetFrameRateTest::commitTransaction() {
for (auto layer : mLayers) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
+ layer->commitTransaction();
}
}
@@ -98,7 +97,7 @@
const auto& layerFactory = GetParam();
auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- layer->setFrameRate(FRAME_RATE_VOTE1);
+ layer->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
}
@@ -115,13 +114,13 @@
addChild(parent, child1);
addChild(child1, child2);
- child2->setFrameRate(FRAME_RATE_VOTE1);
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -140,27 +139,27 @@
addChild(parent, child1);
addChild(child1, child2);
- child2->setFrameRate(FRAME_RATE_VOTE1);
- child1->setFrameRate(FRAME_RATE_VOTE2);
- parent->setFrameRate(FRAME_RATE_VOTE3);
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
+ child1->setFrameRate(FRAME_RATE_VOTE2.vote);
+ parent->setFrameRate(FRAME_RATE_VOTE3.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree());
- child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree());
- parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -179,13 +178,13 @@
addChild(parent, child1);
addChild(child1, child2);
- parent->setFrameRate(FRAME_RATE_VOTE1);
+ parent->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -204,27 +203,27 @@
addChild(parent, child1);
addChild(child1, child2);
- child2->setFrameRate(FRAME_RATE_VOTE1);
- child1->setFrameRate(FRAME_RATE_VOTE2);
- parent->setFrameRate(FRAME_RATE_VOTE3);
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
+ child1->setFrameRate(FRAME_RATE_VOTE2.vote);
+ parent->setFrameRate(FRAME_RATE_VOTE3.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -242,7 +241,7 @@
addChild(parent, child1);
- parent->setFrameRate(FRAME_RATE_VOTE1);
+ parent->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
@@ -254,7 +253,7 @@
EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -273,7 +272,7 @@
addChild(parent, child1);
addChild(child1, child2);
- parent->setFrameRate(FRAME_RATE_VOTE1);
+ parent->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
@@ -285,7 +284,7 @@
EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
- parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -306,14 +305,14 @@
addChild(child1, child2);
addChild(child1, child2_1);
- child2->setFrameRate(FRAME_RATE_VOTE1);
+ child2->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
- child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
@@ -376,7 +375,7 @@
auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
addChild(parent, child);
- parent->setFrameRate(FRAME_RATE_VOTE1);
+ parent->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
@@ -389,8 +388,8 @@
const auto summary = history.summarize(*selectorPtr, 0);
ASSERT_EQ(2u, summary.size());
- EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
- EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[1].desiredRefreshRate);
+ EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[0].desiredRefreshRate);
+ EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[1].desiredRefreshRate);
}
TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
@@ -406,7 +405,7 @@
addChild(parent, child1);
addChild(child1, childOfChild1);
- childOfChild1->setFrameRate(FRAME_RATE_VOTE1);
+ childOfChild1->setFrameRate(FRAME_RATE_VOTE1.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
@@ -420,7 +419,7 @@
EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
- childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE);
+ childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
commitTransaction();
EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 1cc9ba4..dbf0cd8 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -91,7 +91,7 @@
const auto& display = getCurrentDisplayState(displayToken);
EXPECT_TRUE(display.isVirtual());
EXPECT_FALSE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
+ EXPECT_EQ(name.c_str(), display.displayName);
// --------------------------------------------------------------------
// Cleanup conditions
@@ -123,7 +123,7 @@
const auto& display = getCurrentDisplayState(displayToken);
EXPECT_TRUE(display.isVirtual());
EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
+ EXPECT_EQ(name.c_str(), display.displayName);
// --------------------------------------------------------------------
// Cleanup conditions
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index bd2344c..ed8d909 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -55,13 +55,17 @@
sp<DisplayDevice> mInnerDisplay, mOuterDisplay;
};
-TEST_F(FoldableTest, foldUnfold) {
+TEST_F(FoldableTest, promotesPacesetterOnBoot) {
// When the device boots, the inner display should be the pacesetter.
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
// ...and should still be after powering on.
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
// The outer display should become the pacesetter after folding.
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
@@ -72,6 +76,10 @@
mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
// The inner display should stay the pacesetter if both are powered on.
// TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
@@ -81,6 +89,28 @@
// The outer display should become the pacesetter if designated.
mFlinger.scheduler()->setPacesetterDisplay(kOuterDisplayId);
ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The inner display should become the pacesetter if designated.
+ mFlinger.scheduler()->setPacesetterDisplay(kInnerDisplayId);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+}
+
+TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) {
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+ // The outer display should become the pacesetter if the inner display powers off.
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The outer display should stay the pacesetter if both are powered on.
+ // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates.
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+
+ // The inner display should become the pacesetter if the outer display powers off.
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF);
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
}
TEST_F(FoldableTest, doesNotRequestHardwareVsyncIfPoweredOff) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e59d44d..8458aa3 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -631,7 +631,6 @@
auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
- auto& mutableTexturePool() { return mFlinger->mTexturePool; }
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
@@ -652,6 +651,11 @@
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
+ auto initTransactionTraceWriter() {
+ mFlinger->mTransactionTracing.emplace();
+ return mFlinger->initTransactionTraceWriter();
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
@@ -665,6 +669,7 @@
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>();
mFlinger->mCompositionEngine->setRenderEngine(mFlinger->mRenderEngine.get());
+ mFlinger->mTransactionTracing.reset();
}
/* ------------------------------------------------------------------------
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 764d19b..00b5bf0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -61,10 +61,7 @@
return sp<Layer>::make(args);
}
- void commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
- }
+ void commitTransaction(Layer* layer) { layer->commitTransaction(); }
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index e2c6491..caa265f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -60,10 +60,7 @@
return sp<Layer>::make(args);
}
- void commitTransaction(Layer* layer) {
- auto c = layer->getDrawingState();
- layer->commitTransaction(c);
- }
+ void commitTransaction(Layer* layer) { layer->commitTransaction(); }
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
diff --git a/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
new file mode 100644
index 0000000..379135e
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionTraceWriterTest.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TransactionTraceWriterTest"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <filesystem>
+
+#include "TestableSurfaceFlinger.h"
+
+namespace android {
+
+class TransactionTraceWriterTest : public testing::Test {
+protected:
+ std::string mFilename = "/data/local/tmp/testfile_transaction_trace.winscope";
+
+ void SetUp() { mFlinger.initTransactionTraceWriter(); }
+ void TearDown() { std::filesystem::remove(mFilename); }
+
+ void verifyTraceFile() {
+ std::fstream file(mFilename, std::ios::in);
+ ASSERT_TRUE(file.is_open());
+ std::string line;
+ char magicNumber[8];
+ file.read(magicNumber, 8);
+ EXPECT_EQ("\tTNXTRAC", std::string(magicNumber, magicNumber + 8));
+ }
+
+ TestableSurfaceFlinger mFlinger;
+};
+
+TEST_F(TransactionTraceWriterTest, canWriteToFile) {
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ EXPECT_EQ(access(mFilename.c_str(), F_OK), 0);
+ verifyTraceFile();
+}
+
+TEST_F(TransactionTraceWriterTest, canOverwriteFile) {
+ std::string testLine = "test";
+ {
+ std::ofstream file(mFilename, std::ios::out);
+ file << testLine;
+ }
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ true);
+ verifyTraceFile();
+}
+
+TEST_F(TransactionTraceWriterTest, doNotOverwriteFile) {
+ std::string testLine = "test";
+ {
+ std::ofstream file(mFilename, std::ios::out);
+ file << testLine;
+ }
+ TransactionTraceWriter::getInstance().invokeForTest(mFilename, /* overwrite */ false);
+ {
+ std::fstream file(mFilename, std::ios::in);
+ ASSERT_TRUE(file.is_open());
+ std::string line;
+ std::getline(file, line);
+ EXPECT_EQ(line, testLine);
+ }
+}
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index 809966f..71a2d2b 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -25,7 +25,6 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/Update.h"
#include "Tracing/LayerTracing.h"
-#include "Tracing/RingBuffer.h"
#include "Tracing/TransactionTracing.h"
using namespace android::surfaceflinger;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
index 2b9520f..364618d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
@@ -23,6 +23,7 @@
using aidl::android::hardware::power::IPowerHintSession;
using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
using android::binder::Status;
using namespace aidl::android::hardware::power;
@@ -45,6 +46,7 @@
(override));
MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
index ef9cd9b..4cfdd58 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h
@@ -19,5 +19,7 @@
#include <scheduler/FrameRateMode.h>
// Use a C style macro to keep the line numbers printed in gtest
-#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \
- EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode))
+#define EXPECT_FRAME_RATE_MODE(_modePtr, _fps, _mode) \
+ EXPECT_EQ((scheduler::FrameRateMode{(_fps), (_modePtr)}), (_mode)) \
+ << "Expected " << (_fps) << " (" << (_modePtr)->getFps() << ") but was " \
+ << (_mode).fps << " (" << (_mode).modePtr->getFps() << ")"
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index f297da5..1675584 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -170,7 +170,7 @@
String8 err(String8::format("pixel @ (%3d, %3d): "
"expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
- EXPECT_EQ(String8(), err) << err.string();
+ EXPECT_EQ(String8(), err) << err.c_str();
}
}
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index f2870b0..7eda9ef 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -29,8 +29,11 @@
return mExpiration <= std::chrono::steady_clock::now();
}
-DelayedCallback::Timestamp DelayedCallback::getExpiration() const {
- return mExpiration;
+std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const {
+ std::chrono::milliseconds delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ mExpiration - std::chrono::steady_clock::now());
+ // Return zero if this is already expired.
+ return delta > delta.zero() ? delta : delta.zero();
}
void DelayedCallback::run() const {
@@ -88,7 +91,9 @@
mCondition.wait(mMutex);
} else {
// Wait until next callback expires, or a new one is scheduled.
- mCondition.wait_until(mMutex, mQueue.top().getExpiration());
+ // Use the monotonic steady clock to wait for the measured delay interval via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration());
}
}
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
index 2c194b5..c8ec15d 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h
@@ -30,15 +30,13 @@
// Wrapper for a callback to be executed after a delay.
class DelayedCallback {
public:
- using Timestamp = std::chrono::time_point<std::chrono::steady_clock>;
-
DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay)
: mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {}
~DelayedCallback() = default;
void run() const;
bool isExpired() const;
- Timestamp getExpiration() const;
+ std::chrono::milliseconds getWaitForExpirationDuration() const;
// Compare by expiration time, where A < B when A expires first.
bool operator<(const DelayedCallback& other) const;
@@ -46,7 +44,9 @@
private:
std::function<void()> mCallback;
- Timestamp mExpiration;
+ // Use a steady monotonic clock to calculate the duration until expiration.
+ // This clock is not related to wall clock time and is most suitable for measuring intervals.
+ std::chrono::time_point<std::chrono::steady_clock> mExpiration;
};
// Schedules callbacks to be executed after a delay.
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index 106ab9e..426cd42 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -71,15 +71,21 @@
}
int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
- time_point<steady_clock> expiration = steady_clock::now() + timeout + TEST_TIMEOUT;
+ time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT;
int32_t expiredCallbackCount = 0;
- while (steady_clock::now() < expiration) {
+ while (steady_clock::now() < expirationTime) {
std::lock_guard<std::mutex> lock(mMutex);
expiredCallbackCount = mExpiredCallbacks.size();
if (callbackCount <= expiredCallbackCount) {
return expiredCallbackCount;
}
- mCondition.wait_until(mMutex, expiration);
+ auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(
+ expirationTime - steady_clock::now());
+ if (currentTimeout > currentTimeout.zero()) {
+ // Use the monotonic steady clock to wait for the requested timeout via wait_for
+ // instead of using a wall clock via wait_until.
+ mCondition.wait_for(mMutex, currentTimeout);
+ }
}
return expiredCallbackCount;
}
diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
index 523f890..d0a9da1 100644
--- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
+++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp
@@ -113,7 +113,7 @@
static_cast<long>(client_pid_));
touchpad_->dumpInternal(result);
}
- write(fd, result.string(), result.size());
+ write(fd, result.c_str(), result.size());
return OK;
}
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d21deef..bdba27e 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -763,6 +763,17 @@
continue;
}
+ // Ignore duplicate extensions (see: b/288929054)
+ bool duplicate_entry = false;
+ for (uint32_t j = 0; j < filter.name_count; j++) {
+ if (strcmp(name, filter.names[j]) == 0) {
+ duplicate_entry = true;
+ break;
+ }
+ }
+ if (duplicate_entry == true)
+ continue;
+
filter.names[filter.name_count++] = name;
if (ext_bit != ProcHook::EXTENSION_UNKNOWN) {
if (ext_bit == ProcHook::ANDROID_native_buffer)