Merge changes from topics "ndk-fixed-size-array", "simplify-ndk-backend" am: 3183b5c2cc am: 3f1d935b11 am: a546ac860d am: 3f678a9038
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/1906327
Change-Id: Ia4d938ac977424f308bc4e558772e84d7bfcdadc
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 7ef26bf..6d837c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -28,6 +28,7 @@
services/vibratorservice/
services/vr/
vulkan/
+bpfmt = -d
[Hook Scripts]
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 6459443..bb1d206 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -244,6 +244,7 @@
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
{ OPT, "events/gpu_mem/gpu_mem_total/enable" },
+ { OPT, "events/fastrpc/fastrpc_dma_stat/enable" },
} },
{ "thermal", "Thermal event", 0, {
{ REQ, "events/thermal/thermal_temperature/enable" },
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 94c4c8c..2935c6a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,7 +77,7 @@
#define LOG_TAG "installd"
#endif
-// #define GRANULAR_LOCKS
+#define GRANULAR_LOCKS
using android::base::ParseUint;
using android::base::StringPrintf;
@@ -1572,7 +1572,7 @@
}
binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid,
- int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
+ int64_t targetFreeBytes, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
#ifndef GRANULAR_LOCKS
@@ -1688,12 +1688,6 @@
break;
}
- // Only keep clearing when we haven't pushed into reserved area
- if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
- LOG(DEBUG) << "Refusing to clear cached data in reserved space";
- break;
- }
-
// Find the best tracker to work with; this might involve swapping
// if the active tracker is no longer the most over quota
bool nextBetter = active && !queue.empty()
@@ -2644,22 +2638,20 @@
const char* default_value = nullptr) {
return data ? data->c_str() : default_value;
}
-binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
- const std::optional<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+binder::Status InstalldNativeService::dexopt(
+ const std::string& apkPath, int32_t uid, const std::string& packageName,
+ const std::string& instructionSet, int32_t dexoptNeeded,
+ const std::optional<std::string>& outputPath, int32_t dexFlags,
const std::string& compilerFilter, const std::optional<std::string>& uuid,
const std::optional<std::string>& classLoaderContext,
const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
const std::optional<std::string>& profileName,
const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason,
- bool* aidl_return) {
+ const std::optional<std::string>& compilationReason, bool* aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PATH(apkPath);
- if (packageName && *packageName != "*") {
- CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
- }
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(outputPath);
CHECK_ARGUMENT_PATH(dexMetadataPath);
const auto userId = multiuser_get_user_id(uid);
@@ -2667,13 +2659,13 @@
const char* oat_dir = getCStr(outputPath);
const char* instruction_set = instructionSet.c_str();
- if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) {
+ if (oat_dir != nullptr && !createOatDir(packageName, oat_dir, instruction_set).isOk()) {
// Can't create oat dir - let dexopt use cache dir.
oat_dir = nullptr;
}
const char* apk_path = apkPath.c_str();
- const char* pkgname = getCStr(packageName, "*");
+ const char* pkgname = packageName.c_str();
const char* compiler_filter = compilerFilter.c_str();
const char* volume_uuid = getCStr(uuid);
const char* class_loader_context = getCStr(classLoaderContext);
@@ -2840,9 +2832,11 @@
return res;
}
-binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
- const std::string& instructionSet) {
+binder::Status InstalldNativeService::createOatDir(const std::string& packageName,
+ const std::string& oatDir,
+ const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(oatDir);
LOCK_PACKAGE();
@@ -2866,8 +2860,10 @@
return ok();
}
-binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageName,
+ const std::string& packageDir) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(packageDir);
LOCK_PACKAGE();
@@ -2880,9 +2876,12 @@
return ok();
}
-binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
- const std::string& fromBase, const std::string& toBase) {
+binder::Status InstalldNativeService::linkFile(const std::string& packageName,
+ const std::string& relativePath,
+ const std::string& fromBase,
+ const std::string& toBase) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(fromBase);
CHECK_ARGUMENT_PATH(toBase);
LOCK_PACKAGE();
@@ -2910,9 +2909,12 @@
return ok();
}
-binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
- const std::string& instructionSet, const std::string& outputPath) {
+binder::Status InstalldNativeService::moveAb(const std::string& packageName,
+ const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::string& outputPath) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
LOCK_PACKAGE();
@@ -2925,10 +2927,13 @@
return success ? ok() : error();
}
-binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
- const std::string& instructionSet, const std::optional<std::string>& outputPath,
- int64_t* _aidl_return) {
+binder::Status InstalldNativeService::deleteOdex(const std::string& packageName,
+ const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::optional<std::string>& outputPath,
+ int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
LOCK_PACKAGE();
@@ -2960,9 +2965,12 @@
#endif
-binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
- android::base::unique_fd verityInputAshmem, int32_t contentSize) {
+binder::Status InstalldNativeService::installApkVerity(const std::string& packageName,
+ const std::string& filePath,
+ android::base::unique_fd verityInputAshmem,
+ int32_t contentSize) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(filePath);
LOCK_PACKAGE();
@@ -3042,9 +3050,11 @@
return ok();
}
-binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath,
+binder::Status InstalldNativeService::assertFsverityRootHashMatches(
+ const std::string& packageName, const std::string& filePath,
const std::vector<uint8_t>& expectedHash) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(filePath);
LOCK_PACKAGE();
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 09581bb..04662ea 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -119,16 +119,15 @@
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath);
- binder::Status dexopt(const std::string& apkPath, int32_t uid,
- const std::optional<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::optional<std::string>& uuid,
- const std::optional<std::string>& classLoaderContext,
- const std::optional<std::string>& seInfo, bool downgrade,
- int32_t targetSdkVersion, const std::optional<std::string>& profileName,
- const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason,
- bool* aidl_return);
+ binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName,
+ const std::string& instructionSet, int32_t dexoptNeeded,
+ const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade,
+ int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason, bool* aidl_return);
binder::Status controlDexOptBlocking(bool block);
@@ -152,22 +151,25 @@
binder::Status destroyProfileSnapshot(const std::string& packageName,
const std::string& profileName);
- binder::Status rmPackageDir(const std::string& packageDir);
+ binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir);
binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
- int64_t cacheReservedBytes, int32_t flags);
+ int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
- binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
- binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
- const std::string& toBase);
- binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
- const std::string& outputPath);
- binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
- const std::optional<std::string>& outputPath, int64_t* _aidl_return);
- binder::Status installApkVerity(const std::string& filePath,
- android::base::unique_fd verityInput, int32_t contentSize);
- binder::Status assertFsverityRootHashMatches(const std::string& filePath,
- const std::vector<uint8_t>& expectedHash);
+ binder::Status createOatDir(const std::string& packageName, const std::string& oatDir,
+ const std::string& instructionSet);
+ binder::Status linkFile(const std::string& packageName, const std::string& relativePath,
+ const std::string& fromBase, const std::string& toBase);
+ binder::Status moveAb(const std::string& packageName, const std::string& apkPath,
+ const std::string& instructionSet, const std::string& outputPath);
+ binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::optional<std::string>& outputPath, int64_t* _aidl_return);
+ binder::Status installApkVerity(const std::string& packageName, const std::string& filePath,
+ android::base::unique_fd verityInput, int32_t contentSize);
+ binder::Status assertFsverityRootHashMatches(const std::string& packageName,
+ const std::string& filePath,
+ const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 637a9f2..e024548 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -56,7 +56,7 @@
@utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
// Returns false if it is cancelled. Returns true if it is completed or have other errors.
- boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+ boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName,
@utf8InCpp String instructionSet, int dexoptNeeded,
@nullable @utf8InCpp String outputPath, int dexFlags,
@utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
@@ -85,21 +85,22 @@
@utf8InCpp String profileName, @utf8InCpp String classpath);
void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
- void rmPackageDir(@utf8InCpp String packageDir);
- void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
- long cacheReservedBytes, int flags);
+ void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir);
+ void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
@utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
- void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
- void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
- @utf8InCpp String toBase);
- void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
- @utf8InCpp String outputPath);
- long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
- @nullable @utf8InCpp String outputPath);
- void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput,
- int contentSize);
- void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash);
+ void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir,
+ @utf8InCpp String instructionSet);
+ void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath,
+ @utf8InCpp String fromBase, @utf8InCpp String toBase);
+ void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+ @utf8InCpp String instructionSet, @utf8InCpp String outputPath);
+ long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+ @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+ void installApkVerity(@utf8InCpp String packageName, @utf8InCpp String filePath,
+ in FileDescriptor verityInput, int contentSize);
+ void assertFsverityRootHashMatches(@utf8InCpp String packageName, @utf8InCpp String filePath,
+ in byte[] expectedHash);
boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 72f5f4b..8a27a06 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -146,7 +146,7 @@
EXPECT_EQ(0, exists("com.example/cache/foo/one"));
EXPECT_EQ(0, exists("com.example/cache/foo/two"));
- service->freeCache(testUuid, kTbInBytes, 0,
+ service->freeCache(testUuid, kTbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(0, exists("com.example/normal"));
@@ -154,6 +154,33 @@
EXPECT_EQ(-1, exists("com.example/cache/foo/two"));
}
+TEST_F(CacheTest, FreeCache_NonAggressive) {
+ LOG(INFO) << "FreeCache_NonAggressive";
+
+ mkdir("com.example");
+ touch("com.example/normal", 1 * kMbInBytes, 60);
+ mkdir("com.example/cache");
+ mkdir("com.example/cache/foo");
+ touch("com.example/cache/foo/one", 65 * kMbInBytes, 60);
+ touch("com.example/cache/foo/two", 2 * kMbInBytes, 120);
+
+ EXPECT_EQ(0, exists("com.example/normal"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+ service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
+
+ EXPECT_EQ(0, exists("com.example/normal"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+
+ service->freeCache(testUuid, kTbInBytes, FLAG_FREE_CACHE_V2);
+
+ EXPECT_EQ(0, exists("com.example/normal"));
+ EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
+ EXPECT_EQ(0, exists("com.example/cache/foo/two"));
+}
+
TEST_F(CacheTest, FreeCache_Age) {
LOG(INFO) << "FreeCache_Age";
@@ -163,13 +190,13 @@
touch("com.example/cache/foo/one", kMbInBytes, 60);
touch("com.example/cache/foo/two", kMbInBytes, 120);
- service->freeCache(testUuid, free() + kKbInBytes, 0,
+ service->freeCache(testUuid, free() + kKbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
EXPECT_EQ(0, exists("com.example/cache/foo/two"));
- service->freeCache(testUuid, free() + kKbInBytes, 0,
+ service->freeCache(testUuid, free() + kKbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
@@ -197,7 +224,7 @@
EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
- service->freeCache(testUuid, kTbInBytes, 0,
+ service->freeCache(testUuid, kTbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -219,7 +246,7 @@
setxattr("com.example/cache/foo", "user.cache_group");
- service->freeCache(testUuid, free() + kKbInBytes, 0,
+ service->freeCache(testUuid, free() + kKbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -264,7 +291,7 @@
setxattr("com.example/cache/tomb", "user.cache_tombstone");
setxattr("com.example/cache/tomb/group", "user.cache_group");
- service->freeCache(testUuid, free() + kKbInBytes, 0,
+ service->freeCache(testUuid, free() + kKbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
@@ -285,7 +312,7 @@
EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
- service->freeCache(testUuid, free() + kKbInBytes, 0,
+ service->freeCache(testUuid, free() + kKbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, size("com.example/cache/group/file1"));
@@ -306,7 +333,7 @@
EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
- service->freeCache(testUuid, kTbInBytes, 0,
+ service->freeCache(testUuid, kTbInBytes,
FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
EXPECT_EQ(-1, size("com.example/cache/group/file1"));
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index a937436..bb36c39 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -635,6 +635,7 @@
int64_t bytes_freed;
binder::Status result = service_->deleteOdex(
+ package_name_,
apk_path_,
kRuntimeIsa,
in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()),
@@ -729,7 +730,7 @@
TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) {
LOG(INFO) << "DexoptPrimaryPublic";
- ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa));
+ ASSERT_BINDER_SUCCESS(service_->createOatDir(package_name_, app_oat_dir_, kRuntimeIsa));
CompilePrimaryDexOk("verify",
DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
app_oat_dir_.c_str(),
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index cfd42fe..3f7c7d6 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -528,7 +528,7 @@
void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t,
layer_id id, const LayerStackChange& lsc) {
ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
- t.setLayerStack(mLayers[id], lsc.layer_stack());
+ t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack()));
}
void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t,
@@ -566,7 +566,7 @@
void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
display_id id, const LayerStackChange& lsc) {
- t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack());
+ t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack()));
}
void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index af8f718..931c5e3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -243,11 +243,3 @@
src: "handheld_core_hardware.xml",
defaults: ["frameworks_native_data_etc_defaults"],
}
-
-prebuilt_etc {
- name: "android.software.app_compat_overrides.xml",
- product_specific: true,
- sub_dir: "permissions",
- src: "android.software.app_compat_overrides.xml",
- filename_from_src: true,
-}
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index b743f49..0389e57 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -39,6 +39,12 @@
*/
typedef struct AChoreographer AChoreographer;
+struct AChoreographerFrameCallbackData;
+/**
+ * Opaque type that provides access to an AChoreographerFrameCallbackData object.
+ */
+typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData;
+
/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the time that the frame is being rendered as nanoseconds in the
@@ -60,6 +66,14 @@
typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the frame data that should not outlive the callback, as well as the data pointer
+ * provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_extendedFrameCallback)(
+ const AChoreographerFrameCallbackData* callbackData, void* data);
+
+/**
* Prototype of the function that is called when the display refresh rate
* changes. It's passed the new vsync period in nanoseconds, as well as the data
* pointer provided by the application that registered a callback.
@@ -111,6 +125,14 @@
uint32_t delayMillis) __INTRODUCED_IN(29);
/**
+ * Posts a callback to run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback, void* data)
+ __INTRODUCED_IN(33);
+
+/**
* Registers a callback to be run when the display refresh rate changes. The
* data pointer provided will be passed to the callback function when it's
* called. The same callback may be registered multiple times, provided that a
@@ -160,6 +182,42 @@
AChoreographer_refreshRateCallback, void* data)
__INTRODUCED_IN(30);
+/**
+ * The time in nanoseconds when the frame started being rendered.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The number of possible frame timelines.
+ */
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * Get index of the platform-preferred FrameTimeline.
+ */
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33);
+
+/**
+ * The vsync ID token used to map Choreographer data.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index is expected to be presented.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
+/**
+ * The time in nanoseconds which the frame at given index needs to be ready by.
+ */
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33);
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/input.h b/include/android/input.h
index 7642215..27587ce 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -169,6 +169,9 @@
/** Drag event */
AINPUT_EVENT_TYPE_DRAG = 5,
+
+ /** TouchMode event */
+ AINPUT_EVENT_TYPE_TOUCH_MODE = 6,
};
/**
@@ -1382,6 +1385,14 @@
*/
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
+/**
+ * Supplies the AInputQueue* object associated with the supplied Java InputQueue
+ * object.
+ *
+ * Available since API level 33.
+ */
+AInputQueue* AInputQueue_fromJava(jobject inputQueue) __INTRODUCED_IN(33);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h
index 27c8476..ae70831 100644
--- a/include/ftl/Flags.h
+++ b/include/ftl/Flags.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,79 +14,22 @@
* limitations under the License.
*/
-#include <android-base/stringprintf.h>
+#pragma once
-#include <array>
+#include <ftl/enum.h>
+#include <ftl/string.h>
+
#include <cstdint>
-#include <optional>
+#include <iterator>
#include <string>
#include <type_traits>
-#include <ftl/NamedEnum.h>
#include "utils/BitSet.h"
-#pragma once
+// TODO(b/185536303): Align with FTL style and namespace.
namespace android {
-namespace details {
-
-template <typename F>
-inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
-
-template <typename F, typename T, T... I>
-constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) {
- constexpr size_t count = seq.size();
-
- std::array<F, count> values{};
- for (size_t i = 0, v = 0; v < count; ++i) {
- values[v++] = static_cast<F>(T{1} << i);
- }
-
- return values;
-}
-
-template <typename F>
-inline constexpr auto flag_values = generate_flag_values<F>(
- std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{});
-
-template <typename F, std::size_t... I>
-constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept {
- return std::array<std::optional<std::string_view>, sizeof...(I)>{
- {enum_value_name<F, flag_values<F>[I]>()...}};
-}
-
-template <typename F>
-inline constexpr auto flag_names =
- generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{});
-
-// A trait for determining whether a type is specifically an enum class or not.
-template <typename T, bool = std::is_enum_v<T>>
-struct is_enum_class : std::false_type {};
-
-// By definition, an enum class is an enum that is not implicitly convertible to its underlying
-// type.
-template <typename T>
-struct is_enum_class<T, true>
- : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
-
-template <typename T>
-inline constexpr bool is_enum_class_v = is_enum_class<T>::value;
-} // namespace details
-
-template <auto V>
-constexpr auto flag_name() {
- using F = decltype(V);
- return details::enum_value_name<F, V>();
-}
-
-template <typename F>
-constexpr std::optional<std::string_view> flag_name(F flag) {
- using U = std::underlying_type_t<F>;
- auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag)));
- return details::flag_names<F>[idx];
-}
-
/* A class for handling flags defined by an enum or enum class in a type-safe way. */
template <typename F>
class Flags {
@@ -94,7 +37,7 @@
// further to avoid this restriction but in general we want to encourage the use of enums
// anyways.
static_assert(std::is_enum_v<F>, "Flags type must be an enum");
- using U = typename std::underlying_type_t<F>;
+ using U = std::underlying_type_t<F>;
public:
constexpr Flags(F f) : mFlags(static_cast<U>(f)) {}
@@ -106,11 +49,10 @@
// should force them to be explicitly constructed from their underlying types to make full use
// of the type checker.
template <typename T = U>
- constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr)
- : mFlags(t) {}
+ constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {}
+
template <typename T = U>
- explicit constexpr Flags(T t,
- typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr)
+ explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr)
: mFlags(t) {}
class Iterator {
@@ -229,16 +171,16 @@
bool first = true;
U unstringified = 0;
for (const F f : *this) {
- std::optional<std::string_view> flagString = flag_name(f);
- if (flagString) {
- appendFlag(result, flagString.value(), first);
+ if (const auto flagName = ftl::flag_name(f)) {
+ appendFlag(result, flagName.value(), first);
} else {
unstringified |= static_cast<U>(f);
}
}
if (unstringified != 0) {
- appendFlag(result, base::StringPrintf("0x%08x", unstringified), first);
+ constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex;
+ appendFlag(result, ftl::to_string(unstringified, radix), first);
}
if (first) {
@@ -265,15 +207,14 @@
// as flags. In order to use these, add them via a `using namespace` declaration.
namespace flag_operators {
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
inline Flags<F> operator~(F f) {
- using U = typename std::underlying_type_t<F>;
- return static_cast<F>(~static_cast<U>(f));
+ return static_cast<F>(~ftl::enum_cast(f));
}
-template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>>
+
+template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>>
Flags<F> operator|(F lhs, F rhs) {
- using U = typename std::underlying_type_t<F>;
- return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs));
+ return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs));
}
} // namespace flag_operators
diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h
deleted file mode 100644
index 6e98fee..0000000
--- a/include/ftl/NamedEnum.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <android-base/stringprintf.h>
-
-#include <array>
-#include <cstdint>
-#include <optional>
-#include <string>
-
-#pragma once
-
-namespace android {
-
-namespace details {
-template <typename E, E V>
-constexpr std::optional<std::string_view> enum_value_name() {
- // Should look something like (but all on one line):
- // std::optional<std::string_view>
- // android::details::enum_value_name()
- // [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
- std::string_view view = __PRETTY_FUNCTION__;
- size_t templateStart = view.rfind("[");
- size_t templateEnd = view.rfind("]");
- if (templateStart == std::string::npos || templateEnd == std::string::npos) {
- return std::nullopt;
- }
-
- // Extract the template parameters without the enclosing braces.
- // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
- view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
- size_t valStart = view.rfind("V = ");
- if (valStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Example (cont'd): V = android::test::TestEnums::ONE
- view = view.substr(valStart);
- // Check invalid enum values with cast, like V = (android::test::TestEnums)8.
- if (view.find('(') != std::string::npos) {
- return std::nullopt;
- }
- size_t nameStart = view.rfind("::");
- if (nameStart == std::string::npos) {
- return std::nullopt;
- }
-
- // Chop off the initial "::"
- nameStart += 2;
- return view.substr(nameStart);
-}
-
-template <typename E, typename T, T... I>
-constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
- constexpr size_t count = seq.size();
-
- std::array<E, count> values{};
- for (size_t i = 0, v = 0; v < count; ++i) {
- values[v++] = static_cast<E>(T{0} + i);
- }
-
- return values;
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_values =
- generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});
-
-template <typename E, std::size_t N, std::size_t... I>
-constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
- return std::array<std::optional<std::string_view>, sizeof...(I)>{
- {enum_value_name<E, enum_values<E, N>[I]>()...}};
-}
-
-template <typename E, std::size_t N>
-inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});
-
-} // namespace details
-
-class NamedEnum {
-public:
- // By default allowed enum value range is 0 ~ 7.
- template <typename E>
- static constexpr size_t max = 8;
-
- template <auto V>
- static constexpr auto enum_name() {
- using E = decltype(V);
- return details::enum_value_name<E, V>();
- }
-
- template <typename E>
- static constexpr std::optional<std::string_view> enum_name(E val) {
- auto idx = static_cast<size_t>(val);
- return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
- }
-
- // Helper function for parsing enum value to string.
- // Example : enum class TestEnums { ZERO = 0x0 };
- // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
- // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
- // it should be declared to specialized the maximum enum by below:
- // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
- // If the enum class definition is sparse and contains enum values starting from a large value,
- // Do not specialize it to a large number to avoid performance issues.
- // The recommended maximum enum number to specialize is 64.
- template <typename E>
- static const std::string string(E val, const char* fallbackFormat = "%02d") {
- std::string result;
- std::optional<std::string_view> enumString = enum_name(val);
- result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
- return result;
- }
-};
-
-} // namespace android
diff --git a/include/ftl/cast.h b/include/ftl/cast.h
new file mode 100644
index 0000000..ff1b58a
--- /dev/null
+++ b/include/ftl/cast.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <limits>
+#include <type_traits>
+
+#include <ftl/details/cast.h>
+
+namespace android::ftl {
+
+enum class CastSafety { kSafe, kUnderflow, kOverflow };
+
+// Returns whether static_cast<R>(v) is safe, or would result in underflow or overflow.
+//
+// static_assert(ftl::cast_safety<uint8_t>(-1) == ftl::CastSafety::kUnderflow);
+// static_assert(ftl::cast_safety<int8_t>(128u) == ftl::CastSafety::kOverflow);
+//
+// static_assert(ftl::cast_safety<uint32_t>(-.1f) == ftl::CastSafety::kUnderflow);
+// static_assert(ftl::cast_safety<int32_t>(static_cast<float>(INT32_MAX)) ==
+// ftl::CastSafety::kOverflow);
+//
+// static_assert(ftl::cast_safety<float>(-DBL_MAX) == ftl::CastSafety::kUnderflow);
+//
+template <typename R, typename T>
+constexpr CastSafety cast_safety(T v) {
+ static_assert(std::is_arithmetic_v<T>);
+ static_assert(std::is_arithmetic_v<R>);
+
+ constexpr bool kFromSigned = std::is_signed_v<T>;
+ constexpr bool kToSigned = std::is_signed_v<R>;
+
+ using details::max_exponent;
+
+ // If the R range contains the T range, then casting is always safe.
+ if constexpr ((kFromSigned == kToSigned && max_exponent<R> >= max_exponent<T>) ||
+ (!kFromSigned && kToSigned && max_exponent<R> > max_exponent<T>)) {
+ return CastSafety::kSafe;
+ }
+
+ using C = std::common_type_t<R, T>;
+
+ if constexpr (kFromSigned) {
+ using L = details::safe_limits<R, T>;
+
+ if constexpr (kToSigned) {
+ // Signed to signed.
+ if (v < L::lowest()) return CastSafety::kUnderflow;
+ return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+ } else {
+ // Signed to unsigned.
+ if (v < 0) return CastSafety::kUnderflow;
+ return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+ : CastSafety::kOverflow;
+ }
+ } else {
+ using L = std::numeric_limits<R>;
+
+ if constexpr (kToSigned) {
+ // Unsigned to signed.
+ return static_cast<C>(v) <= static_cast<C>(L::max()) ? CastSafety::kSafe
+ : CastSafety::kOverflow;
+ } else {
+ // Unsigned to unsigned.
+ return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow;
+ }
+ }
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/details/cast.h b/include/ftl/details/cast.h
new file mode 100644
index 0000000..87b9f1e
--- /dev/null
+++ b/include/ftl/details/cast.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <limits>
+#include <type_traits>
+
+namespace android::ftl::details {
+
+// Exponent whose power of 2 is the (exclusive) upper bound of T.
+template <typename T, typename L = std::numeric_limits<T>>
+constexpr int max_exponent = std::is_floating_point_v<T> ? L::max_exponent : L::digits;
+
+// Extension of std::numeric_limits<T> that reduces the maximum for integral types T such that it
+// has an exact representation for floating-point types F. For example, the maximum int32_t value
+// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot
+// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is
+// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the
+// minimum is representable.
+template <typename T, typename F>
+struct safe_limits : std::numeric_limits<T> {
+ static constexpr T max() {
+ using Base = std::numeric_limits<T>;
+
+ if constexpr (std::is_integral_v<T> && std::is_floating_point_v<F>) {
+ // Assume the mantissa is 24 bits for float, or 53 bits for double.
+ using Float = std::numeric_limits<F>;
+ static_assert(Float::is_iec559);
+
+ // If the integer is wider than the mantissa, clear the excess bits of precision.
+ constexpr int kShift = Base::digits - Float::digits;
+ if constexpr (kShift > 0) {
+ using U = std::make_unsigned_t<T>;
+ constexpr U kOne = static_cast<U>(1);
+ return static_cast<U>(Base::max()) & ~((kOne << kShift) - kOne);
+ }
+ }
+
+ return Base::max();
+ }
+};
+
+} // namespace android::ftl::details
diff --git a/include/ftl/enum.h b/include/ftl/enum.h
new file mode 100644
index 0000000..dfe3a09
--- /dev/null
+++ b/include/ftl/enum.h
@@ -0,0 +1,299 @@
+/*
+ * 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 <cstddef>
+#include <limits>
+#include <optional>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+
+#include <ftl/string.h>
+
+// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
+// compiler-generated string literal for the signature of this function. The function is defined in
+// the global namespace with a short name and inferred return type to reduce bloat in the read-only
+// data segment.
+template <typename E, E V>
+constexpr auto ftl_enum() {
+ static_assert(std::is_enum_v<E>);
+
+ using R = std::optional<std::string_view>;
+ using namespace std::literals;
+
+ // The "pretty" signature has the following format:
+ //
+ // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue]
+ //
+ std::string_view view = __PRETTY_FUNCTION__;
+ const auto template_begin = view.rfind('[');
+ const auto template_end = view.rfind(']');
+ if (template_begin == view.npos || template_end == view.npos) return R{};
+
+ // Extract the template parameters without the enclosing brackets. Example (cont'd):
+ //
+ // E = android::test::Enum, V = android::test::Enum::kValue
+ //
+ view = view.substr(template_begin + 1, template_end - template_begin - 1);
+ const auto value_begin = view.rfind("V = "sv);
+ if (value_begin == view.npos) return R{};
+
+ // Example (cont'd):
+ //
+ // V = android::test::Enum::kValue
+ //
+ view = view.substr(value_begin);
+ const auto name_begin = view.rfind("::"sv);
+ if (name_begin == view.npos) return R{};
+
+ // Chop off the leading "::".
+ const auto name = view.substr(name_begin + 2);
+
+ // A value that is not enumerated has the format "Enum)42".
+ return name.find(')') == view.npos ? R{name} : R{};
+}
+
+namespace android::ftl {
+
+// Trait for determining whether a type is specifically a scoped enum or not. By definition, a
+// scoped enum is one that is not implicitly convertible to its underlying type.
+//
+// TODO: Replace with std::is_scoped_enum in C++23.
+//
+template <typename T, bool = std::is_enum_v<T>>
+struct is_scoped_enum : std::false_type {};
+
+template <typename T>
+struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
+};
+
+template <typename T>
+inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
+
+// Shorthand for casting an enumerator to its integral value.
+//
+// enum class E { A, B, C };
+// static_assert(ftl::enum_cast(E::B) == 1);
+//
+template <typename E>
+constexpr auto enum_cast(E v) {
+ return static_cast<std::underlying_type_t<E>>(v);
+}
+
+// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
+// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
+// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
+// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
+// range values results in undefined behavior if the underlying type is not fixed.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// static_assert(ftl::enum_begin_v<E> == E::A);
+// static_assert(ftl::enum_last_v<E> == E::F);
+// static_assert(ftl::enum_size_v<E> == 6);
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// static_assert(ftl::enum_begin_v<F> == F{0});
+// static_assert(ftl::enum_last_v<F> == F{15});
+// static_assert(ftl::enum_size_v<F> == 16);
+//
+template <typename E, typename = void>
+struct enum_begin {
+ static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
+ static constexpr E value{0};
+};
+
+template <typename E>
+struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
+ static constexpr E value = E::ftl_first;
+};
+
+template <typename E>
+inline constexpr E enum_begin_v = enum_begin<E>::value;
+
+template <typename E, typename = void>
+struct enum_end {
+ using U = std::underlying_type_t<E>;
+ static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
+
+ static constexpr E value{std::numeric_limits<U>::digits};
+};
+
+template <typename E>
+struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
+ static constexpr E value = E{enum_cast(E::ftl_last) + 1};
+};
+
+template <typename E>
+inline constexpr E enum_end_v = enum_end<E>::value;
+
+template <typename E>
+inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};
+
+template <typename E>
+struct enum_size {
+ static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ static constexpr auto kEnd = enum_cast(enum_end_v<E>);
+ static_assert(kBegin < kEnd, "Invalid range");
+
+ static constexpr std::size_t value = kEnd - kBegin;
+ static_assert(value <= 64, "Excessive range size");
+};
+
+template <typename E>
+inline constexpr std::size_t enum_size_v = enum_size<E>::value;
+
+namespace details {
+
+template <auto V>
+struct Identity {
+ static constexpr auto value = V;
+};
+
+template <typename E>
+using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
+
+template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
+struct EnumRange;
+
+template <typename E, template <E> class F, typename T, T... Vs>
+struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
+ static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ static constexpr auto kSize = enum_size_v<E>;
+
+ using R = decltype(F<E{}>::value);
+ const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
+
+ constexpr const auto* begin() const { return values; }
+ constexpr const auto* end() const { return values + kSize; }
+};
+
+template <auto V>
+struct EnumName {
+ static constexpr auto value = ftl_enum<decltype(V), V>();
+};
+
+template <auto I>
+struct FlagName {
+ using E = decltype(I);
+ using U = std::underlying_type_t<E>;
+
+ static constexpr E V{U{1} << enum_cast(I)};
+ static constexpr auto value = ftl_enum<E, V>();
+};
+
+} // namespace details
+
+// Returns an iterable over the range of an enum.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// std::string string;
+// for (E v : ftl::enum_range<E>()) {
+// string += ftl::enum_name(v).value_or("?");
+// }
+//
+// assert(string == "ABC??F");
+//
+template <typename E>
+constexpr auto enum_range() {
+ return details::EnumRange<E>{};
+}
+
+// Returns a stringified enumerator at compile time.
+//
+// enum class E { A, B, C };
+// static_assert(ftl::enum_name<E::B>() == "B");
+//
+template <auto V>
+constexpr std::string_view enum_name() {
+ constexpr auto kName = ftl_enum<decltype(V), V>();
+ static_assert(kName, "Unknown enumerator");
+ return *kName;
+}
+
+// Returns a stringified enumerator, possibly at compile time.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+// static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> enum_name(E v) {
+ const auto value = enum_cast(v);
+
+ constexpr auto kBegin = enum_cast(enum_begin_v<E>);
+ constexpr auto kLast = enum_cast(enum_last_v<E>);
+ if (value < kBegin || value > kLast) return {};
+
+ constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
+ return kRange.values[value - kBegin];
+}
+
+// Returns a stringified flag enumerator, possibly at compile time.
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+//
+template <typename E>
+constexpr std::optional<std::string_view> flag_name(E v) {
+ const auto value = enum_cast(v);
+
+ // TODO: Replace with std::popcount and std::countr_zero in C++20.
+ if (__builtin_popcountl(value) != 1) return {};
+
+ constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
+ return kRange.values[__builtin_ctzl(value)];
+}
+
+// Returns a stringified enumerator, or its integral value if not named.
+//
+// enum class E { A, B, C, F = 5, ftl_last = F };
+//
+// assert(ftl::enum_string(E::C) == "C");
+// assert(ftl::enum_string(E{3}) == "3");
+//
+template <typename E>
+inline std::string enum_string(E v) {
+ if (const auto name = enum_name(v)) {
+ return std::string(*name);
+ }
+ return to_string(enum_cast(v));
+}
+
+// Returns a stringified flag enumerator, or its integral value if not named.
+//
+// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+//
+// assert(ftl::flag_string(F::Z) == "Z");
+// assert(ftl::flag_string(F{7}) == "0b111");
+//
+template <typename E>
+inline std::string flag_string(E v) {
+ if (const auto name = flag_name(v)) {
+ return std::string(*name);
+ }
+ constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
+ return to_string(enum_cast(v), radix);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/initializer_list.h b/include/ftl/initializer_list.h
index 769c09f..2102c25 100644
--- a/include/ftl/initializer_list.h
+++ b/include/ftl/initializer_list.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <tuple>
#include <utility>
@@ -65,18 +66,18 @@
std::tuple<Types...> tuple;
};
-template <typename K, typename V>
+template <typename K, typename V, typename KeyEqual = std::equal_to<K>>
struct KeyValue {};
// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the
// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works
// with the latter.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-struct InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...> {
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+struct InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...> {
// Accumulate the three arguments to std::pair's piecewise constructor.
template <typename... Args>
[[nodiscard]] constexpr auto operator()(K&& k, Args&&... args) && -> InitializerList<
- KeyValue<K, V>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
+ KeyValue<K, V, E>, std::index_sequence<Sizes..., 3>, Types..., std::piecewise_construct_t,
std::tuple<K&&>, std::tuple<Args&&...>> {
return {std::tuple_cat(
std::move(tuple),
@@ -94,9 +95,9 @@
return InitializerList<T>{}(std::forward<Args>(args)...);
}
-template <typename K, typename V, typename... Args>
+template <typename K, typename V, typename E = std::equal_to<K>, typename... Args>
[[nodiscard]] constexpr auto map(Args&&... args) {
- return list<KeyValue<K, V>>(std::forward<Args>(args)...);
+ return list<KeyValue<K, V, E>>(std::forward<Args>(args)...);
}
template <typename K, typename V>
diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h
index 84c15eb..2effaa4 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -19,6 +19,7 @@
#include <ftl/initializer_list.h>
#include <ftl/small_vector.h>
+#include <algorithm>
#include <functional>
#include <optional>
#include <type_traits>
@@ -28,7 +29,10 @@
// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are
// stored in contiguous storage for cache efficiency. The map is allocated statically until its size
-// exceeds N, at which point mappings are relocated to dynamic memory.
+// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has
+// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place
+// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator,
+// but immutable getters that can optionally transform the value.
//
// SmallMap<K, V, 0> unconditionally allocates on the heap.
//
@@ -43,18 +47,21 @@
// assert(!map.dynamic());
//
// assert(map.contains(123));
-// assert(map.find(42, [](const std::string& s) { return s.size(); }) == 3u);
+// assert(map.get(42, [](const std::string& s) { return s.size(); }) == 3u);
//
-// const auto opt = map.find(-1);
+// const auto opt = map.get(-1);
// assert(opt);
//
// std::string& ref = *opt;
// assert(ref.empty());
// ref = "xyz";
//
-// assert(map == SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+// map.emplace_or_replace(0, "vanilla", 2u, 3u);
+// assert(map.dynamic());
//
-template <typename K, typename V, std::size_t N>
+// assert(map == SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
+//
+template <typename K, typename V, std::size_t N, typename KeyEqual = std::equal_to<K>>
class SmallMap final {
using Map = SmallVector<std::pair<const K, V>, N>;
@@ -80,12 +87,7 @@
// The syntax for listing pairs is as follows:
//
// ftl::SmallMap map = ftl::init::map<int, std::string>(123, "abc")(-1)(42, 3u, '?');
- //
// static_assert(std::is_same_v<decltype(map), ftl::SmallMap<int, std::string, 3>>);
- // assert(map.size() == 3u);
- // assert(map.contains(-1) && map.find(-1)->get().empty());
- // assert(map.contains(42) && map.find(42)->get() == "???");
- // assert(map.contains(123) && map.find(123)->get() == "abc");
//
// The types of the key and value are deduced if the first pair contains exactly two arguments:
//
@@ -95,7 +97,7 @@
template <typename U, std::size_t... Sizes, typename... Types>
SmallMap(InitializerList<U, std::index_sequence<Sizes...>, Types...>&& list)
: map_(std::move(list)) {
- // TODO: Enforce unique keys.
+ deduplicate();
}
size_type max_size() const { return map_.max_size(); }
@@ -115,27 +117,27 @@
// Returns whether a mapping exists for the given key.
bool contains(const key_type& key) const {
- return find(key, [](const mapped_type&) {});
+ return get(key, [](const mapped_type&) {});
}
// Returns a reference to the value for the given key, or std::nullopt if the key was not found.
//
// ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
//
- // const auto opt = map.find('c');
+ // const auto opt = map.get('c');
// assert(opt == 'C');
//
// char d = 'd';
- // const auto ref = map.find('d').value_or(std::ref(d));
+ // const auto ref = map.get('d').value_or(std::ref(d));
// ref.get() = 'D';
// assert(d == 'D');
//
- auto find(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
- return find(key, [](const mapped_type& v) { return std::cref(v); });
+ auto get(const key_type& key) const -> std::optional<std::reference_wrapper<const mapped_type>> {
+ return get(key, [](const mapped_type& v) { return std::cref(v); });
}
- auto find(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
- return find(key, [](mapped_type& v) { return std::ref(v); });
+ auto get(const key_type& key) -> std::optional<std::reference_wrapper<mapped_type>> {
+ return get(key, [](mapped_type& v) { return std::ref(v); });
}
// Returns the result R of a unary operation F on (a constant or mutable reference to) the value
@@ -144,14 +146,14 @@
//
// ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
//
- // assert(map.find('c', [](char c) { return std::toupper(c); }) == 'Z');
- // assert(map.find('c', [](char& c) { c = std::toupper(c); }));
+ // assert(map.get('c', [](char c) { return std::toupper(c); }) == 'Z');
+ // assert(map.get('c', [](char& c) { c = std::toupper(c); }));
//
template <typename F, typename R = std::invoke_result_t<F, const mapped_type&>>
- auto find(const key_type& key, F f) const
+ auto get(const key_type& key, F f) const
-> std::conditional_t<std::is_void_v<R>, bool, std::optional<R>> {
for (auto& [k, v] : *this) {
- if (k == key) {
+ if (KeyEqual{}(k, key)) {
if constexpr (std::is_void_v<R>) {
f(v);
return true;
@@ -165,28 +167,119 @@
}
template <typename F>
- auto find(const key_type& key, F f) {
- return std::as_const(*this).find(
+ auto get(const key_type& key, F f) {
+ return std::as_const(*this).get(
key, [&f](const mapped_type& v) { return f(const_cast<mapped_type&>(v)); });
}
+ // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise.
+ const_iterator find(const key_type& key) const { return const_cast<SmallMap&>(*this).find(key); }
+ iterator find(const key_type& key) { return find(key, begin()); }
+
+ // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping,
+ // and whether the mapping was inserted.
+ //
+ // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+ // invalidated. Otherwise, only the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) {
+ if (const auto it = find(key); it != end()) {
+ return {it, false};
+ }
+
+ auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ return {&ref, true};
+ }
+
+ // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator
+ // otherwise.
+ //
+ // The value is replaced via move constructor, so type V does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the mapping being replaced.
+ //
+ // Iterators to the replaced mapping point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ iterator try_replace(const key_type& key, Args&&... args) {
+ const auto it = find(key);
+ if (it == end()) return it;
+ map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ return it;
+ }
+
+ // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or
+ // false on replace.
+ //
+ // The value is emplaced and replaced via move constructor, so type V does not need to define
+ // copy/move assignment, e.g. its data members may be const.
+ //
+ // On emplace, if the map reaches its static or dynamic capacity, then all iterators are
+ // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators
+ // to the replaced mapping point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ std::pair<iterator, bool> emplace_or_replace(const key_type& key, Args&&... args) {
+ const auto [it, ok] = try_emplace(key, std::forward<Args>(args)...);
+ if (ok) return {it, ok};
+ map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ return {it, ok};
+ }
+
+ // Removes a mapping if it exists, and returns whether it did.
+ //
+ // The last() and end() iterators, as well as those to the erased mapping, are invalidated.
+ //
+ bool erase(const key_type& key) { return erase(key, begin()); }
+
+ // Removes all mappings.
+ //
+ // All iterators are invalidated.
+ //
+ void clear() { map_.clear(); }
+
private:
+ iterator find(const key_type& key, iterator first) {
+ return std::find_if(first, end(),
+ [&key](const auto& pair) { return KeyEqual{}(pair.first, key); });
+ }
+
+ bool erase(const key_type& key, iterator first) {
+ const auto it = find(key, first);
+ if (it == end()) return false;
+ map_.unstable_erase(it);
+ return true;
+ }
+
+ void deduplicate() {
+ for (auto it = begin(); it != end();) {
+ if (const auto key = it->first; ++it != end()) {
+ while (erase(key, it));
+ }
+ }
+ }
+
Map map_;
};
// Deduction guide for in-place constructor.
-template <typename K, typename V, std::size_t... Sizes, typename... Types>
-SmallMap(InitializerList<KeyValue<K, V>, std::index_sequence<Sizes...>, Types...>&&)
- -> SmallMap<K, V, sizeof...(Sizes)>;
+template <typename K, typename V, typename E, std::size_t... Sizes, typename... Types>
+SmallMap(InitializerList<KeyValue<K, V, E>, std::index_sequence<Sizes...>, Types...>&&)
+ -> SmallMap<K, V, sizeof...(Sizes), E>;
// Returns whether the key-value pairs of two maps are equal.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-bool operator==(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+bool operator==(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
if (lhs.size() != rhs.size()) return false;
for (const auto& [k, v] : lhs) {
const auto& lv = v;
- if (!rhs.find(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
+ if (!rhs.get(k, [&lv](const auto& rv) { return lv == rv; }).value_or(false)) {
return false;
}
}
@@ -195,8 +288,8 @@
}
// TODO: Remove in C++20.
-template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M>
-inline bool operator!=(const SmallMap<K, V, N>& lhs, const SmallMap<Q, W, M>& rhs) {
+template <typename K, typename V, std::size_t N, typename Q, typename W, std::size_t M, typename E>
+inline bool operator!=(const SmallMap<K, V, N, E>& lhs, const SmallMap<Q, W, M, E>& rhs) {
return !(lhs == rhs);
}
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index cb0ae35..65a9536 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -151,8 +151,6 @@
DISPATCH(reference, back, noexcept)
DISPATCH(const_reference, back, const)
-#undef DISPATCH
-
reference operator[](size_type i) {
return dynamic() ? std::get<Dynamic>(vector_)[i] : std::get<Static>(vector_)[i];
}
@@ -214,13 +212,15 @@
//
// The last() and end() iterators are invalidated.
//
- void pop_back() {
- if (dynamic()) {
- std::get<Dynamic>(vector_).pop_back();
- } else {
- std::get<Static>(vector_).pop_back();
- }
- }
+ DISPATCH(void, pop_back, noexcept)
+
+ // Removes all elements.
+ //
+ // All iterators are invalidated.
+ //
+ DISPATCH(void, clear, noexcept)
+
+#undef DISPATCH
// Erases an element, but does not preserve order. Rather than shifting subsequent elements,
// this moves the last element to the slot of the erased element.
@@ -345,10 +345,11 @@
return true;
}
+ using Impl::clear;
using Impl::pop_back;
void unstable_erase(iterator it) {
- if (it != last()) std::iter_swap(it, last());
+ if (it != last()) replace(it, std::move(back()));
pop_back();
}
diff --git a/include/ftl/static_vector.h b/include/ftl/static_vector.h
index 96a1ae8..cd7b92a 100644
--- a/include/ftl/static_vector.h
+++ b/include/ftl/static_vector.h
@@ -189,8 +189,7 @@
}
StaticVector& operator=(StaticVector&& other) {
- std::destroy(begin(), end());
- size_ = 0;
+ clear();
swap<true>(other);
return *this;
}
@@ -280,6 +279,15 @@
//
void pop_back() { unstable_erase(last()); }
+ // Removes all elements.
+ //
+ // All iterators are invalidated.
+ //
+ void clear() {
+ std::destroy(begin(), end());
+ size_ = 0;
+ }
+
// Erases an element, but does not preserve order. Rather than shifting subsequent elements,
// this moves the last element to the slot of the erased element.
//
diff --git a/include/ftl/string.h b/include/ftl/string.h
new file mode 100644
index 0000000..2d96b06
--- /dev/null
+++ b/include/ftl/string.h
@@ -0,0 +1,101 @@
+/*
+ * 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 <cassert>
+#include <charconv>
+#include <limits>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace android::ftl {
+
+enum class Radix { kBin = 2, kDec = 10, kHex = 16 };
+
+template <typename T>
+struct to_chars_length {
+ static_assert(std::is_integral_v<T>);
+ // Maximum binary digits, plus minus sign and radix prefix.
+ static constexpr std::size_t value = std::numeric_limits<std::make_unsigned_t<T>>::digits + 3;
+};
+
+template <typename T>
+constexpr std::size_t to_chars_length_v = to_chars_length<T>::value;
+
+template <typename T = std::int64_t>
+using to_chars_buffer_t = char[to_chars_length_v<T>];
+
+// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with
+// optional radix. See also ftl::to_string below.
+//
+// ftl::to_chars_buffer_t<> buffer;
+//
+// assert(ftl::to_chars(buffer, 123u) == "123");
+// assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010");
+// assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe");
+// assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T, std::size_t N>
+std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) {
+ static_assert(N >= to_chars_length_v<T>);
+
+ auto begin = buffer + 2;
+ const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast<int>(radix));
+ assert(err == std::errc());
+
+ if (radix == Radix::kDec) {
+ // TODO: Replace with {begin, end} in C++20.
+ return {begin, static_cast<std::size_t>(end - begin)};
+ }
+
+ const auto prefix = radix == Radix::kBin ? 'b' : 'x';
+ if constexpr (std::is_unsigned_v<T>) {
+ buffer[0] = '0';
+ buffer[1] = prefix;
+ } else {
+ if (*begin == '-') {
+ *buffer = '-';
+ } else {
+ --begin;
+ }
+
+ *begin-- = prefix;
+ *begin = '0';
+ }
+
+ // TODO: Replace with {buffer, end} in C++20.
+ return {buffer, static_cast<std::size_t>(end - buffer)};
+}
+
+// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix.
+//
+// assert(ftl::to_string(123u) == "123");
+// assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010");
+// assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe");
+// assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a");
+//
+template <typename T>
+inline std::string to_string(T v, Radix radix = Radix::kDec) {
+ to_chars_buffer_t<T> buffer;
+ return std::string(to_chars(buffer, v, radix));
+}
+
+std::string to_string(bool) = delete;
+std::string to_string(bool, Radix) = delete;
+
+} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index a6213f3..9148fee 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -18,7 +18,8 @@
#define _LIBINPUT_DISPLAY_VIEWPORT_H
#include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
+#include <ftl/string.h>
#include <gui/constants.h>
#include <input/Input.h>
@@ -44,6 +45,8 @@
INTERNAL = 1,
EXTERNAL = 2,
VIRTUAL = 3,
+
+ ftl_last = VIRTUAL
};
/*
@@ -132,9 +135,8 @@
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
- physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
- : "<none>",
+ ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
+ physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
deviceHeight, isActive);
diff --git a/include/input/Input.h b/include/input/Input.h
index 4adaa5b..e1cacac 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -201,8 +201,17 @@
class Parcel;
#endif
+/*
+ * Apply the given transform to the point without applying any translation/offset.
+ */
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy);
+
const char* inputEventTypeToString(int32_t type);
+std::string inputEventSourceToString(int32_t source);
+
+bool isFromSource(uint32_t source, uint32_t test);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -369,8 +378,6 @@
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
- void scale(float globalScale);
-
// Scale the pointer coordinates according to a global scale and a
// window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
// axes, however the window scaling will not.
@@ -526,13 +533,17 @@
inline int32_t getAction() const { return mAction; }
- inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; }
+ static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; }
- inline int32_t getActionIndex() const {
- return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
- >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+ inline int32_t getActionMasked() const { return getActionMasked(mAction); }
+
+ static int32_t getActionIndex(int32_t action) {
+ return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
+ AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
+ inline int32_t getActionIndex() const { return getActionIndex(mAction); }
+
inline void setAction(int32_t action) { mAction = action; }
inline int32_t getFlags() const { return mFlags; }
@@ -563,6 +574,8 @@
inline ui::Transform getTransform() const { return mTransform; }
+ int getSurfaceRotation() const;
+
inline float getXPrecision() const { return mXPrecision; }
inline float getYPrecision() const { return mYPrecision; }
@@ -577,9 +590,7 @@
void setCursorPosition(float x, float y);
- uint32_t getDisplayOrientation() const { return mDisplayOrientation; }
-
- int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; }
+ ui::Transform getRawTransform() const { return mRawTransform; }
static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
@@ -755,8 +766,8 @@
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float rawXCursorPosition,
- float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth,
- int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ float rawYCursorPosition, const ui::Transform& rawTransform, nsecs_t downTime,
+ nsecs_t eventTime, size_t pointerCount,
const PointerProperties* pointerProperties, const PointerCoords* pointerCoords);
void copyFrom(const MotionEvent* other, bool keepHistory);
@@ -801,6 +812,14 @@
static std::string actionToString(int32_t action);
+ // MotionEvent will transform various axes in different ways, based on the source. For
+ // example, the x and y axes will not have any offsets/translations applied if it comes from a
+ // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
+ // are used to apply these transformations for different axes.
+ static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
+ static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
+ const PointerCoords&);
+
protected:
int32_t mAction;
int32_t mActionButton;
@@ -814,9 +833,7 @@
float mYPrecision;
float mRawXCursorPosition;
float mRawYCursorPosition;
- uint32_t mDisplayOrientation;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
+ ui::Transform mRawTransform;
nsecs_t mDownTime;
Vector<PointerProperties> mPointerProperties;
std::vector<nsecs_t> mSampleEventTimes;
@@ -834,15 +851,12 @@
inline bool getHasFocus() const { return mHasFocus; }
- inline bool getInTouchMode() const { return mInTouchMode; }
-
- void initialize(int32_t id, bool hasFocus, bool inTouchMode);
+ void initialize(int32_t id, bool hasFocus);
void initialize(const FocusEvent& from);
protected:
bool mHasFocus;
- bool mInTouchMode;
};
/*
@@ -888,6 +902,25 @@
float mX, mY;
};
+/*
+ * Touch mode events.
+ */
+class TouchModeEvent : public InputEvent {
+public:
+ virtual ~TouchModeEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_TOUCH_MODE; }
+
+ inline bool isInTouchMode() const { return mIsInTouchMode; }
+
+ void initialize(int32_t id, bool isInTouchMode);
+
+ void initialize(const TouchModeEvent& from);
+
+protected:
+ bool mIsInTouchMode;
+};
+
/**
* Base class for verified events.
* Do not create a VerifiedInputEvent explicitly.
@@ -912,8 +945,8 @@
*/
struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
int32_t action;
- nsecs_t downTimeNanos;
int32_t flags;
+ nsecs_t downTimeNanos;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
@@ -928,8 +961,8 @@
float rawX;
float rawY;
int32_t actionMasked;
- nsecs_t downTimeNanos;
int32_t flags;
+ nsecs_t downTimeNanos;
int32_t metaState;
int32_t buttonState;
};
@@ -952,6 +985,7 @@
virtual FocusEvent* createFocusEvent() = 0;
virtual CaptureEvent* createCaptureEvent() = 0;
virtual DragEvent* createDragEvent() = 0;
+ virtual TouchModeEvent* createTouchModeEvent() = 0;
};
/*
@@ -968,6 +1002,7 @@
virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
virtual CaptureEvent* createCaptureEvent() override { return &mCaptureEvent; }
virtual DragEvent* createDragEvent() override { return &mDragEvent; }
+ virtual TouchModeEvent* createTouchModeEvent() override { return &mTouchModeEvent; }
private:
KeyEvent mKeyEvent;
@@ -975,6 +1010,7 @@
FocusEvent mFocusEvent;
CaptureEvent mCaptureEvent;
DragEvent mDragEvent;
+ TouchModeEvent mTouchModeEvent;
};
/*
@@ -990,6 +1026,7 @@
virtual FocusEvent* createFocusEvent() override;
virtual CaptureEvent* createCaptureEvent() override;
virtual DragEvent* createDragEvent() override;
+ virtual TouchModeEvent* createTouchModeEvent() override;
void recycle(InputEvent* event);
@@ -1001,6 +1038,7 @@
std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
std::queue<std::unique_ptr<CaptureEvent>> mCaptureEventPool;
std::queue<std::unique_ptr<DragEvent>> mDragEventPool;
+ std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
};
/*
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7f0324a..22aae19 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -84,6 +84,9 @@
GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR,
GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED,
SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION,
+
+ ftl_first = ACCELEROMETER,
+ ftl_last = SIGNIFICANT_MOTION
};
enum class InputDeviceSensorAccuracy : int32_t {
@@ -105,6 +108,8 @@
PLAYER_ID = 1,
RGB = 2,
MULTI_COLOR = 3,
+
+ ftl_last = MULTI_COLOR
};
struct InputDeviceSensorInfo {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index a790b56..edcb615 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -72,6 +72,9 @@
CAPTURE,
DRAG,
TIMELINE,
+ TOUCH_MODE,
+
+ ftl_last = TOUCH_MODE
};
struct Header {
@@ -111,7 +114,7 @@
struct Motion {
int32_t eventId;
- uint32_t empty1;
+ uint32_t pointerCount;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -126,20 +129,22 @@
uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
- float dsdx;
- float dtdx;
- float dtdy;
- float dsdy;
- float tx;
- float ty;
+ float dsdx; // Begin window transform
+ float dtdx; //
+ float dtdy; //
+ float dsdy; //
+ float tx; //
+ float ty; // End window transform
float xPrecision;
float yPrecision;
float xCursorPosition;
float yCursorPosition;
- uint32_t displayOrientation;
- int32_t displayWidth;
- int32_t displayHeight;
- uint32_t pointerCount;
+ float dsdxRaw; // Begin raw transform
+ float dtdxRaw; //
+ float dtdyRaw; //
+ float dsdyRaw; //
+ float txRaw; //
+ float tyRaw; // End raw transform
/**
* The "pointers" field must be the last field of the struct InputMessage.
* When we send the struct InputMessage across the socket, we are not
@@ -173,10 +178,9 @@
struct Focus {
int32_t eventId;
- // The following 3 fields take up 4 bytes total
+ // The following 2 fields take up 4 bytes total
bool hasFocus;
- bool inTouchMode;
- uint8_t empty[2];
+ uint8_t empty[3];
inline size_t size() const { return sizeof(Focus); }
} focus;
@@ -206,6 +210,15 @@
inline size_t size() const { return sizeof(Timeline); }
} timeline;
+
+ struct TouchMode {
+ int32_t eventId;
+ // The following 2 fields take up 4 bytes total
+ bool isInTouchMode;
+ uint8_t empty[3];
+
+ inline size_t size() const { return sizeof(TouchMode); }
+ } touchMode;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -355,9 +368,8 @@
int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, uint32_t displayOrientation,
- int32_t displayWidth, int32_t displayHeight, nsecs_t downTime,
- nsecs_t eventTime, uint32_t pointerCount,
+ float yCursorPosition, const ui::Transform& rawTransform,
+ nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
@@ -368,7 +380,7 @@
* Returns DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode);
+ status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus);
/* Publishes a capture event to the input channel.
*
@@ -388,6 +400,15 @@
*/
status_t publishDragEvent(uint32_t seq, int32_t eventId, float x, float y, bool isExiting);
+ /* Publishes a touch mode event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishTouchModeEvent(uint32_t seq, int32_t eventId, bool isInTouchMode);
+
struct Finished {
uint32_t seq;
bool handled;
@@ -658,6 +679,7 @@
static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
+ static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h
index 37a476e..7e6c515 100644
--- a/include/private/surface_control_private.h
+++ b/include/private/surface_control_private.h
@@ -29,8 +29,8 @@
/**
* Callback to be notified when surface stats for a specific surface control are available.
*/
-typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context,
- ASurfaceControl* control, ASurfaceControlStats* stats);
+typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, int32_t id,
+ ASurfaceControlStats* stats);
/**
* Registers a callback to be invoked when surface stats from a specific surface are available.
@@ -42,7 +42,7 @@
*
* \param func The callback to be invoked when surface stats are available.
*/
-void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id, void* context,
ASurfaceControl_SurfaceStatsListener func);
/**
diff --git a/libs/battery/Android.bp b/libs/battery/Android.bp
new file mode 100644
index 0000000..c860324
--- /dev/null
+++ b/libs/battery/Android.bp
@@ -0,0 +1,37 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library {
+ name: "libbattery",
+ srcs: [
+ "LongArrayMultiStateCounter.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+ export_include_dirs: ["."],
+}
+
+cc_test {
+ name: "libbattery_test",
+ srcs: [
+ "MultiStateCounterTest.cpp",
+ "LongArrayMultiStateCounterTest.cpp",
+ ],
+ static_libs: ["libbattery"],
+ shared_libs: [
+ "liblog",
+ ],
+ cflags: [
+ "-Werror",
+ "-Wall",
+ "-Wextra",
+ ],
+}
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
new file mode 100644
index 0000000..125cfaf
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 "LongArrayMultiStateCounter.h"
+#include <log/log.h>
+
+namespace android {
+namespace battery {
+
+template <>
+bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
+ const std::vector<uint64_t>& newValue,
+ std::vector<uint64_t>* outValue) const {
+ size_t size = previousValue.size();
+ if (newValue.size() != size) {
+ ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
+ return false;
+ }
+
+ bool is_delta_valid = true;
+ for (int i = size - 1; i >= 0; i--) {
+ if (newValue[i] >= previousValue[i]) {
+ (*outValue)[i] = newValue[i] - previousValue[i];
+ } else {
+ (*outValue)[i] = 0;
+ is_delta_valid = false;
+ }
+ }
+ return is_delta_valid;
+}
+
+template <>
+void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
+ const std::vector<uint64_t>& value2, const uint64_t numerator,
+ const uint64_t denominator) const {
+ if (numerator != denominator) {
+ for (int i = value2.size() - 1; i >= 0; i--) {
+ // The caller ensures that denominator != 0
+ (*value1)[i] += value2[i] * numerator / denominator;
+ }
+ } else {
+ for (int i = value2.size() - 1; i >= 0; i--) {
+ (*value1)[i] += value2[i];
+ }
+ }
+}
+
+template <>
+std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
+ std::stringstream s;
+ s << "{";
+ bool first = true;
+ for (uint64_t n : v) {
+ if (!first) {
+ s << ", ";
+ }
+ s << n;
+ first = false;
+ }
+ s << "}";
+ return s.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/ui/Size.cpp b/libs/battery/LongArrayMultiStateCounter.h
similarity index 63%
copy from libs/ui/Size.cpp
copy to libs/battery/LongArrayMultiStateCounter.h
index d2996d1..f3439f6 100644
--- a/libs/ui/Size.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -1,5 +1,6 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +15,15 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+#pragma once
-namespace android::ui {
+#include <vector>
+#include "MultiStateCounter.h"
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+namespace android {
+namespace battery {
-} // namespace android::ui
+typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
new file mode 100644
index 0000000..e4e6b2a
--- /dev/null
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <gtest/gtest.h>
+#include "LongArrayMultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+class LongArrayMultiStateCounterTest : public testing::Test {};
+
+TEST_F(LongArrayMultiStateCounterTest, stateChange) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ // Time was split in half between the two states, so the counts will be split 50:50 too
+ EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
+ EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, accumulation) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+ testCounter.setState(0, 4000);
+ testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+
+ // The first delta is split 50:50:
+ // 0: {50, 100, 150, 200}
+ // 1: {50, 100, 150, 200}
+ // The second delta is split 4:1
+ // 0: {80, 80, 80, 80}
+ // 1: {20, 20, 20, 20}
+ EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
+ EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+}
+
+TEST_F(LongArrayMultiStateCounterTest, toString) {
+ LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
+ testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+ testCounter.setState(0, 1000);
+ testCounter.setState(1, 2000);
+ testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+
+ EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
new file mode 100644
index 0000000..0caf005
--- /dev/null
+++ b/libs/battery/MultiStateCounter.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <inttypes.h>
+#include <log/log.h>
+#include <time.h>
+#include <sstream>
+#include <string>
+
+/**
+ * An object that can track changes of some value over time, taking into account an additional
+ * dimension: the object's state. As the tracked value changes, the deltas are distributed
+ * among the object states in accordance with the time spent in those states.
+ */
+namespace android {
+namespace battery {
+
+typedef uint16_t state_t;
+
+template <class T>
+class MultiStateCounter {
+ uint16_t stateCount;
+ state_t currentState;
+ time_t lastStateChangeTimestamp;
+ T emptyValue;
+ T lastValue;
+ time_t lastUpdateTimestamp;
+ T deltaValue;
+ bool isEnabled;
+
+ struct State {
+ time_t timeInStateSinceUpdate;
+ T counter;
+ };
+
+ State* states;
+
+public:
+ MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+
+ virtual ~MultiStateCounter();
+
+ void setEnabled(bool enabled, time_t timestamp);
+
+ void setState(state_t state, time_t timestamp);
+
+ void setValue(state_t state, const T& value);
+
+ /**
+ * Updates the value by distributing the delta from the previously set value
+ * among states according to their respective time-in-state.
+ * Returns the delta from the previously set value.
+ */
+ const T& updateValue(const T& value, time_t timestamp);
+
+ /**
+ * Updates the value by distributing the specified increment among states according
+ * to their respective time-in-state.
+ */
+ void incrementValue(const T& increment, time_t timestamp);
+
+ /**
+ * Adds the specified increment to the value for the current state, without affecting
+ * the last updated value or timestamp. Ignores partial time-in-state: the entirety of
+ * the increment is given to the current state.
+ */
+ void addValue(const T& increment);
+
+ void reset();
+
+ uint16_t getStateCount();
+
+ const T& getCount(state_t state);
+
+ std::string toString();
+
+private:
+ /**
+ * Subtracts previousValue from newValue and returns the result in outValue.
+ * Returns true iff the combination of previousValue and newValue is valid
+ * (newValue >= prevValue)
+ */
+ bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+
+ /**
+ * Adds value2 to value1 and stores the result in value1. Denominator is
+ * guaranteed to be non-zero.
+ */
+ void add(T* value1, const T& value2, const uint64_t numerator,
+ const uint64_t denominator) const;
+
+ std::string valueToString(const T& value) const;
+};
+
+// ---------------------- MultiStateCounter Implementation -------------------------
+// Since MultiStateCounter is a template, the implementation must be inlined.
+
+template <class T>
+MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+ : stateCount(stateCount),
+ currentState(0),
+ lastStateChangeTimestamp(-1),
+ emptyValue(emptyValue),
+ lastValue(emptyValue),
+ lastUpdateTimestamp(-1),
+ deltaValue(emptyValue),
+ isEnabled(true) {
+ states = new State[stateCount];
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+MultiStateCounter<T>::~MultiStateCounter() {
+ delete[] states;
+};
+
+template <class T>
+void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+ if (enabled == isEnabled) {
+ return;
+ }
+
+ if (!enabled) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+ }
+
+ isEnabled = enabled;
+
+ if (lastStateChangeTimestamp >= 0) {
+ lastStateChangeTimestamp = timestamp;
+ }
+}
+
+template <class T>
+void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+ if (isEnabled && lastStateChangeTimestamp >= 0) {
+ if (timestamp >= lastStateChangeTimestamp) {
+ states[currentState].timeInStateSinceUpdate += timestamp - lastStateChangeTimestamp;
+ } else {
+ ALOGE("setState is called with an earlier timestamp: %lu, previous timestamp: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastStateChangeTimestamp);
+ // The accumulated durations have become unreliable. For example, if the timestamp
+ // sequence was 1000, 2000, 1000, 3000, if we accumulated the positive deltas,
+ // we would get 4000, which is greater than (last - first). This could lead to
+ // counts exceeding 100%.
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ }
+ currentState = state;
+ lastStateChangeTimestamp = timestamp;
+}
+
+template <class T>
+void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+ states[state].counter = value;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
+ T* returnValue = &emptyValue;
+
+ // If the counter is disabled, we ignore the update, except when the counter got disabled after
+ // the previous update, in which case we still need to pick up the residual delta.
+ if (isEnabled || lastUpdateTimestamp < lastStateChangeTimestamp) {
+ // Confirm the current state for the side-effect of updating the time-in-state
+ // counter for the current state.
+ setState(currentState, timestamp);
+
+ if (lastUpdateTimestamp >= 0) {
+ if (timestamp > lastUpdateTimestamp) {
+ if (delta(lastValue, value, &deltaValue)) {
+ returnValue = &deltaValue;
+ time_t timeSinceUpdate = timestamp - lastUpdateTimestamp;
+ for (int i = 0; i < stateCount; i++) {
+ time_t timeInState = states[i].timeInStateSinceUpdate;
+ if (timeInState) {
+ add(&states[i].counter, deltaValue, timeInState, timeSinceUpdate);
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else {
+ std::stringstream str;
+ str << "updateValue is called with a value " << valueToString(value)
+ << ", which is lower than the previous value " << valueToString(lastValue)
+ << "\n";
+ ALOGE("%s", str.str().c_str());
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ } else if (timestamp < lastUpdateTimestamp) {
+ ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
+ (unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
+ }
+ }
+ }
+ lastValue = value;
+ lastUpdateTimestamp = timestamp;
+ return *returnValue;
+}
+
+template <class T>
+void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+ T newValue = lastValue;
+ add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
+ updateValue(newValue, timestamp);
+}
+
+template <class T>
+void MultiStateCounter<T>::addValue(const T& value) {
+ if (!isEnabled) {
+ return;
+ }
+ add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
+}
+
+template <class T>
+void MultiStateCounter<T>::reset() {
+ lastStateChangeTimestamp = -1;
+ lastUpdateTimestamp = -1;
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ states[i].counter = emptyValue;
+ }
+}
+
+template <class T>
+uint16_t MultiStateCounter<T>::getStateCount() {
+ return stateCount;
+}
+
+template <class T>
+const T& MultiStateCounter<T>::getCount(state_t state) {
+ return states[state].counter;
+}
+
+template <class T>
+std::string MultiStateCounter<T>::toString() {
+ std::stringstream str;
+ str << "[";
+ for (int i = 0; i < stateCount; i++) {
+ if (i != 0) {
+ str << ", ";
+ }
+ str << i << ": " << valueToString(states[i].counter);
+ if (states[i].timeInStateSinceUpdate > 0) {
+ str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
+ }
+ }
+ str << "]";
+ if (lastUpdateTimestamp >= 0) {
+ str << " updated: " << lastUpdateTimestamp;
+ }
+ if (lastStateChangeTimestamp >= 0) {
+ str << " currentState: " << currentState;
+ if (lastStateChangeTimestamp > lastUpdateTimestamp) {
+ str << " stateChanged: " << lastStateChangeTimestamp;
+ }
+ } else {
+ str << " currentState: none";
+ }
+ if (!isEnabled) {
+ str << " disabled";
+ }
+ return str.str();
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
new file mode 100644
index 0000000..cb11a54
--- /dev/null
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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 <gtest/gtest.h>
+#include "MultiStateCounter.h"
+
+namespace android {
+namespace battery {
+
+typedef MultiStateCounter<double> DoubleMultiStateCounter;
+
+template <>
+bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
+ double* outValue) const {
+ *outValue = newValue - previousValue;
+ return *outValue >= 0;
+}
+
+template <>
+void DoubleMultiStateCounter::add(double* value1, const double& value2, const uint64_t numerator,
+ const uint64_t denominator) const {
+ if (numerator != denominator) {
+ // The caller ensures that denominator != 0
+ *value1 += value2 * numerator / denominator;
+ } else {
+ *value1 += value2;
+ }
+}
+
+template <>
+std::string DoubleMultiStateCounter::valueToString(const double& v) const {
+ return std::to_string(v);
+}
+
+class MultiStateCounterTest : public testing::Test {};
+
+TEST_F(MultiStateCounterTest, constructor) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ double delta = testCounter.updateValue(3.14, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+ EXPECT_DOUBLE_EQ(3.14, delta);
+}
+
+TEST_F(MultiStateCounterTest, stateChange) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(2, 1000);
+ testCounter.updateValue(6.0, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, setEnabled) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setEnabled(false, 1000);
+ testCounter.setState(2, 2000);
+ testCounter.updateValue(6.0, 3000);
+
+ // In state 1: accumulated 1000 before disabled, that's 6.0 * 1000/3000 = 2.0
+ // In state 2: 0, since it is still disabled
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Should have no effect since the counter is disabled
+ testCounter.setState(0, 3500);
+
+ // Should have no effect since the counter is disabled
+ testCounter.updateValue(10.0, 4000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ testCounter.setState(2, 4500);
+
+ // Enable the counter to partially accumulate deltas for the current state, 2
+ testCounter.setEnabled(true, 5000);
+ testCounter.setEnabled(false, 6000);
+ testCounter.setEnabled(true, 7000);
+ testCounter.updateValue(20.0, 8000);
+
+ // The delta is 10.0 over 5000-3000=2000.
+ // Counter has been enabled in state 2 for (6000-5000)+(8000-7000) = 2000,
+ // so its share is (20.0-10.0) * 2000/(8000-4000) = 5.0
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(5.0, testCounter.getCount(2));
+
+ testCounter.reset();
+ testCounter.setState(0, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 2000);
+ testCounter.setEnabled(false, 3000);
+ testCounter.updateValue(200, 5000);
+
+ // 200 over 5000 = 40 per second
+ // Counter was in state 0 from 0 to 2000, so 2 sec, so the count should be 40 * 2 = 80
+ // It stayed in state 1 from 2000 to 3000, at which point the counter was disabled,
+ // so the count for state 1 should be 40 * 1 = 40.
+ // The remaining 2 seconds from 3000 to 5000 don't count because the counter was disabled.
+ EXPECT_DOUBLE_EQ(80.0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(40.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, reset) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.updateValue(2.72, 3000);
+
+ testCounter.reset();
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+
+ // Assert that we can still continue accumulating after a reset
+ testCounter.updateValue(0, 4000);
+ testCounter.updateValue(3.14, 5000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(3.14, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_setState) {
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(2, 2000);
+
+ // Time moves back
+ testCounter.setState(1, 1000);
+ testCounter.updateValue(6.0, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+
+ // We were in state 1 from 0 to 2000, which was erased because the time moved back.
+ // Then from 1000 to 3000, so we expect the count to be 6 * (2000/3000)
+ EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(1));
+
+ // No time was effectively accumulated for state 2, because the timestamp moved back
+ // while we were in state 2.
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(2));
+}
+
+TEST_F(MultiStateCounterTest, timeAdjustment_updateValue) {
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ // Time moves back. The delta over the negative interval from 2000 to 1000 is ignored
+ testCounter.updateValue(8.0, 1000);
+ double delta = testCounter.updateValue(11.0, 3000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(11.0-8.0) // For the period 1000-3000
+ EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 11.0-8.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, updateValue_nonmonotonic) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ // Value goes down. The negative delta from 6.0 to 4.0 is ignored
+ testCounter.updateValue(4.0, 3000);
+
+ // Value goes up again. The positive delta from 4.0 to 7.0 is accumulated.
+ double delta = testCounter.updateValue(7.0, 4000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(7.0-4.0) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 7.0-4.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, incrementValue) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.setState(1, 3000);
+
+ testCounter.incrementValue(8.0, 6000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(8.0 * 0.25) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(8.0, testCounter.getCount(0));
+
+ // 0 // For the period 0-3000
+ // +(8.0 * 0.75) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(6.0, testCounter.getCount(1));
+}
+
+TEST_F(MultiStateCounterTest, addValue) {
+ DoubleMultiStateCounter testCounter(1, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.addValue(8.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+
+ testCounter.setEnabled(false, 3000);
+ testCounter.addValue(888.0);
+
+ EXPECT_DOUBLE_EQ(14.0, testCounter.getCount(0));
+}
+
+TEST_F(MultiStateCounterTest, toString) {
+ DoubleMultiStateCounter testCounter(2, 0);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000] currentState: none", testCounter.toString().c_str());
+
+ testCounter.updateValue(0, 0);
+ testCounter.setState(1, 0);
+ testCounter.setState(1, 2000);
+ EXPECT_STREQ("[0: 0.000000, 1: 0.000000 timeInStateSinceUpdate: 2000]"
+ " updated: 0 currentState: 1 stateChanged: 2000",
+ testCounter.toString().c_str());
+
+ testCounter.updateValue(3.14, 3000);
+
+ EXPECT_STREQ("[0: 0.000000, 1: 3.140000] updated: 3000 currentState: 1",
+ testCounter.toString().c_str());
+}
+
+} // namespace battery
+} // namespace android
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7d14315..f5abb85 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -228,10 +228,8 @@
"android.gfx.tests.IIPCTest",
"android.gfx.tests.ISafeInterfaceTest",
"android.graphicsenv.IGpuService",
- "android.gui.DisplayEventConnection",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.IRegionSamplingListener",
"android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 565542b..4163897 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -289,7 +289,7 @@
/**
* Built-in transaction for all binder objects. This sends a transaction that will immediately
* return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a
- * sanity check.
+ * consistency check.
*
* Available since API level 29.
*
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 2524c5f..5a80ad0 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -15,11 +15,13 @@
},
srcs: [
"Flags_test.cpp",
+ "cast_test.cpp",
+ "enum_test.cpp",
"future_test.cpp",
- "NamedEnum_test.cpp",
"small_map_test.cpp",
"small_vector_test.cpp",
"static_vector_test.cpp",
+ "string_test.cpp",
],
cflags: [
"-Wall",
diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp
index 8c00b52..d241fa2 100644
--- a/libs/ftl/Flags_test.cpp
+++ b/libs/ftl/Flags_test.cpp
@@ -23,7 +23,7 @@
using namespace android::flag_operators;
-enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
+enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 };
TEST(Flags, Test) {
Flags<TestFlags> flags = TestFlags::ONE;
@@ -165,7 +165,7 @@
TEST(Flags, String_UnknownValues) {
auto flags = Flags<TestFlags>(0b1011);
- ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008");
+ ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000");
}
TEST(FlagsIterator, IteratesOverAllFlags) {
@@ -210,18 +210,4 @@
ASSERT_EQ(++iter, flags.end());
}
-TEST(FlagNames, RuntimeFlagName) {
- TestFlags f = TestFlags::ONE;
- ASSERT_EQ(flag_name(f), "ONE");
-}
-
-TEST(FlagNames, RuntimeUnknownFlagName) {
- TestFlags f = static_cast<TestFlags>(0x8);
- ASSERT_EQ(flag_name(f), std::nullopt);
-}
-
-TEST(FlagNames, CompileTimeFlagName) {
- static_assert(flag_name<TestFlags::TWO>() == "TWO");
-}
-
-} // namespace android::test
\ No newline at end of file
+} // namespace android::test
diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp
deleted file mode 100644
index dff2b8a..0000000
--- a/libs/ftl/NamedEnum_test.cpp
+++ /dev/null
@@ -1,101 +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 <gtest/gtest.h>
-#include <ftl/NamedEnum.h>
-
-namespace android {
-
-// Test enum class maximum enum value smaller than default maximum of 8.
-enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
-// Big enum contains enum values greater than default maximum of 8.
-enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };
-
-// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
-template <>
-constexpr size_t NamedEnum::max<TestBigEnums> = 16;
-
-namespace test {
-using android::TestBigEnums;
-using android::TestEnums;
-
-TEST(NamedEnum, RuntimeNamedEnum) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::enum_name(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::enum_name(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
-}
-
-// Test big enum
-TEST(NamedEnum, RuntimeBigNamedEnum) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeNamedEnumAsString) {
- TestEnums e = TestEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestEnums::ONE;
- ASSERT_EQ(NamedEnum::string(e), "ONE");
-
- e = TestEnums::THREE;
- ASSERT_EQ(NamedEnum::string(e), "THREE");
-
- e = TestEnums::SEVEN;
- ASSERT_EQ(NamedEnum::string(e), "SEVEN");
-}
-
-TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
- TestBigEnums e = TestBigEnums::ZERO;
- ASSERT_EQ(NamedEnum::string(e), "ZERO");
-
- e = TestBigEnums::FIFTEEN;
- ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnum) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
-}
-
-TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
- TestEnums e = static_cast<TestEnums>(0x5);
- ASSERT_EQ(NamedEnum::string(e), "05");
- e = static_cast<TestEnums>(0x9);
- ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009");
-}
-
-TEST(NamedEnum, CompileTimeFlagName) {
- static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
- static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
-}
-
-} // namespace test
-
-} // namespace android
diff --git a/libs/ftl/cast_test.cpp b/libs/ftl/cast_test.cpp
new file mode 100644
index 0000000..2abcb8f
--- /dev/null
+++ b/libs/ftl/cast_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#include <ftl/cast.h>
+#include <gtest/gtest.h>
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+
+namespace android::test {
+
+using ftl::cast_safety;
+using ftl::CastSafety;
+
+template <typename T>
+constexpr T min = std::numeric_limits<T>::lowest();
+
+template <typename T>
+constexpr T max = std::numeric_limits<T>::max();
+
+template <typename T>
+constexpr T inf = std::numeric_limits<T>::infinity();
+
+template <typename T>
+constexpr T NaN = std::numeric_limits<T>::quiet_NaN();
+
+// Keep in sync with example usage in header file.
+
+static_assert(cast_safety<uint8_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(128u) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(static_cast<float>(INT32_MAX)) == CastSafety::kOverflow);
+
+static_assert(cast_safety<float>(-DBL_MAX) == CastSafety::kUnderflow);
+
+// Unsigned to unsigned.
+
+static_assert(cast_safety<uint8_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(max<uint64_t>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint8_t>(static_cast<uint32_t>(max<uint8_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Unsigned to signed.
+
+static_assert(cast_safety<int16_t>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<int16_t>(max<uint16_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<uint64_t>(max<int64_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Signed to unsigned.
+
+static_assert(cast_safety<uint16_t>(0) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int8_t>) == CastSafety::kSafe);
+static_assert(cast_safety<uint16_t>(max<int16_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint32_t>(-1) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) - 1) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<int64_t>(max<uint32_t>) + 1) ==
+ CastSafety::kOverflow);
+
+// Signed to signed.
+
+static_assert(cast_safety<int8_t>(-129) == CastSafety::kUnderflow);
+static_assert(cast_safety<int8_t>(-128) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(127) == CastSafety::kSafe);
+static_assert(cast_safety<int8_t>(128) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<int64_t>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int16_t>(min<int32_t>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<int64_t>) == CastSafety::kOverflow);
+
+// Float to float.
+
+static_assert(cast_safety<double>(max<float>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(min<float>) == CastSafety::kSafe);
+
+static_assert(cast_safety<float>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<float>(max<double>) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToFloat) {
+ EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(min<float>), min<double>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<float>(std::nexttoward(static_cast<double>(max<float>), max<double>)),
+ CastSafety::kOverflow);
+}
+
+// Unsigned to float.
+
+static_assert(cast_safety<float>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<uint64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(0u) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<uint64_t>) == CastSafety::kSafe);
+
+// Signed to float.
+
+static_assert(cast_safety<float>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<float>(max<int64_t>) == CastSafety::kSafe);
+
+static_assert(cast_safety<double>(min<int64_t>) == CastSafety::kSafe);
+static_assert(cast_safety<double>(max<int64_t>) == CastSafety::kSafe);
+
+// Float to unsigned.
+
+static_assert(cast_safety<uint32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(max<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(-.1f) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint16_t>(-inf<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint32_t>(inf<float>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(NaN<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<float>(max<uint32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint32_t>(static_cast<double>(max<uint32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<uint64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<uint64_t>(max<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(-.1) == CastSafety::kUnderflow);
+
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<float>(max<uint64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<uint64_t>(static_cast<double>(max<uint64_t>)) == CastSafety::kOverflow);
+
+// Float to signed.
+
+static_assert(cast_safety<int32_t>(0.f) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(min<float>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(max<float>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int16_t>(-inf<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int32_t>(inf<double>) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(NaN<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int32_t>(static_cast<float>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<float>(max<int32_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int32_t>(static_cast<double>(min<int32_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int32_t>(static_cast<double>(max<int32_t>)) == CastSafety::kSafe);
+
+static_assert(cast_safety<int64_t>(0.0) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(min<double>) == CastSafety::kUnderflow);
+static_assert(cast_safety<int64_t>(max<double>) == CastSafety::kOverflow);
+
+static_assert(cast_safety<int64_t>(static_cast<float>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<float>(max<int64_t>)) == CastSafety::kOverflow);
+static_assert(cast_safety<int64_t>(static_cast<double>(min<int64_t>)) == CastSafety::kSafe);
+static_assert(cast_safety<int64_t>(static_cast<double>(max<int64_t>)) == CastSafety::kOverflow);
+
+TEST(CastSafety, FloatToSigned) {
+ constexpr int32_t kMax = ftl::details::safe_limits<int32_t, float>::max();
+ static_assert(kMax == 2'147'483'520);
+ EXPECT_EQ(kMax, static_cast<int32_t>(std::nexttowardf(max<int32_t>, 0)));
+
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, 0)), CastSafety::kSafe);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, 0)), CastSafety::kSafe);
+
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(min<int32_t>, min<float>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<int32_t>(std::nexttowardf(max<int32_t>, max<float>)),
+ CastSafety::kOverflow);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(min<int64_t>, min<double>)),
+ CastSafety::kUnderflow);
+ EXPECT_EQ(cast_safety<int64_t>(std::nexttoward(max<int64_t>, max<double>)),
+ CastSafety::kOverflow);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp
new file mode 100644
index 0000000..1fd43ab
--- /dev/null
+++ b/libs/ftl/enum_test.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+namespace {
+
+enum class E { A, B, C, F = 5, ftl_last = F };
+
+static_assert(ftl::enum_begin_v<E> == E::A);
+static_assert(ftl::enum_last_v<E> == E::F);
+static_assert(ftl::enum_size_v<E> == 6);
+
+static_assert(ftl::enum_name<E::B>() == "B");
+static_assert(ftl::enum_name<E::ftl_last>() == "F");
+static_assert(ftl::enum_name(E::C).value_or("?") == "C");
+static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
+
+enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
+
+static_assert(ftl::enum_begin_v<F> == F{0});
+static_assert(ftl::enum_last_v<F> == F{15});
+static_assert(ftl::enum_size_v<F> == 16);
+
+static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
+static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
+
+// If a scoped enum is unsigned, its implicit range corresponds to its bit indices.
+enum class Flags : std::uint8_t {
+ kNone = 0,
+ kFlag1 = 0b0000'0010,
+ kFlag4 = 0b0001'0000,
+ kFlag7 = 0b1000'0000,
+ kMask = kFlag1 | kFlag4 | kFlag7,
+ kAll = 0b1111'1111
+};
+
+static_assert(ftl::enum_begin_v<Flags> == Flags{0});
+static_assert(ftl::enum_last_v<Flags> == Flags{7});
+static_assert(ftl::enum_size_v<Flags> == 8);
+
+static_assert(ftl::enum_name<Flags::kNone>() == "kNone");
+static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4");
+static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7");
+
+// Though not flags, the enumerators are within the implicit range of bit indices.
+enum class Planet : std::uint8_t {
+ kMercury,
+ kVenus,
+ kEarth,
+ kMars,
+ kJupiter,
+ kSaturn,
+ kUranus,
+ kNeptune
+};
+
+constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1}; // Honorable mention.
+
+static_assert(ftl::enum_begin_v<Planet> == Planet::kMercury);
+static_assert(ftl::enum_last_v<Planet> == Planet::kNeptune);
+static_assert(ftl::enum_size_v<Planet> == 8);
+
+static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury");
+static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn");
+
+// Unscoped enum must define explicit range, even if the underlying type is fixed.
+enum Temperature : int {
+ kRoom = 20,
+ kFridge = 4,
+ kFreezer = -18,
+
+ ftl_first = kFreezer,
+ ftl_last = kRoom
+};
+
+static_assert(ftl::enum_begin_v<Temperature> == kFreezer);
+static_assert(ftl::enum_last_v<Temperature> == kRoom);
+static_assert(ftl::enum_size_v<Temperature> == 39);
+
+static_assert(ftl::enum_name<kFreezer>() == "kFreezer");
+static_assert(ftl::enum_name<kFridge>() == "kFridge");
+static_assert(ftl::enum_name<kRoom>() == "kRoom");
+
+} // namespace
+
+TEST(Enum, Range) {
+ std::string string;
+ for (E v : ftl::enum_range<E>()) {
+ string += ftl::enum_name(v).value_or("?");
+ }
+ EXPECT_EQ(string, "ABC??F");
+}
+
+TEST(Enum, Name) {
+ {
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt);
+ EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt);
+ }
+ {
+ EXPECT_EQ(ftl::enum_name(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_name(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt);
+ EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt);
+ }
+}
+
+TEST(Enum, String) {
+ {
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1");
+ EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7");
+
+ EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0");
+ EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010");
+ EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth");
+ EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune");
+
+ EXPECT_EQ(ftl::enum_string(kPluto), "8");
+ }
+ {
+ EXPECT_EQ(ftl::enum_string(kRoom), "kRoom");
+ EXPECT_EQ(ftl::enum_string(kFridge), "kFridge");
+ EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer");
+
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0");
+ EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100");
+ }
+}
+
+} // namespace android::test
diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp
index 323b9f9..ee650e5 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -18,6 +18,9 @@
#include <gtest/gtest.h>
#include <cctype>
+#include <string>
+
+using namespace std::string_literals;
namespace android::test {
@@ -35,16 +38,19 @@
EXPECT_TRUE(map.contains(123));
- EXPECT_EQ(map.find(42, [](const std::string& s) { return s.size(); }), 3u);
+ EXPECT_EQ(map.get(42, [](const std::string& s) { return s.size(); }), 3u);
- const auto opt = map.find(-1);
+ const auto opt = map.get(-1);
ASSERT_TRUE(opt);
std::string& ref = *opt;
EXPECT_TRUE(ref.empty());
ref = "xyz";
- EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(42, "???")(123, "abc")));
+ map.emplace_or_replace(0, "vanilla", 2u, 3u);
+ EXPECT_TRUE(map.dynamic());
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, "xyz")(0, "nil")(42, "???")(123, "abc")));
}
TEST(SmallMap, Construct) {
@@ -90,42 +96,290 @@
}
}
+TEST(SmallMap, UniqueKeys) {
+ {
+ // Duplicate mappings are discarded.
+ const SmallMap map = ftl::init::map<int, float>(1)(2)(3)(2)(3)(1)(3)(2)(1);
+
+ EXPECT_EQ(map.size(), 3u);
+ EXPECT_EQ(map.max_size(), 9u);
+
+ using Map = decltype(map);
+ EXPECT_EQ(map, Map(ftl::init::map(1, 0.f)(2, 0.f)(3, 0.f)));
+ }
+ {
+ // Duplicate mappings may be reordered.
+ const SmallMap map = ftl::init::map('a', 'A')(
+ 'b', 'B')('b')('b')('c', 'C')('a')('d')('c')('e', 'E')('d', 'D')('a')('f', 'F');
+
+ EXPECT_EQ(map.size(), 6u);
+ EXPECT_EQ(map.max_size(), 12u);
+
+ using Map = decltype(map);
+ EXPECT_EQ(map, Map(ftl::init::map('a', 'A')('b', 'B')('c', 'C')('d', 'D')('e', 'E')('f', 'F')));
+ }
+}
+
TEST(SmallMap, Find) {
{
// Constant reference.
- const ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ const SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
- const auto opt = map.find('b');
+ const auto opt = map.get('b');
EXPECT_EQ(opt, 'B');
const char d = 'D';
- const auto ref = map.find('d').value_or(std::cref(d));
+ const auto ref = map.get('d').value_or(std::cref(d));
EXPECT_EQ(ref.get(), 'D');
}
{
// Mutable reference.
- ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
+ SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C');
- const auto opt = map.find('c');
+ const auto opt = map.get('c');
EXPECT_EQ(opt, 'C');
char d = 'd';
- const auto ref = map.find('d').value_or(std::ref(d));
+ const auto ref = map.get('d').value_or(std::ref(d));
ref.get() = 'D';
EXPECT_EQ(d, 'D');
}
{
// Constant unary operation.
- const ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
- EXPECT_EQ(map.find('c', [](char c) { return std::toupper(c); }), 'Z');
+ const SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_EQ(map.get('c', [](char c) { return std::toupper(c); }), 'Z');
}
{
// Mutable unary operation.
- ftl::SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
- EXPECT_TRUE(map.find('c', [](char& c) { c = std::toupper(c); }));
+ SmallMap map = ftl::init::map('a', 'x')('b', 'y')('c', 'z');
+ EXPECT_TRUE(map.get('c', [](char& c) { c = std::toupper(c); }));
EXPECT_EQ(map, SmallMap(ftl::init::map('c', 'Z')('b', 'y')('a', 'x')));
}
}
+TEST(SmallMap, TryEmplace) {
+ SmallMap<int, std::string, 3> map;
+ using Pair = decltype(map)::value_type;
+
+ {
+ const auto [it, ok] = map.try_emplace(123, "abc");
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(123, "abc"s));
+ }
+ {
+ const auto [it, ok] = map.try_emplace(42, 3u, '?');
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(42, "???"s));
+ }
+ {
+ const auto [it, ok] = map.try_emplace(-1);
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(-1, std::string()));
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // Insertion fails if mapping exists.
+ const auto [it, ok] = map.try_emplace(42, "!!!");
+ EXPECT_FALSE(ok);
+ EXPECT_EQ(*it, Pair(42, "???"));
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ // Insertion at capacity promotes the map.
+ const auto [it, ok] = map.try_emplace(999, "xyz");
+ ASSERT_TRUE(ok);
+ EXPECT_EQ(*it, Pair(999, "xyz"));
+ EXPECT_TRUE(map.dynamic());
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(-1, ""s)(42, "???"s)(123, "abc"s)(999, "xyz"s)));
+}
+
+namespace {
+
+// The mapped type does not require a copy/move assignment operator.
+struct String {
+ template <typename... Args>
+ String(Args... args) : str(args...) {}
+ const std::string str;
+
+ bool operator==(const String& other) const { return other.str == str; }
+};
+
+} // namespace
+
+TEST(SmallMap, TryReplace) {
+ SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+ using Pair = decltype(map)::value_type;
+
+ {
+ // Replacing fails unless mapping exists.
+ const auto it = map.try_replace(3, "c");
+ EXPECT_EQ(it, map.end());
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from one character.
+ const auto it = map.try_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+ ASSERT_NE(it, map.end());
+ EXPECT_EQ(*it, Pair(2, "b"));
+ }
+
+ EXPECT_FALSE(map.dynamic());
+ EXPECT_TRUE(map.try_emplace(3, "abc").second);
+ EXPECT_TRUE(map.try_emplace(4, "d").second);
+ EXPECT_TRUE(map.dynamic());
+
+ {
+ // Replacing fails unless mapping exists.
+ const auto it = map.try_replace(5, "e");
+ EXPECT_EQ(it, map.end());
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(3);
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from substring.
+ const auto it = map.try_replace(3, ref->get().str, 2u, 1u);
+ ASSERT_NE(it, map.end());
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, EmplaceOrReplace) {
+ SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B");
+ using Pair = decltype(map)::value_type;
+
+ {
+ // New mapping is emplaced.
+ const auto [it, emplace] = map.emplace_or_replace(3, "c");
+ EXPECT_TRUE(emplace);
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(2, [](const auto& s) { return s.str[0]; });
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from one character.
+ const auto [it, emplace] = map.emplace_or_replace(2, 1u, static_cast<char>(std::tolower(*ref)));
+ EXPECT_FALSE(emplace);
+ EXPECT_EQ(*it, Pair(2, "b"));
+ }
+
+ EXPECT_FALSE(map.dynamic());
+ EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace.
+ EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace.
+ EXPECT_TRUE(map.dynamic());
+
+ {
+ // New mapping is emplaced.
+ const auto [it, emplace] = map.emplace_or_replace(5, "e");
+ EXPECT_TRUE(emplace);
+ EXPECT_EQ(*it, Pair(5, "e"));
+ }
+ {
+ // Replacement arguments can refer to the replaced mapping.
+ const auto ref = map.get(3);
+ ASSERT_TRUE(ref);
+
+ // Construct std::string from substring.
+ const auto [it, emplace] = map.emplace_or_replace(3, ref->get().str, 2u, 1u);
+ EXPECT_FALSE(emplace);
+ EXPECT_EQ(*it, Pair(3, "c"));
+ }
+
+ EXPECT_EQ(map, SmallMap(ftl::init::map(5, "e"s)(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s)));
+}
+
+TEST(SmallMap, Erase) {
+ {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3')(4, '4');
+ EXPECT_FALSE(map.dynamic());
+
+ EXPECT_FALSE(map.erase(0)); // Key not found.
+
+ EXPECT_TRUE(map.erase(2));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(1));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(4));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+ EXPECT_TRUE(map.erase(3));
+ EXPECT_FALSE(map.erase(3)); // Key not found.
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+ }
+ {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+ map.try_emplace(4, '4');
+ EXPECT_TRUE(map.dynamic());
+
+ EXPECT_FALSE(map.erase(0)); // Key not found.
+
+ EXPECT_TRUE(map.erase(2));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(1, '1')(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(1));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')(4, '4')));
+
+ EXPECT_TRUE(map.erase(4));
+ EXPECT_EQ(map, SmallMap(ftl::init::map(3, '3')));
+
+ EXPECT_TRUE(map.erase(3));
+ EXPECT_FALSE(map.erase(3)); // Key not found.
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_TRUE(map.dynamic());
+ }
+}
+
+TEST(SmallMap, Clear) {
+ SmallMap map = ftl::init::map(1, '1')(2, '2')(3, '3');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_FALSE(map.dynamic());
+
+ map = ftl::init::map(1, '1')(2, '2')(3, '3');
+ map.try_emplace(4, '4');
+
+ map.clear();
+
+ EXPECT_TRUE(map.empty());
+ EXPECT_TRUE(map.dynamic());
+}
+
+TEST(SmallMap, KeyEqual) {
+ struct KeyEqual {
+ bool operator()(int lhs, int rhs) const { return lhs % 10 == rhs % 10; }
+ };
+
+ SmallMap<int, char, 1, KeyEqual> map;
+
+ EXPECT_TRUE(map.try_emplace(3, '3').second);
+ EXPECT_FALSE(map.try_emplace(13, '3').second);
+
+ EXPECT_TRUE(map.try_emplace(22, '2').second);
+ EXPECT_TRUE(map.contains(42));
+
+ EXPECT_TRUE(map.try_emplace(111, '1').second);
+ EXPECT_EQ(map.get(321), '1');
+
+ map.erase(123);
+ EXPECT_EQ(map, SmallMap(ftl::init::map<int, char, KeyEqual>(1, '1')(2, '2')));
+}
+
} // namespace android::test
diff --git a/libs/ftl/small_vector_test.cpp b/libs/ftl/small_vector_test.cpp
index 3a03e69..4237496 100644
--- a/libs/ftl/small_vector_test.cpp
+++ b/libs/ftl/small_vector_test.cpp
@@ -460,4 +460,34 @@
EXPECT_EQ(0, dead);
}
+TEST(SmallVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_FALSE(counts.dynamic());
+
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_TRUE(counts.dynamic());
+
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+}
+
} // namespace android::test
diff --git a/libs/ftl/static_vector_test.cpp b/libs/ftl/static_vector_test.cpp
index cbe8dff..2de3ad2 100644
--- a/libs/ftl/static_vector_test.cpp
+++ b/libs/ftl/static_vector_test.cpp
@@ -396,4 +396,19 @@
EXPECT_EQ(0, dead);
}
+TEST(StaticVector, Clear) {
+ int live = 0;
+ int dead = 0;
+
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ counts.clear();
+
+ EXPECT_TRUE(counts.empty());
+ EXPECT_EQ(2, live);
+ EXPECT_EQ(0, dead);
+}
+
} // namespace android::test
diff --git a/libs/ftl/string_test.cpp b/libs/ftl/string_test.cpp
new file mode 100644
index 0000000..f3d85c8
--- /dev/null
+++ b/libs/ftl/string_test.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#include <ftl/string.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <sstream>
+#include <type_traits>
+
+namespace android::test {
+
+// Keep in sync with example usage in header file.
+TEST(String, ToChars) {
+ ftl::to_chars_buffer_t<> buffer;
+
+ EXPECT_EQ(ftl::to_chars(buffer, 123u), "123");
+ EXPECT_EQ(ftl::to_chars(buffer, -42, ftl::Radix::kBin), "-0b101010");
+ EXPECT_EQ(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex), "0xcafe");
+ EXPECT_EQ(ftl::to_chars(buffer, '*', ftl::Radix::kHex), "0x2a");
+}
+
+namespace {
+
+template <typename F, typename T>
+void ToCharsTest() {
+ constexpr auto kRadix = F::kRadix;
+
+ using Limits = std::numeric_limits<T>;
+ constexpr auto kMin = Limits::min();
+ constexpr auto kMax = Limits::max();
+ constexpr auto kNeg = static_cast<T>(-42);
+ constexpr auto kPos = static_cast<T>(123);
+
+ ftl::to_chars_buffer_t<T> buffer;
+
+ EXPECT_EQ(ftl::to_chars(buffer, kMin, kRadix), F{}(kMin));
+ EXPECT_EQ(ftl::to_chars(buffer, kMax, kRadix), F{}(kMax));
+ EXPECT_EQ(ftl::to_chars(buffer, kNeg, kRadix), F{}(kNeg));
+ EXPECT_EQ(ftl::to_chars(buffer, kPos, kRadix), F{}(kPos));
+}
+
+template <typename...>
+struct Types {};
+
+template <typename F, typename Types>
+struct ToCharsTests;
+
+template <typename F, typename T, typename... Ts>
+struct ToCharsTests<F, Types<T, Ts...>> {
+ static void test() {
+ ToCharsTest<F, T>();
+ ToCharsTests<F, Types<Ts...>>::test();
+ }
+};
+
+template <typename F>
+struct ToCharsTests<F, Types<>> {
+ static void test() {}
+};
+
+template <typename T, typename U = std::make_unsigned_t<T>>
+U to_unsigned(std::ostream& stream, T v) {
+ if (std::is_same_v<T, U>) return v;
+
+ if (v < 0) {
+ stream << '-';
+ return std::numeric_limits<U>::max() - static_cast<U>(v) + 1;
+ } else {
+ return static_cast<U>(v);
+ }
+}
+
+struct Bin {
+ static constexpr auto kRadix = ftl::Radix::kBin;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ std::ostringstream stream;
+ auto u = to_unsigned(stream, v);
+ stream << "0b";
+
+ if (u == 0) {
+ stream << 0;
+ } else {
+ std::ostringstream digits;
+ do {
+ digits << (u & 1);
+ } while (u >>= 1);
+
+ const auto str = digits.str();
+ std::copy(str.rbegin(), str.rend(), std::ostream_iterator<char>(stream));
+ }
+
+ return stream.str();
+ }
+};
+
+struct Dec {
+ static constexpr auto kRadix = ftl::Radix::kDec;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ return std::to_string(v);
+ }
+};
+
+struct Hex {
+ static constexpr auto kRadix = ftl::Radix::kHex;
+
+ template <typename T>
+ std::string operator()(T v) const {
+ std::ostringstream stream;
+ const auto u = to_unsigned(stream, v);
+ stream << "0x" << std::hex << std::nouppercase;
+ stream << (sizeof(T) == 1 ? static_cast<unsigned>(u) : u);
+ return stream.str();
+ }
+};
+
+using IntegerTypes =
+ Types<char, unsigned char, signed char, std::uint8_t, std::uint16_t, std::uint32_t,
+ std::uint64_t, std::int8_t, std::int16_t, std::int32_t, std::int64_t>;
+
+} // namespace
+
+TEST(String, ToCharsBin) {
+ ToCharsTests<Bin, IntegerTypes>::test();
+
+ {
+ const std::uint8_t x = 0b1111'1111;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "0b11111111");
+ }
+ {
+ const std::int16_t x = -0b1000'0000'0000'0000;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kBin), "-0b1000000000000000");
+ }
+}
+
+TEST(String, ToCharsDec) {
+ ToCharsTests<Dec, IntegerTypes>::test();
+
+ {
+ const std::uint32_t x = UINT32_MAX;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x), "4294967295");
+ }
+ {
+ const std::int32_t x = INT32_MIN;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x), "-2147483648");
+ }
+}
+
+TEST(String, ToCharsHex) {
+ ToCharsTests<Hex, IntegerTypes>::test();
+
+ {
+ const std::uint16_t x = 0xfade;
+ ftl::to_chars_buffer_t<decltype(x)> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, x, ftl::Radix::kHex), "0xfade");
+ }
+ {
+ ftl::to_chars_buffer_t<> buffer;
+ EXPECT_EQ(ftl::to_chars(buffer, INT64_MIN, ftl::Radix::kHex), "-0x8000000000000000");
+ }
+}
+
+} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cda9e19..6afd172 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,7 +33,7 @@
target: {
darwin: {
enabled: false,
- }
+ },
},
vendor_available: true,
@@ -48,18 +48,18 @@
min_sdk_version: "29",
srcs: [
- "Gralloc4.cpp"
+ "Gralloc4.cpp",
],
shared_libs: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index e2f072a..61e6657 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -1134,6 +1134,18 @@
decodeByteVector);
}
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+ hidl_vec<uint8_t>* outSmpte2094_10) {
+ return encodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+ encodeByteVector);
+}
+
+status_t decodeSmpte2094_10(const hidl_vec<uint8_t>& smpte2094_10,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+ return decodeOptionalMetadata(MetadataType_Smpte2094_10, smpte2094_10, outSmpte2094_10,
+ decodeByteVector);
+}
+
status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
hidl_vec<uint8_t>* output) {
return encodeMetadata(metadataType, input, output, encodeInteger);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 2f418ac..deaffad 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -134,6 +134,12 @@
aidl::android::hardware::graphics::common::
StandardMetadataType::SMPTE2094_40)};
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2094_10 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2094_10)};
+
/*---------------------------------------------------------------------------------------------*/
/**
@@ -327,6 +333,11 @@
status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+status_t encodeSmpte2094_10(const std::optional<std::vector<uint8_t>>& smpte2094_10,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2094_10);
+status_t decodeSmpte2094_10(const android::hardware::hidl_vec<uint8_t>& smpte2094_10,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10);
+
/**
* The functions below can be used to encode and decode vendor metadata types.
*/
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index 89cbf4a..94e344f 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -455,6 +455,32 @@
ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
}
+class Gralloc4TestSmpte2094_10
+ : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> {};
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2094_10Params, Gralloc4TestSmpte2094_10,
+ ::testing::Values(
+ std::optional<std::vector<uint8_t>>({}),
+ std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::min() + 1,
+ std::numeric_limits<uint8_t>::min() + 2,
+ std::numeric_limits<uint8_t>::min() + 3,
+ std::numeric_limits<uint8_t>::min() + 4}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::max() - 1,
+ std::numeric_limits<uint8_t>::max() - 2,
+ std::numeric_limits<uint8_t>::max() - 3,
+ std::numeric_limits<uint8_t>::max() - 4}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_10, Smpte2094_10) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(),
+ gralloc4::encodeSmpte2094_10,
+ gralloc4::decodeSmpte2094_10));
+}
+
class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
INSTANTIATE_TEST_CASE_P(
@@ -491,6 +517,7 @@
ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_10({{}}, nullptr));
}
TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
@@ -516,6 +543,7 @@
ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_10(vec, nullptr));
}
TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 514988b..c5dec19 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,7 +55,7 @@
filegroup {
name: "guiconstants_aidl",
srcs: [
- "android/**/DropInputMode.aidl",
+ "android/gui/DropInputMode.aidl",
"android/**/TouchOcclusionMode.aidl",
],
}
@@ -66,11 +66,13 @@
host_supported: true,
srcs: [
":guiconstants_aidl",
+ "android/gui/DisplayInfo.aidl",
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
"android/gui/IWindowInfosListener.aidl",
"android/gui/IWindowInfosReportedListener.aidl",
"android/gui/WindowInfo.aidl",
+ "DisplayInfo.cpp",
"WindowInfo.cpp",
],
@@ -91,7 +93,7 @@
],
aidl: {
- export_aidl_headers: true
+ export_aidl_headers: true,
},
include_dirs: [
@@ -136,8 +138,8 @@
],
aidl: {
- export_aidl_headers: true
- }
+ export_aidl_headers: true,
+ },
}
cc_library_shared {
@@ -178,11 +180,9 @@
"FrameTimelineInfo.cpp",
"GLConsumer.cpp",
"IConsumerListener.cpp",
- "IDisplayEventConnection.cpp",
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
- "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 60c2e2e..85a4b67 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,13 +132,12 @@
}
}
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
- int width, int height, int32_t format)
- : mSurfaceControl(surface),
- mSize(width, height),
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
+ : mSurfaceControl(nullptr),
+ mSize(1, 1),
mRequestedSize(mSize),
- mFormat(format),
- mNextTransaction(nullptr) {
+ mFormat(PIXEL_FORMAT_RGBA_8888),
+ mSyncTransaction(nullptr) {
createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -158,24 +157,21 @@
mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
- mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
- mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
mBufferItemConsumer->setBlastBufferQueue(this);
ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
-
- mTransformHint = mSurfaceControl->getTransformHint();
- mBufferItemConsumer->setTransformHint(mTransformHint);
- SurfaceComposerClient::Transaction()
- .setFlags(surface, layer_state_t::eEnableBackpressure,
- layer_state_t::eEnableBackpressure)
- .setApplyToken(mApplyToken)
- .apply();
+ mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
mNumAcquired = 0;
mNumFrameAvailable = 0;
- BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width,
- height, format, mTransformHint);
+ BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width,
+ mSize.height, mFormat, mTransformHint);
+}
+
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+ int width, int height, int32_t format)
+ : BLASTBufferQueue(name) {
+ update(surface, width, height, format);
}
BLASTBufferQueue::~BLASTBufferQueue() {
@@ -227,12 +223,9 @@
// If the buffer supports scaling, update the frame immediately since the client may
// want to scale the existing buffer to the new size.
mSize = mRequestedSize;
- // We only need to update the scale if we've received at least one buffer. The reason
- // for this is the scale is calculated based on the requested size and buffer size.
- // If there's no buffer, the scale will always be 1.
SurfaceComposerClient::Transaction* destFrameTransaction =
(outTransaction) ? outTransaction : &t;
- if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) {
+ if (mSurfaceControl != nullptr) {
destFrameTransaction->setDestinationFrame(mSurfaceControl,
Rect(0, 0, newSize.getWidth(),
newSize.getHeight()));
@@ -289,10 +282,10 @@
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+ if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
currFrameNumber >= mLastAcquiredFrameNumber) {
mWaitForTransactionCallback = false;
- flushShadowQueueLocked();
+ flushShadowQueue();
}
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback");
@@ -318,9 +311,6 @@
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& stats) {
- std::function<void(int64_t)> transactionCompleteCallback = nullptr;
- uint64_t currFrameNumber = 0;
-
{
std::unique_lock _lock{mMutex};
ATRACE_CALL();
@@ -347,18 +337,6 @@
stat.latchTime,
stat.frameEventStats.dequeueReadyTime);
}
- currFrameNumber = stat.frameEventStats.frameNumber;
-
- if (mTransactionCompleteCallback &&
- currFrameNumber >= mTransactionCompleteFrameNumber) {
- if (currFrameNumber > mTransactionCompleteFrameNumber) {
- BQA_LOGE("transactionCallback received for a newer framenumber=%" PRIu64
- " than expected=%" PRIu64,
- currFrameNumber, mTransactionCompleteFrameNumber);
- }
- transactionCompleteCallback = std::move(mTransactionCompleteCallback);
- mTransactionCompleteFrameNumber = 0;
- }
} else {
BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
@@ -369,10 +347,6 @@
decStrong((void*)transactionCallbackThunk);
}
-
- if (transactionCompleteCallback) {
- transactionCompleteCallback(currFrameNumber);
- }
}
// Unlike transactionCallbackThunk the release buffer callback does not extend the life of the
@@ -380,19 +354,18 @@
// So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer.
// Otherwise, this is a no-op.
static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) {
+ const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount) {
sp<BLASTBufferQueue> blastBufferQueue = context.promote();
if (blastBufferQueue) {
- blastBufferQueue->releaseBufferCallback(id, releaseFence, transformHint,
- currentMaxAcquiredBufferCount);
+ blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
} else {
ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
}
}
-void BLASTBufferQueue::flushShadowQueueLocked() {
- BQA_LOGV("flushShadowQueueLocked");
+void BLASTBufferQueue::flushShadowQueue() {
+ BQA_LOGV("flushShadowQueue");
int numFramesToFlush = mNumFrameAvailable;
while (numFramesToFlush > 0) {
acquireNextBufferLocked(std::nullopt);
@@ -400,25 +373,13 @@
}
}
-void BLASTBufferQueue::flushShadowQueue() {
- std::unique_lock _lock{mMutex};
- flushShadowQueueLocked();
-}
-
-void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
- const sp<Fence>& releaseFence, uint32_t transformHint,
- uint32_t currentMaxAcquiredBufferCount) {
+void BLASTBufferQueue::releaseBufferCallback(
+ const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
+ std::optional<uint32_t> currentMaxAcquiredBufferCount) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str());
- if (mSurfaceControl != nullptr) {
- mTransformHint = transformHint;
- mSurfaceControl->setTransformHint(transformHint);
- mBufferItemConsumer->setTransformHint(mTransformHint);
- BQA_LOGV("updated mTransformHint=%d", mTransformHint);
- }
-
// Calculate how many buffers we need to hold before we release them back
// to the buffer queue. This will prevent higher latency when we are running
// on a lower refresh rate than the max supported. We only do that for EGL
@@ -428,26 +389,21 @@
return it != mSubmitted.end() && it->second.mApi == NATIVE_WINDOW_API_EGL;
}();
+ if (currentMaxAcquiredBufferCount) {
+ mCurrentMaxAcquiredBufferCount = *currentMaxAcquiredBufferCount;
+ }
+
const auto numPendingBuffersToHold =
- isEGL ? std::max(0u, mMaxAcquiredBuffers - currentMaxAcquiredBufferCount) : 0;
+ isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0;
mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence});
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
- const auto releaseBuffer = mPendingRelease.front();
+ const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
- auto it = mSubmitted.find(releaseBuffer.callbackId);
- if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
- releaseBuffer.callbackId.to_string().c_str());
- return;
- }
- mNumAcquired--;
- BQA_LOGV("released %s", id.to_string().c_str());
- mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
- mSubmitted.erase(it);
+ releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
// Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+ // onFrameAvailable handle processing them since it will merge with the syncTransaction.
if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
@@ -459,6 +415,20 @@
mCallbackCV.notify_all();
}
+void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& releaseFence) {
+ auto it = mSubmitted.find(callbackId);
+ if (it == mSubmitted.end()) {
+ BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+ callbackId.to_string().c_str());
+ return;
+ }
+ mNumAcquired--;
+ BQA_LOGV("released %s", callbackId.to_string().c_str());
+ mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+ mSubmitted.erase(it);
+}
+
void BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
ATRACE_CALL();
@@ -530,9 +500,8 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- const bool sizeHasChanged = mRequestedSize != mSize;
+ const bool updateDestinationFrame = mRequestedSize != mSize;
mSize = mRequestedSize;
- const bool updateDestinationFrame = sizeHasChanged || !mLastBufferInfo.hasBuffer;
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
@@ -540,14 +509,13 @@
auto releaseBufferCallback =
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
- std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
- std::placeholders::_4);
- t->setBuffer(mSurfaceControl, buffer, releaseCallbackId, releaseBufferCallback);
+ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+ sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+ t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseCallbackId,
+ releaseBufferCallback);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
- t->setAcquireFence(mSurfaceControl,
- bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
@@ -561,7 +529,6 @@
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
- t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber);
if (!mNextFrameTimelineInfoQueue.empty()) {
t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
@@ -583,21 +550,7 @@
}
}
- auto mergeTransaction =
- [&t, currentFrameNumber = bufferItem.mFrameNumber](
- std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
- auto& [targetFrameNumber, transaction] = pendingTransaction;
- if (currentFrameNumber < targetFrameNumber) {
- return false;
- }
- t->merge(std::move(transaction));
- return true;
- };
-
- mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
- mPendingTransactions.end(), mergeTransaction),
- mPendingTransactions.end());
-
+ mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
t->setApplyToken(mApplyToken).apply();
}
@@ -631,34 +584,57 @@
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
+void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+ if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked. The release callback will be async so we need to wait
+ // on max acquired to make sure we have the capacity to acquire another buffer.
+ if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting to flush shadow queue...");
+ mCallbackCV.wait(lock);
+ }
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ }
+
+ while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting for free buffer.");
+ mCallbackCV.wait(lock);
+ }
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- const bool nextTransactionSet = mNextTransaction != nullptr;
- BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
- if (nextTransactionSet) {
- if (mWaitForTransactionCallback) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mNextTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(_lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
+ const bool syncTransactionSet = mSyncTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+
+ if (syncTransactionSet) {
+ bool mayNeedToWaitForBuffer = true;
+ // If we are going to re-use the same mSyncTransaction, release the buffer that may already
+ // be set in the Transaction. This is to allow us a free slot early to continue processing
+ // a new buffer.
+ if (!mAcquireSingleBuffer) {
+ auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+ if (bufferData) {
+ BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+ bufferData->frameNumber);
+ releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence);
+ // Because we just released a buffer, we know there's no need to wait for a free
+ // buffer.
+ mayNeedToWaitForBuffer = false;
}
}
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(_lock);
+ if (mayNeedToWaitForBuffer) {
+ flushAndWaitForFreeBuffer(_lock);
}
}
@@ -670,19 +646,21 @@
ATRACE_INT(mQueuedBufferTrace.c_str(),
mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
- BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
- boolToString(nextTransactionSet));
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
+ boolToString(syncTransactionSet));
- if (nextTransactionSet) {
- acquireNextBufferLocked(std::move(mNextTransaction));
+ if (syncTransactionSet) {
+ acquireNextBufferLocked(mSyncTransaction);
// Only need a commit callback when syncing to ensure the buffer that's synced has been sent
// to SF
incStrong((void*)transactionCommittedCallbackThunk);
- mNextTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+ mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mNextTransaction = nullptr;
+ if (mAcquireSingleBuffer) {
+ mSyncTransaction = nullptr;
+ }
mWaitForTransactionCallback = true;
} else if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
@@ -704,9 +682,11 @@
mDequeueTimestamps.erase(bufferId);
};
-void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
+ bool acquireSingleBuffer) {
std::lock_guard _lock{mMutex};
- mNextTransaction = t;
+ mSyncTransaction = t;
+ mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
}
bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
@@ -731,17 +711,6 @@
return mSize != bufferSize;
}
-void BLASTBufferQueue::setTransactionCompleteCallback(
- uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) {
- std::lock_guard _lock{mMutex};
- if (transactionCompleteCallback == nullptr) {
- mTransactionCompleteCallback = nullptr;
- } else {
- mTransactionCompleteCallback = std::move(transactionCompleteCallback);
- mTransactionCompleteFrameNumber = frameNumber;
- }
-}
-
// Check if we have acquired the maximum number of buffers.
// Consumer can acquire an additional buffer if that buffer is not droppable. Set
// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
@@ -849,6 +818,32 @@
}
}
+void BLASTBufferQueue::applyPendingTransactions(uint64_t frameNumber) {
+ std::lock_guard _lock{mMutex};
+
+ SurfaceComposerClient::Transaction t;
+ mergePendingTransactions(&t, frameNumber);
+ t.setApplyToken(mApplyToken).apply();
+}
+
+void BLASTBufferQueue::mergePendingTransactions(SurfaceComposerClient::Transaction* t,
+ uint64_t frameNumber) {
+ auto mergeTransaction =
+ [&t, currentFrameNumber = frameNumber](
+ std::tuple<uint64_t, SurfaceComposerClient::Transaction> pendingTransaction) {
+ auto& [targetFrameNumber, transaction] = pendingTransaction;
+ if (currentFrameNumber < targetFrameNumber) {
+ return false;
+ }
+ t->merge(std::move(transaction));
+ return true;
+ };
+
+ mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
+ mPendingTransactions.end(), mergeTransaction),
+ mPendingTransactions.end());
+}
+
// Maintains a single worker thread per process that services a list of runnables.
class AsyncWorker : public Singleton<AsyncWorker> {
private:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 46800f2..ee80082 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -146,6 +146,19 @@
return 1; // keep the callback
}
+void DisplayEventDispatcher::populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const {
+ for (size_t i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ DisplayEventReceiver::Event::VSync::FrameTimeline receiverTimeline =
+ event.vsync.frameTimelines[i];
+ outVsyncEventData->frameTimelines[i] = {.id = receiverTimeline.vsyncId,
+ .deadlineTimestamp =
+ receiverTimeline.deadlineTimestamp,
+ .expectedPresentTime =
+ receiverTimeline.expectedVSyncTimestamp};
+ }
+}
+
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
uint32_t* outCount,
@@ -169,6 +182,9 @@
outVsyncEventData->id = ev.vsync.vsyncId;
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
+ outVsyncEventData->preferredFrameTimelineIndex =
+ ev.vsync.preferredFrameTimelineIndex;
+ populateFrameTimelines(ev, outVsyncEventData);
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 03b33c7..b916e48 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -19,7 +19,6 @@
#include <utils/Errors.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
new file mode 100644
index 0000000..52d9540
--- /dev/null
+++ b/libs/gui/DisplayInfo.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DisplayInfo"
+
+#include <binder/Parcel.h>
+#include <gui/DisplayInfo.h>
+#include <private/gui/ParcelUtils.h>
+
+#include <log/log.h>
+
+namespace android::gui {
+
+// --- DisplayInfo ---
+
+status_t DisplayInfo::readFromParcel(const android::Parcel* parcel) {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ float dsdx, dtdx, tx, dtdy, dsdy, ty;
+ SAFE_PARCEL(parcel->readInt32, &displayId);
+ SAFE_PARCEL(parcel->readInt32, &logicalWidth);
+ SAFE_PARCEL(parcel->readInt32, &logicalHeight);
+ SAFE_PARCEL(parcel->readFloat, &dsdx);
+ SAFE_PARCEL(parcel->readFloat, &dtdx);
+ SAFE_PARCEL(parcel->readFloat, &tx);
+ SAFE_PARCEL(parcel->readFloat, &dtdy);
+ SAFE_PARCEL(parcel->readFloat, &dsdy);
+ SAFE_PARCEL(parcel->readFloat, &ty);
+
+ transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
+
+ return OK;
+}
+
+status_t DisplayInfo::writeToParcel(android::Parcel* parcel) const {
+ if (parcel == nullptr) {
+ ALOGE("%s: Null parcel", __func__);
+ return BAD_VALUE;
+ }
+
+ SAFE_PARCEL(parcel->writeInt32, displayId);
+ SAFE_PARCEL(parcel->writeInt32, logicalWidth);
+ SAFE_PARCEL(parcel->writeInt32, logicalHeight);
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdx());
+ SAFE_PARCEL(parcel->writeFloat, transform.tx());
+ SAFE_PARCEL(parcel->writeFloat, transform.dtdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.dsdy());
+ SAFE_PARCEL(parcel->writeFloat, transform.ty());
+
+ return OK;
+}
+
+} // namespace android::gui
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
deleted file mode 100644
index c0e246f..0000000
--- a/libs/gui/IDisplayEventConnection.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2011 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 <gui/IDisplayEventConnection.h>
-
-#include <private/gui/BitTube.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
- SET_VSYNC_RATE,
- REQUEST_NEXT_VSYNC,
- LAST = REQUEST_NEXT_VSYNC,
-};
-
-} // Anonymous namespace
-
-class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
-public:
- explicit BpDisplayEventConnection(const sp<IBinder>& impl)
- : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
-
- ~BpDisplayEventConnection() override;
-
- status_t stealReceiveChannel(gui::BitTube* outChannel) override {
- return callRemote<decltype(
- &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
- outChannel);
- }
-
- status_t setVsyncRate(uint32_t count) override {
- return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
- count);
- }
-
- void requestNextVsync() override {
- callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
- Tag::REQUEST_NEXT_VSYNC);
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() = default;
-
-IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-
-status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::STEAL_RECEIVE_CHANNEL:
- return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
- case Tag::SET_VSYNC_RATE:
- return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
- case Tag::REQUEST_NEXT_VSYNC:
- return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
deleted file mode 100644
index 40cbfce..0000000
--- a/libs/gui/IRegionSamplingListener.cpp
+++ /dev/null
@@ -1,64 +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_TAG "IRegionSamplingListener"
-//#define LOG_NDEBUG 0
-
-#include <gui/IRegionSamplingListener.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
- LAST = ON_SAMPLE_COLLECTED,
-};
-
-} // Anonymous namespace
-
-class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
-public:
- explicit BpRegionSamplingListener(const sp<IBinder>& impl)
- : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
-
- ~BpRegionSamplingListener() override;
-
- void onSampleCollected(float medianLuma) override {
- callRemoteAsync<decltype(
- &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpRegionSamplingListener::~BpRegionSamplingListener() = default;
-
-IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
-
-status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_SAMPLE_COLLECTED:
- return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 1726761..7f73013 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,13 +17,13 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -44,6 +44,8 @@
namespace android {
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IWindowInfosListener;
using ui::ColorMode;
@@ -124,11 +126,11 @@
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply);
}
- status_t captureDisplay(uint64_t displayOrLayerStack,
+ status_t captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(data.writeUint64, displayOrLayerStack);
+ SAFE_PARCEL(data.writeUint64, displayId.value);
SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener));
return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply);
@@ -282,9 +284,14 @@
NO_ERROR) {
std::vector<uint64_t> rawIds;
if (reply.readUint64Vector(&rawIds) == NO_ERROR) {
- std::vector<PhysicalDisplayId> displayIds(rawIds.size());
- std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(),
- [](uint64_t rawId) { return PhysicalDisplayId(rawId); });
+ std::vector<PhysicalDisplayId> displayIds;
+ displayIds.reserve(rawIds.size());
+
+ for (const uint64_t rawId : rawIds) {
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ displayIds.push_back(*id);
+ }
+ }
return displayIds;
}
}
@@ -299,8 +306,11 @@
&reply);
uint64_t rawId;
SAFE_PARCEL(reply.readUint64, &rawId);
- *displayId = PhysicalDisplayId(rawId);
- return NO_ERROR;
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(rawId)) {
+ *displayId = *id;
+ return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override {
@@ -1142,41 +1152,6 @@
return reply.readInt32();
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override {
- if (!outToken) return BAD_VALUE;
-
- Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
- &reply);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
- err);
- return err;
- }
-
- err = reply.readInt32();
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
- return err;
- }
-
- err = reply.readStrongBinder(outToken);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
- strerror(-err), err);
- return err;
- }
-
- return NO_ERROR;
- }
-
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override {
Parcel data, reply;
@@ -1355,12 +1330,15 @@
}
case CAPTURE_DISPLAY_BY_ID: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- uint64_t displayOrLayerStack = 0;
+ uint64_t value;
+ SAFE_PARCEL(data.readUint64, &value);
+ const auto id = DisplayId::fromValue(value);
+ if (!id) return BAD_VALUE;
+
sp<IScreenCaptureListener> captureListener;
- SAFE_PARCEL(data.readUint64, &displayOrLayerStack);
SAFE_PARCEL(data.readStrongBinder, &captureListener);
- return captureDisplay(displayOrLayerStack, captureListener);
+ return captureDisplay(*id, captureListener);
}
case CAPTURE_LAYERS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
@@ -1427,9 +1405,9 @@
}
case GET_PHYSICAL_DISPLAY_TOKEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- PhysicalDisplayId displayId(data.readUint64());
- sp<IBinder> display = getPhysicalDisplayToken(displayId);
- reply->writeStrongBinder(display);
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(data.readUint64());
+ if (!id) return BAD_VALUE;
+ reply->writeStrongBinder(getPhysicalDisplayToken(*id));
return NO_ERROR;
}
case GET_DISPLAY_STATE: {
@@ -2062,16 +2040,6 @@
reply->writeInt32(result);
return NO_ERROR;
}
- case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> token;
- status_t result = acquireFrameRateFlexibilityToken(&token);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->writeStrongBinder(token);
- }
- return NO_ERROR;
- }
case SET_FRAME_TIMELINE_INFO: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 98e8b54..aa7ebc9 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -254,11 +254,10 @@
}
void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override {
+ uint32_t currentMaxAcquiredBufferCount) override {
callRemoteAsync<decltype(
&ITransactionCompletedListener::onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER,
callbackId, releaseFence,
- transformHint,
currentMaxAcquiredBufferCount);
}
};
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index d266229..ec0573a 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -41,7 +41,6 @@
z(0),
w(0),
h(0),
- layerStack(0),
alpha(0),
flags(0),
mask(0),
@@ -51,7 +50,6 @@
transform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
- orientedDisplaySpaceRect(Rect::INVALID_RECT),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -65,12 +63,10 @@
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
fixedTransformHint(ui::Transform::ROT_INVALID),
- frameNumber(0),
autoRefresh(false),
isTrustedOverlay(false),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
- releaseBufferListener(nullptr),
dropInputMode(gui::DropInputMode::NONE) {
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
@@ -87,7 +83,7 @@
SAFE_PARCEL(output.writeInt32, z);
SAFE_PARCEL(output.writeUint32, w);
SAFE_PARCEL(output.writeUint32, h);
- SAFE_PARCEL(output.writeUint32, layerStack);
+ SAFE_PARCEL(output.writeUint32, layerStack.id);
SAFE_PARCEL(output.writeFloat, alpha);
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
@@ -103,21 +99,6 @@
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
- SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
-
- if (buffer) {
- SAFE_PARCEL(output.writeBool, true);
- SAFE_PARCEL(output.write, *buffer);
- } else {
- SAFE_PARCEL(output.writeBool, false);
- }
-
- if (acquireFence) {
- SAFE_PARCEL(output.writeBool, true);
- SAFE_PARCEL(output.write, *acquireFence);
- } else {
- SAFE_PARCEL(output.writeBool, false);
- }
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
@@ -134,8 +115,6 @@
SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float));
SAFE_PARCEL(output.writeFloat, cornerRadius);
SAFE_PARCEL(output.writeUint32, backgroundBlurRadius);
- SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
- SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
SAFE_PARCEL(output.writeParcelable, metadata);
SAFE_PARCEL(output.writeFloat, bgColorAlpha);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace));
@@ -152,9 +131,7 @@
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
- SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeBool, autoRefresh);
- SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
SAFE_PARCEL(output.writeUint32, blurRegions.size());
for (auto region : blurRegions) {
@@ -175,8 +152,8 @@
SAFE_PARCEL(output.write, destinationFrame);
SAFE_PARCEL(output.writeBool, isTrustedOverlay);
- SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
+ SAFE_PARCEL(bufferData.write, output);
return NO_ERROR;
}
@@ -190,7 +167,7 @@
SAFE_PARCEL(input.readInt32, &z);
SAFE_PARCEL(input.readUint32, &w);
SAFE_PARCEL(input.readUint32, &h);
- SAFE_PARCEL(input.readUint32, &layerStack);
+ SAFE_PARCEL(input.readUint32, &layerStack.id);
SAFE_PARCEL(input.readFloat, &alpha);
SAFE_PARCEL(input.readUint32, &flags);
@@ -216,20 +193,6 @@
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
- SAFE_PARCEL(input.read, orientedDisplaySpaceRect);
-
- bool tmpBool = false;
- SAFE_PARCEL(input.readBool, &tmpBool);
- if (tmpBool) {
- buffer = new GraphicBuffer();
- SAFE_PARCEL(input.read, *buffer);
- }
-
- SAFE_PARCEL(input.readBool, &tmpBool);
- if (tmpBool) {
- acquireFence = new Fence();
- SAFE_PARCEL(input.read, *acquireFence);
- }
uint32_t tmpUint32 = 0;
SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -238,6 +201,8 @@
SAFE_PARCEL(input.read, hdrMetadata);
SAFE_PARCEL(input.read, surfaceDamageRegion);
SAFE_PARCEL(input.readInt32, &api);
+
+ bool tmpBool = false;
SAFE_PARCEL(input.readBool, &tmpBool);
if (tmpBool) {
sidebandStream = NativeHandle::create(input.readNativeHandle(), true);
@@ -246,10 +211,6 @@
SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float));
SAFE_PARCEL(input.readFloat, &cornerRadius);
SAFE_PARCEL(input.readUint32, &backgroundBlurRadius);
- sp<IBinder> tmpBinder;
- SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
- cachedBuffer.token = tmpBinder;
- SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
SAFE_PARCEL(input.readParcelable, &metadata);
SAFE_PARCEL(input.readFloat, &bgColorAlpha);
@@ -274,15 +235,8 @@
SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
- SAFE_PARCEL(input.readUint64, &frameNumber);
SAFE_PARCEL(input.readBool, &autoRefresh);
- tmpBinder = nullptr;
- SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
- if (tmpBinder) {
- releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
- }
-
uint32_t numRegions = 0;
SAFE_PARCEL(input.readUint32, &numRegions);
blurRegions.clear();
@@ -306,11 +260,10 @@
SAFE_PARCEL(input.read, destinationFrame);
SAFE_PARCEL(input.readBool, &isTrustedOverlay);
- SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
-
uint32_t mode;
SAFE_PARCEL(input.readUint32, &mode);
dropInputMode = static_cast<gui::DropInputMode>(mode);
+ SAFE_PARCEL(bufferData.read, input);
return NO_ERROR;
}
@@ -322,21 +275,14 @@
return state.read(input);
}
-DisplayState::DisplayState()
- : what(0),
- layerStack(0),
- flags(0),
- layerStackSpaceRect(Rect::EMPTY_RECT),
- orientedDisplaySpaceRect(Rect::EMPTY_RECT),
- width(0),
- height(0) {}
+DisplayState::DisplayState() = default;
status_t DisplayState::write(Parcel& output) const {
SAFE_PARCEL(output.writeStrongBinder, token);
SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface));
SAFE_PARCEL(output.writeUint32, what);
- SAFE_PARCEL(output.writeUint32, layerStack);
SAFE_PARCEL(output.writeUint32, flags);
+ SAFE_PARCEL(output.writeUint32, layerStack.id);
SAFE_PARCEL(output.writeUint32, toRotationInt(orientation));
SAFE_PARCEL(output.write, layerStackSpaceRect);
SAFE_PARCEL(output.write, orientedDisplaySpaceRect);
@@ -352,8 +298,8 @@
surface = interface_cast<IGraphicBufferProducer>(tmpBinder);
SAFE_PARCEL(input.readUint32, &what);
- SAFE_PARCEL(input.readUint32, &layerStack);
SAFE_PARCEL(input.readUint32, &flags);
+ SAFE_PARCEL(input.readUint32, &layerStack.id);
uint32_t tmpUint = 0;
SAFE_PARCEL(input.readUint32, &tmpUint);
orientation = ui::toRotation(tmpUint);
@@ -468,12 +414,7 @@
}
if (other.what & eBufferChanged) {
what |= eBufferChanged;
- buffer = other.buffer;
- releaseBufferEndpoint = other.releaseBufferEndpoint;
- }
- if (other.what & eAcquireFenceChanged) {
- what |= eAcquireFenceChanged;
- acquireFence = other.acquireFence;
+ bufferData = other.bufferData;
}
if (other.what & eDataspaceChanged) {
what |= eDataspaceChanged;
@@ -506,10 +447,6 @@
what |= eInputInfoChanged;
windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle);
}
- if (other.what & eCachedBufferChanged) {
- what |= eCachedBufferChanged;
- cachedBuffer = other.cachedBuffer;
- }
if (other.what & eBackgroundColorChanged) {
what |= eBackgroundColorChanged;
color = other.color;
@@ -538,10 +475,6 @@
what |= eFixedTransformHintChanged;
fixedTransformHint = other.fixedTransformHint;
}
- if (other.what & eFrameNumberChanged) {
- what |= eFrameNumberChanged;
- frameNumber = other.frameNumber;
- }
if (other.what & eAutoRefreshChanged) {
what |= eAutoRefreshChanged;
autoRefresh = other.autoRefresh;
@@ -550,13 +483,6 @@
what |= eTrustedOverlayChanged;
isTrustedOverlay = other.isTrustedOverlay;
}
- if (other.what & eReleaseBufferListenerChanged) {
- if (releaseBufferListener) {
- ALOGW("Overriding releaseBufferListener");
- }
- what |= eReleaseBufferListenerChanged;
- releaseBufferListener = other.releaseBufferListener;
- }
if (other.what & eStretchChanged) {
what |= eStretchChanged;
stretchEffect = other.stretchEffect;
@@ -569,6 +495,9 @@
what |= eDestinationFrameChanged;
destinationFrame = other.destinationFrame;
}
+ if (other.what & eProducerDisconnect) {
+ what |= eProducerDisconnect;
+ }
if (other.what & eDropInputModeChanged) {
what |= eDropInputModeChanged;
dropInputMode = other.dropInputMode;
@@ -579,17 +508,17 @@
}
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
- "other.what=0x%" PRIu64 " what=0x%" PRIu64,
- other.what, what);
+ "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
+ other.what, what, (other.what & what) ^ other.what);
}
}
bool layer_state_t::hasBufferChanges() const {
- return (what & layer_state_t::eBufferChanged) || (what & layer_state_t::eCachedBufferChanged);
+ return what & layer_state_t::eBufferChanged;
}
bool layer_state_t::hasValidBuffer() const {
- return buffer || cachedBuffer.isValid();
+ return bufferData.buffer || bufferData.cachedBuffer.isValid();
}
status_t layer_state_t::matrix22_t::write(Parcel& output) const {
@@ -621,9 +550,7 @@
}
bool InputWindowCommands::empty() const {
- bool empty = true;
- empty = focusRequests.empty() && !syncInputWindows;
- return empty;
+ return focusRequests.empty() && !syncInputWindows;
}
void InputWindowCommands::clear() {
@@ -750,4 +677,66 @@
return NO_ERROR;
}
+status_t BufferData::write(Parcel& output) const {
+ SAFE_PARCEL(output.writeInt32, flags.get());
+
+ if (buffer) {
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *buffer);
+ } else {
+ SAFE_PARCEL(output.writeBool, false);
+ }
+
+ if (acquireFence) {
+ SAFE_PARCEL(output.writeBool, true);
+ SAFE_PARCEL(output.write, *acquireFence);
+ } else {
+ SAFE_PARCEL(output.writeBool, false);
+ }
+
+ SAFE_PARCEL(output.writeUint64, frameNumber);
+ SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(releaseBufferListener));
+ SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint);
+
+ SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote());
+ SAFE_PARCEL(output.writeUint64, cachedBuffer.id);
+
+ return NO_ERROR;
+}
+
+status_t BufferData::read(const Parcel& input) {
+ int32_t tmpInt32;
+ SAFE_PARCEL(input.readInt32, &tmpInt32);
+ flags = Flags<BufferDataChange>(tmpInt32);
+
+ bool tmpBool = false;
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ buffer = new GraphicBuffer();
+ SAFE_PARCEL(input.read, *buffer);
+ }
+
+ SAFE_PARCEL(input.readBool, &tmpBool);
+ if (tmpBool) {
+ acquireFence = new Fence();
+ SAFE_PARCEL(input.read, *acquireFence);
+ }
+
+ SAFE_PARCEL(input.readUint64, &frameNumber);
+
+ sp<IBinder> tmpBinder = nullptr;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ if (tmpBinder) {
+ releaseBufferListener = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+ }
+ SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint);
+
+ tmpBinder = nullptr;
+ SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder);
+ cachedBuffer.token = tmpBinder;
+ SAFE_PARCEL(input.readUint64, &cachedBuffer.id);
+
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 922bceb..aafa5e4 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -53,6 +53,7 @@
namespace android {
using gui::FocusRequest;
+using gui::IRegionSamplingListener;
using gui::WindowInfo;
using gui::WindowInfoHandle;
using gui::WindowInfosListener;
@@ -61,6 +62,14 @@
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+namespace {
+// Initialize transaction id counter used to generate transaction ids
+std::atomic<uint32_t> idCounter = 0;
+int64_t generateId() {
+ return (((int64_t)getpid()) << 32) | ++idCounter;
+}
+} // namespace
+
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
@@ -214,12 +223,6 @@
mReleaseBufferCallbacks[callbackId] = listener;
}
-void TransactionCompletedListener::removeReleaseBufferCallback(
- const ReleaseCallbackId& callbackId) {
- std::scoped_lock<std::mutex> lock(mMutex);
- mReleaseBufferCallbacks.erase(callbackId);
-}
-
void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie,
sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) {
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -343,7 +346,6 @@
surfaceStats.previousReleaseFence
? surfaceStats.previousReleaseFence
: Fence::NO_FENCE,
- surfaceStats.transformHint,
surfaceStats.currentMaxAcquiredBufferCount);
}
}
@@ -359,6 +361,10 @@
// through all until the SC is found.
int32_t layerId = -1;
for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+ // We only want to run the stats callback for ON_COMPLETE
+ continue;
+ }
sp<SurfaceControl> sc =
callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
if (sc != nullptr) {
@@ -367,7 +373,7 @@
}
}
- {
+ if (layerId != -1) {
// Acquire surface stats listener lock such that we guarantee that after calling
// unregister, there won't be any further callback.
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
@@ -389,7 +395,7 @@
}
void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence, uint32_t transformHint,
+ sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) {
ReleaseBufferCallback callback;
{
@@ -401,7 +407,11 @@
callbackId.to_string().c_str());
return;
}
- callback(callbackId, releaseFence, transformHint, currentMaxAcquiredBufferCount);
+ std::optional<uint32_t> optionalMaxAcquiredBufferCount =
+ currentMaxAcquiredBufferCount == UINT_MAX
+ ? std::nullopt
+ : std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
+ callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -416,6 +426,14 @@
return callback;
}
+void TransactionCompletedListener::removeReleaseBufferCallback(
+ const ReleaseCallbackId& callbackId) {
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ popReleaseBufferCallbackLocked(callbackId);
+ }
+}
+
// ---------------------------------------------------------------------------
void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -525,10 +543,6 @@
// ---------------------------------------------------------------------------
-// Initialize transaction id counter used to generate transaction ids
-// Transactions will start counting at 1, 0 is used for invalid transactions
-std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
-
SurfaceComposerClient::Transaction::Transaction() {
mId = generateId();
}
@@ -560,9 +574,6 @@
return nullptr;
}
-int64_t SurfaceComposerClient::Transaction::generateId() {
- return (((int64_t)getpid()) << 32) | idCounter++;
-}
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint32_t forceSynchronous = parcel->readUint32();
@@ -712,11 +723,34 @@
return NO_ERROR;
}
+void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_state_t& state) {
+ if (!(state.what & layer_state_t::eBufferChanged)) {
+ return;
+ }
+
+ auto listener = state.bufferData.releaseBufferListener;
+ sp<Fence> fence =
+ state.bufferData.acquireFence ? state.bufferData.acquireFence : Fence::NO_FENCE;
+ if (state.bufferData.releaseBufferEndpoint ==
+ IInterface::asBinder(TransactionCompletedListener::getIInstance())) {
+ // if the callback is in process, run on a different thread to avoid any lock contigency
+ // issues in the client.
+ SurfaceComposerClient::getDefault()
+ ->mReleaseCallbackThread.addReleaseCallback(state.bufferData.releaseCallbackId,
+ fence);
+ } else {
+ listener->onReleaseBuffer(state.bufferData.releaseCallbackId, fence, UINT_MAX);
+ }
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
for (auto const& [handle, composerState] : other.mComposerStates) {
if (mComposerStates.count(handle) == 0) {
mComposerStates[handle] = composerState;
} else {
+ if (composerState.state.what & layer_state_t::eBufferChanged) {
+ releaseBufferIfOverwriting(mComposerStates[handle].state);
+ }
mComposerStates[handle].state.merge(composerState.state);
}
}
@@ -792,7 +826,7 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
- uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+ uncacheBuffer, false, {}, generateId());
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -805,7 +839,7 @@
layer_state_t* s = &(mComposerStates[handle].state);
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
- } else if (s->what & layer_state_t::eCachedBufferChanged) {
+ } else if (s->bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged)) {
// If eBufferChanged and eCachedBufferChanged are both trued then that means
// we already cached the buffer in a previous call to cacheBuffers, perhaps
// from writeToParcel on a Transaction that was merged in to this one.
@@ -814,23 +848,22 @@
// Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste
// time trying to cache them.
- if (!s->buffer) {
+ if (!s->bufferData.buffer) {
continue;
}
uint64_t cacheId = 0;
- status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId);
+ status_t ret = BufferCache::getInstance().getCacheId(s->bufferData.buffer, &cacheId);
if (ret == NO_ERROR) {
// Cache-hit. Strip the buffer and send only the id.
- s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged);
- s->buffer = nullptr;
+ s->bufferData.buffer = nullptr;
} else {
// Cache-miss. Include the buffer and send the new cacheId.
- cacheId = BufferCache::getInstance().cache(s->buffer);
+ cacheId = BufferCache::getInstance().cache(s->bufferData.buffer);
}
- s->what |= layer_state_t::eCachedBufferChanged;
- s->cachedBuffer.token = BufferCache::getInstance().getToken();
- s->cachedBuffer.id = cacheId;
+ s->bufferData.flags |= BufferData::BufferDataChange::cachedBufferChanged;
+ s->bufferData.cachedBuffer.token = BufferCache::getInstance().getToken();
+ s->bufferData.cachedBuffer.id = cacheId;
// If we have more buffers than the size of the cache, we should stop caching so we don't
// evict other buffers in this transaction
@@ -1112,7 +1145,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayerStack(
- const sp<SurfaceControl>& sc, uint32_t layerStack) {
+ const sp<SurfaceControl>& sc, ui::LayerStack layerStack) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1288,75 +1321,83 @@
return *this;
}
+std::optional<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ return std::nullopt;
+ }
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ return std::nullopt;
+ }
+
+ BufferData bufferData = s->bufferData;
+
+ TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
+ bufferData.releaseCallbackId);
+ BufferData emptyBufferData;
+ s->what &= ~layer_state_t::eBufferChanged;
+ s->bufferData = emptyBufferData;
+
+ mContainsBuffer = false;
+ return bufferData;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
- const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer, const ReleaseCallbackId& id,
- ReleaseBufferCallback callback) {
+ const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
+ const ReleaseCallbackId& id, ReleaseBufferCallback callback) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
- removeReleaseBufferCallback(s);
- s->what |= layer_state_t::eBufferChanged;
- s->buffer = buffer;
- s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+
+ releaseBufferIfOverwriting(*s);
+
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ if (frameNumber) {
+ bufferData.frameNumber = *frameNumber;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ }
+ if (fence) {
+ bufferData.acquireFence = *fence;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ }
+ bufferData.releaseBufferEndpoint =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
if (mIsAutoTimestamp) {
mDesiredPresentTime = systemTime();
}
- setReleaseBufferCallback(s, id, callback);
-
+ setReleaseBufferCallback(&bufferData, id, callback);
+ s->what |= layer_state_t::eBufferChanged;
+ s->bufferData = bufferData;
registerSurfaceControlForCallback(sc);
mContainsBuffer = true;
return *this;
}
-void SurfaceComposerClient::Transaction::removeReleaseBufferCallback(layer_state_t* s) {
- if (!s->releaseBufferListener) {
- return;
- }
-
- s->what &= ~static_cast<uint64_t>(layer_state_t::eReleaseBufferListenerChanged);
- s->releaseBufferListener = nullptr;
- auto listener = TransactionCompletedListener::getInstance();
- listener->removeReleaseBufferCallback(s->releaseCallbackId);
- s->releaseCallbackId = ReleaseCallbackId::INVALID_ID;
-}
-
-void SurfaceComposerClient::Transaction::setReleaseBufferCallback(layer_state_t* s,
+void SurfaceComposerClient::Transaction::setReleaseBufferCallback(BufferData* bufferData,
const ReleaseCallbackId& id,
ReleaseBufferCallback callback) {
if (!callback) {
return;
}
- if (!s->buffer) {
+ if (!bufferData->buffer) {
ALOGW("Transaction::setReleaseBufferCallback"
"ignored trying to set a callback on a null buffer.");
return;
}
- s->what |= layer_state_t::eReleaseBufferListenerChanged;
- s->releaseBufferListener = TransactionCompletedListener::getIInstance();
- s->releaseCallbackId = id;
+ bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance();
+ bufferData->releaseCallbackId = id;
auto listener = TransactionCompletedListener::getInstance();
listener->setReleaseBufferCallback(id, callback);
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence(
- const sp<SurfaceControl>& sc, const sp<Fence>& fence) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
- s->what |= layer_state_t::eAcquireFenceChanged;
- s->acquireFence = fence;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
const sp<SurfaceControl>& sc, ui::Dataspace dataspace) {
layer_state_t* s = getLayerState(sc);
@@ -1506,20 +1547,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber(
- const sp<SurfaceControl>& sc, uint64_t frameNumber) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
-
- s->what |= layer_state_t::eFrameNumberChanged;
- s->frameNumber = frameNumber;
-
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc, const WindowInfo& info) {
layer_state_t* s = getLayerState(sc);
@@ -1792,7 +1819,7 @@
}
void SurfaceComposerClient::Transaction::setDisplayLayerStack(const sp<IBinder>& token,
- uint32_t layerStack) {
+ ui::LayerStack layerStack) {
DisplayState& s(getDisplayState(token));
s.layerStack = layerStack;
s.what |= DisplayState::eLayerStackChanged;
@@ -2216,12 +2243,12 @@
return s->captureDisplay(captureArgs, captureListener);
}
-status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack,
+status_t ScreenshotClient::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == nullptr) return NO_INIT;
- return s->captureDisplay(displayOrLayerStack, captureListener);
+ return s->captureDisplay(displayId, captureListener);
}
status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs,
@@ -2232,4 +2259,43 @@
return s->captureLayers(captureArgs, captureListener);
}
+// ---------------------------------------------------------------------------------
+
+void ReleaseCallbackThread::addReleaseCallback(const ReleaseCallbackId callbackId,
+ sp<Fence> releaseFence) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (!mStarted) {
+ mThread = std::thread(&ReleaseCallbackThread::threadMain, this);
+ mStarted = true;
+ }
+
+ mCallbackInfos.emplace(callbackId, std::move(releaseFence));
+ mReleaseCallbackPending.notify_one();
+}
+
+void ReleaseCallbackThread::threadMain() {
+ const auto listener = TransactionCompletedListener::getInstance();
+ std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> callbackInfos;
+ while (true) {
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ callbackInfos = std::move(mCallbackInfos);
+ mCallbackInfos = {};
+ }
+
+ while (!callbackInfos.empty()) {
+ auto [callbackId, releaseFence] = callbackInfos.front();
+ listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ callbackInfos.pop();
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCallbackInfos.size() == 0) {
+ mReleaseCallbackPending.wait(lock);
+ }
+ }
+ }
+}
+
} // namespace android
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index b2ef7aa..5f3a726 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -54,12 +54,11 @@
info.frameLeft == frameLeft && info.frameTop == frameTop &&
info.frameRight == frameRight && info.frameBottom == frameBottom &&
info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor &&
- info.transform == transform && info.displayOrientation == displayOrientation &&
- info.displayWidth == displayWidth && info.displayHeight == displayHeight &&
- info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible &&
- info.trustedOverlay == trustedOverlay && info.focusable == focusable &&
- info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper &&
- info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
+ info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) &&
+ info.visible == visible && info.trustedOverlay == trustedOverlay &&
+ info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode &&
+ info.hasWallpaper == hasWallpaper && info.paused == paused &&
+ info.ownerPid == ownerPid && info.ownerUid == ownerUid &&
info.packageName == packageName && info.inputFeatures == inputFeatures &&
info.displayId == displayId && info.portalToDisplayId == portalToDisplayId &&
info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop &&
@@ -97,9 +96,6 @@
parcel->writeFloat(transform.dtdy()) ?:
parcel->writeFloat(transform.dsdy()) ?:
parcel->writeFloat(transform.ty()) ?:
- parcel->writeUint32(displayOrientation) ?:
- parcel->writeInt32(displayWidth) ?:
- parcel->writeInt32(displayHeight) ?:
parcel->writeBool(visible) ?:
parcel->writeBool(focusable) ?:
parcel->writeBool(hasWallpaper) ?:
@@ -155,9 +151,6 @@
parcel->readFloat(&dtdy) ?:
parcel->readFloat(&dsdy) ?:
parcel->readFloat(&ty) ?:
- parcel->readUint32(&displayOrientation) ?:
- parcel->readInt32(&displayWidth) ?:
- parcel->readInt32(&displayHeight) ?:
parcel->readBool(&visible) ?:
parcel->readBool(&focusable) ?:
parcel->readBool(&hasWallpaper) ?:
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index c00a438..c32b9ab 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -19,6 +19,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::IWindowInfosReportedListener;
using gui::WindowInfo;
using gui::WindowInfosListener;
@@ -65,7 +66,7 @@
}
binder::Status WindowInfosListenerReporter::onWindowInfosChanged(
- const std::vector<WindowInfo>& windowInfos,
+ const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>& displayInfos,
const sp<IWindowInfosReportedListener>& windowInfosReportedListener) {
std::unordered_set<sp<WindowInfosListener>, ISurfaceComposer::SpHash<WindowInfosListener>>
windowInfosListeners;
@@ -78,7 +79,7 @@
}
for (auto listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos);
+ listener->onWindowInfosChanged(windowInfos, displayInfos);
}
if (windowInfosReportedListener) {
diff --git a/libs/ui/Size.cpp b/libs/gui/aidl/android/gui/BitTube.aidl
similarity index 74%
copy from libs/ui/Size.cpp
copy to libs/gui/aidl/android/gui/BitTube.aidl
index d2996d1..6b0595e 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/aidl/android/gui/BitTube.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+package android.gui;
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+parcelable BitTube cpp_header "private/gui/BitTube.h";
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
new file mode 100644
index 0000000..9f41593
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+import android.gui.BitTube;
+
+/** @hide */
+interface IDisplayEventConnection {
+ /*
+ * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
+ * descriptor of outChannel will be initialized, and this effectively "steals" the receive
+ * channel from the remote end (such that the remote end can only use its send channel).
+ */
+ void stealReceiveChannel(out BitTube outChannel);
+
+ /*
+ * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
+ * A value of 2 returns every other event, etc. A value of 0 returns no event unless
+ * requestNextVsync() has been called.
+ */
+ void setVsyncRate(in int count);
+
+ /*
+ * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
+ */
+ oneway void requestNextVsync(); // Asynchronous
+}
diff --git a/libs/ui/Size.cpp b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
similarity index 74%
rename from libs/ui/Size.cpp
rename to libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
index d2996d1..00a3959 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+package android.gui;
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+/** @hide */
+oneway interface IRegionSamplingListener {
+ void onSampleCollected(float medianLuma);
+}
diff --git a/libs/ui/Size.cpp b/libs/gui/android/gui/DisplayInfo.aidl
similarity index 67%
copy from libs/ui/Size.cpp
copy to libs/gui/android/gui/DisplayInfo.aidl
index d2996d1..30c0885 100644
--- a/libs/ui/Size.cpp
+++ b/libs/gui/android/gui/DisplayInfo.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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
+ * 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,
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+package android.gui;
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+parcelable DisplayInfo cpp_header "gui/DisplayInfo.h";
diff --git a/libs/gui/android/gui/IWindowInfosListener.aidl b/libs/gui/android/gui/IWindowInfosListener.aidl
index d4553ca..a5b2762 100644
--- a/libs/gui/android/gui/IWindowInfosListener.aidl
+++ b/libs/gui/android/gui/IWindowInfosListener.aidl
@@ -16,11 +16,12 @@
package android.gui;
+import android.gui.DisplayInfo;
import android.gui.IWindowInfosReportedListener;
import android.gui.WindowInfo;
/** @hide */
oneway interface IWindowInfosListener
{
- void onWindowInfosChanged(in WindowInfo[] windowInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
+ void onWindowInfosChanged(in WindowInfo[] windowInfos, in DisplayInfo[] displayInfos, in @nullable IWindowInfosReportedListener windowInfosReportedListener);
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 698844c..8a2f392 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -74,6 +74,7 @@
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
+ BLASTBufferQueue(const std::string& name);
BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
int height, int32_t format);
@@ -90,14 +91,13 @@
void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
const std::vector<SurfaceControlStats>& stats);
- void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats);
+ virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount);
- void setNextTransaction(SurfaceComposerClient::Transaction *t);
+ std::optional<uint32_t> currentMaxAcquiredBufferCount);
+ void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
- void setTransactionCompleteCallback(uint64_t frameNumber,
- std::function<void(int64_t)>&& transactionCompleteCallback);
+ void applyPendingTransactions(uint64_t frameNumber);
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format,
SurfaceComposerClient::Transaction* outTransaction = nullptr);
@@ -108,8 +108,6 @@
void setSidebandStream(const sp<NativeHandle>& stream);
uint32_t getLastTransformHint() const;
- void flushShadowQueue();
-
uint64_t getLastAcquiredFrameNum();
virtual ~BLASTBufferQueue();
@@ -130,9 +128,14 @@
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
+ void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
+ REQUIRES(mMutex);
- void flushShadowQueueLocked() REQUIRES(mMutex);
+ void flushShadowQueue() REQUIRES(mMutex);
void acquireAndReleaseBuffer() REQUIRES(mMutex);
+ void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
+ REQUIRES(mMutex);
+ void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
@@ -209,7 +212,7 @@
sp<IGraphicBufferProducer> mProducer;
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
- SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+ SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
@@ -223,9 +226,6 @@
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
- std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr;
- uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0};
-
// Queues up transactions using this token in SurfaceFlinger. This prevents queued up
// transactions from other parts of the client from blocking this transaction.
const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder();
@@ -243,7 +243,11 @@
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback = false;
+ bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
+
+ // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
+ // continue to acquire buffers until explicitly cleared
+ bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 08f3597..40621dd 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -17,6 +17,7 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
+#include <array>
namespace android {
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -33,6 +34,26 @@
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval = 0;
+
+ struct FrameTimeline {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = FrameTimelineInfo::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+
+ // The anticipated Vsync present time.
+ int64_t expectedPresentTime = 0;
+ };
+
+ // Sorted possible frame timelines.
+ std::array<FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength> frameTimelines;
+
+ // Index into the frameTimelines that represents the platform's preferred frame timeline.
+ size_t preferredFrameTimelineIndex = std::numeric_limits<size_t>::max();
};
class DisplayEventDispatcher : public LooperCallback {
@@ -76,5 +97,8 @@
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
uint32_t* outCount, VsyncEventData* outVsyncEventData);
+
+ void populateFrameTimelines(const DisplayEventReceiver::Event& event,
+ VsyncEventData* outVsyncEventData) const;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 0dffbde..456bbfb 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -33,7 +33,7 @@
// ----------------------------------------------------------------------------
-class IDisplayEventConnection;
+using gui::IDisplayEventConnection;
namespace gui {
class BitTube;
@@ -49,6 +49,9 @@
// ----------------------------------------------------------------------------
class DisplayEventReceiver {
public:
+ // Max amount of frame timelines is arbitrarily set to be reasonable.
+ static constexpr int64_t kFrameTimelinesLength = 7;
+
enum {
DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
@@ -77,6 +80,12 @@
nsecs_t deadlineTimestamp __attribute__((aligned(8)));
nsecs_t frameInterval __attribute__((aligned(8)));
int64_t vsyncId;
+ size_t preferredFrameTimelineIndex __attribute__((aligned(8)));
+ struct FrameTimeline {
+ nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+ nsecs_t deadlineTimestamp __attribute__((aligned(8)));
+ int64_t vsyncId;
+ } frameTimelines[kFrameTimelinesLength];
};
struct Hotplug {
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
new file mode 100644
index 0000000..74f33a2
--- /dev/null
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <gui/constants.h>
+#include <ui/Transform.h>
+
+namespace android::gui {
+
+/*
+ * Describes information about a display that can have windows in it.
+ *
+ * This should only be used by InputFlinger to support raw coordinates in logical display space.
+ */
+struct DisplayInfo : public Parcelable {
+ int32_t displayId = ADISPLAY_ID_NONE;
+
+ // Logical display dimensions.
+ int32_t logicalWidth = 0;
+ int32_t logicalHeight = 0;
+
+ // The display transform. This takes display coordinates to logical display coordinates.
+ ui::Transform transform;
+
+ status_t writeToParcel(android::Parcel*) const override;
+
+ status_t readFromParcel(const android::Parcel*) override;
+};
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
deleted file mode 100644
index cff22a3..0000000
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-#include <gui/ISurfaceComposer.h>
-#include <utils/Errors.h>
-
-#include <cstdint>
-
-namespace android {
-
-namespace gui {
-class BitTube;
-} // namespace gui
-
-class IDisplayEventConnection : public IInterface {
-public:
- DECLARE_META_INTERFACE(DisplayEventConnection)
-
- /*
- * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
- * descriptor of outChannel will be initialized, and this effectively "steals" the receive
- * channel from the remote end (such that the remote end can only use its send channel).
- */
- virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
-
- /*
- * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
- * A value of 2 returns every other event, etc. A value of 0 returns no event unless
- * requestNextVsync() has been called.
- */
- virtual status_t setVsyncRate(uint32_t count) = 0;
-
- /*
- * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
- */
- virtual void requestNextVsync() = 0; // Asynchronous
-};
-
-class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
-public:
- BnDisplayEventConnection()
- : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
deleted file mode 100644
index 1803d9a..0000000
--- a/libs/gui/include/gui/IRegionSamplingListener.h
+++ /dev/null
@@ -1,43 +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 <cstdint>
-#include <vector>
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-class IRegionSamplingListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(RegionSamplingListener)
-
- virtual void onSampleCollected(float medianLuma) = 0;
-};
-
-class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
-public:
- BnRegionSamplingListener()
- : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index cd289cb..2546e4c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -17,8 +17,10 @@
#pragma once
#include <android/gui/DisplayBrightness.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IFpsListener.h>
#include <android/gui/IHdrLayerInfoListener.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
@@ -60,13 +62,13 @@
struct LayerCaptureArgs;
class LayerDebugInfo;
class HdrCapabilities;
-class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
-class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
namespace ui {
@@ -116,6 +118,11 @@
using EventRegistrationFlags = Flags<EventRegistration>;
+ template <typename T>
+ struct SpHash {
+ size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+ };
+
/*
* Create a connection with SurfaceFlinger.
*/
@@ -241,24 +248,17 @@
* The subregion can be optionally rotated. It will also be scaled to
* match the size of the output buffer.
*/
- virtual status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureDisplay(const DisplayCaptureArgs&,
+ const sp<IScreenCaptureListener>&) = 0;
- virtual status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener) = 0;
-
- template <class AA>
- struct SpHash {
- size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); }
- };
+ virtual status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) = 0;
/**
* Capture a subtree of the layer hierarchy, potentially ignoring the root node.
* This requires READ_FRAME_BUFFER permission. This function will fail if there
* is a secure window on screen
*/
- virtual status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) = 0;
+ virtual status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) = 0;
/* Clears the frame statistics for animations.
*
@@ -514,14 +514,6 @@
int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
- * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
- * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
- * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
- * for tests. Release the token by releasing the returned IBinder reference.
- */
- virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
-
- /*
* Sets the frame timeline vsync info received from choreographer that corresponds to next
* buffer submitted on that surface.
*/
@@ -618,6 +610,7 @@
GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
+ // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
SET_FRAME_TIMELINE_INFO,
ADD_TRANSACTION_TRACE_LISTENER,
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 937095c..0df5822 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -192,7 +192,6 @@
virtual void onTransactionCompleted(ListenerStats stats) = 0;
virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t transformHint,
uint32_t currentMaxAcquiredBufferCount) = 0;
};
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index de14b3d..27f4d37 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -59,4 +59,14 @@
std::string itemToString(uint32_t key, const char* separator) const;
};
+// Keep in sync with the GameManager.java constants.
+enum class GameMode : int32_t {
+ Unsupported = 0,
+ Standard = 1,
+ Performance = 2,
+ Battery = 3,
+
+ ftl_last = Battery
+};
+
} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 03e4aac..b01eed4 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -36,6 +36,7 @@
#include <math/vec3.h>
#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Rotation.h>
@@ -57,6 +58,44 @@
bool isValid() const { return token != nullptr; }
};
+struct BufferData {
+ enum class BufferDataChange : uint32_t {
+ fenceChanged = 0x01,
+ frameNumberChanged = 0x02,
+ cachedBufferChanged = 0x04,
+ };
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> acquireFence;
+
+ // Used by BlastBufferQueue to forward the framenumber generated by the
+ // graphics producer.
+ uint64_t frameNumber = 0;
+
+ // Listens to when the buffer is safe to be released. This is used for blast
+ // layers only. The callback includes a release fence as well as the graphic
+ // buffer id to identify the buffer.
+ sp<ITransactionCompletedListener> releaseBufferListener = nullptr;
+
+ // Keeps track of the release callback id associated with the listener. This
+ // is not sent to the server since the id can be reconstructed there. This
+ // is used to remove the old callback from the client process map if it is
+ // overwritten by another setBuffer call.
+ ReleaseCallbackId releaseCallbackId = ReleaseCallbackId::INVALID_ID;
+
+ // Stores which endpoint the release information should be sent to. We don't want to send the
+ // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
+ // was called with.
+ sp<IBinder> releaseBufferEndpoint;
+
+ Flags<BufferDataChange> flags;
+
+ client_cache_t cachedBuffer;
+
+ status_t write(Parcel& output) const;
+ status_t read(const Parcel& input);
+};
+
/*
* Used to communicate layer information between SurfaceFlinger and its clients.
*/
@@ -81,7 +120,7 @@
eTransparentRegionChanged = 0x00000020,
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
- eReleaseBufferListenerChanged = 0x00000400,
+ /* unused 0x00000400, */
eShadowRadiusChanged = 0x00000800,
eLayerCreated = 0x00001000,
eBufferCropChanged = 0x00002000,
@@ -93,7 +132,7 @@
eTransformToDisplayInverseChanged = 0x00080000,
eCropChanged = 0x00100000,
eBufferChanged = 0x00200000,
- eAcquireFenceChanged = 0x00400000,
+ /* unused 0x00400000, */
eDataspaceChanged = 0x00800000,
eHdrMetadataChanged = 0x01000000,
eSurfaceDamageRegionChanged = 0x02000000,
@@ -104,7 +143,7 @@
eInputInfoChanged = 0x40000000,
eCornerRadiusChanged = 0x80000000,
eDestinationFrameChanged = 0x1'00000000,
- eCachedBufferChanged = 0x2'00000000,
+ /* unused = 0x2'00000000, */
eBackgroundColorChanged = 0x4'00000000,
eMetadataChanged = 0x8'00000000,
eColorSpaceAgnosticChanged = 0x10'00000000,
@@ -113,7 +152,7 @@
eBackgroundBlurRadiusChanged = 0x80'00000000,
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
- eFrameNumberChanged = 0x400'00000000,
+ /* unused 0x400'00000000, */
eBlurRegionsChanged = 0x800'00000000,
eAutoRefreshChanged = 0x1000'00000000,
eStretchChanged = 0x2000'00000000,
@@ -145,7 +184,7 @@
int32_t z;
uint32_t w;
uint32_t h;
- uint32_t layerStack;
+ ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
float alpha;
uint32_t flags;
uint32_t mask;
@@ -167,9 +206,7 @@
uint32_t transform;
bool transformToDisplayInverse;
Rect crop;
- Rect orientedDisplaySpaceRect;
- sp<GraphicBuffer> buffer;
- sp<Fence> acquireFence;
+ BufferData bufferData;
ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
@@ -180,8 +217,6 @@
sp<gui::WindowInfoHandle> windowInfoHandle = new gui::WindowInfoHandle();
- client_cache_t cachedBuffer;
-
LayerMetadata metadata;
// The following refer to the alpha, and dataspace, respectively of
@@ -215,10 +250,6 @@
// otherwise the value will be a valid ui::Rotation.
ui::Transform::RotationFlags fixedTransformHint;
- // Used by BlastBufferQueue to forward the framenumber generated by the
- // graphics producer.
- uint64_t frameNumber;
-
// Indicates that the consumer should acquire the next frame as soon as it
// can and not wait for a frame to become available. This is only relevant
// in shared buffer mode.
@@ -234,22 +265,6 @@
Rect bufferCrop;
Rect destinationFrame;
- // Listens to when the buffer is safe to be released. This is used for blast
- // layers only. The callback includes a release fence as well as the graphic
- // buffer id to identify the buffer.
- sp<ITransactionCompletedListener> releaseBufferListener;
-
- // Keeps track of the release callback id associated with the listener. This
- // is not sent to the server since the id can be reconstructed there. This
- // is used to remove the old callback from the client process map if it is
- // overwritten by another setBuffer call.
- ReleaseCallbackId releaseCallbackId;
-
- // Stores which endpoint the release information should be sent to. We don't want to send the
- // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer
- // was called with.
- sp<IBinder> releaseBufferEndpoint;
-
// Force inputflinger to drop all input events for the layer and its children.
gui::DropInputMode dropInputMode;
};
@@ -272,11 +287,12 @@
DisplayState();
void merge(const DisplayState& other);
- uint32_t what;
+ uint32_t what = 0;
+ uint32_t flags = 0;
sp<IBinder> token;
sp<IGraphicBufferProducer> surface;
- uint32_t layerStack;
- uint32_t flags;
+
+ ui::LayerStack layerStack = ui::DEFAULT_LAYER_STACK;
// These states define how layers are projected onto the physical display.
//
@@ -290,10 +306,11 @@
// will be additionally rotated by 90 degrees around the origin clockwise and translated by (W,
// 0).
ui::Rotation orientation = ui::ROTATION_0;
- Rect layerStackSpaceRect;
- Rect orientedDisplaySpaceRect;
+ Rect layerStackSpaceRect = Rect::EMPTY_RECT;
+ Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
- uint32_t width, height;
+ uint32_t width = 0;
+ uint32_t height = 0;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index e540351..40d096e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -429,11 +429,11 @@
uint32_t mReqHeight;
// mReqFormat is the buffer pixel format that will be requested at the next
- // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
+ // dequeue operation. It is initialized to PIXEL_FORMAT_RGBA_8888.
PixelFormat mReqFormat;
// mReqUsage is the set of buffer usage flags that will be requested
- // at the next deuque operation. It is initialized to 0.
+ // at the next dequeue operation. It is initialized to 0.
uint64_t mReqUsage;
// mTimestamp is the timestamp that will be used for the next buffer queue
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 9af9e64..17b4846 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <set>
+#include <thread>
#include <unordered_map>
#include <unordered_set>
@@ -50,10 +51,11 @@
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
-class IRegionSamplingListener;
class ITunnelModeEnabledListener;
class Region;
+using gui::IRegionSamplingListener;
+
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
@@ -84,7 +86,7 @@
const std::vector<SurfaceControlStats>& /*stats*/)>;
using ReleaseBufferCallback =
std::function<void(const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/,
- uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount)>;
+ std::optional<uint32_t> currentMaxAcquiredBufferCount)>;
using SurfaceStatsCallback =
std::function<void(void* /*context*/, nsecs_t /*latchTime*/,
@@ -93,6 +95,22 @@
// ---------------------------------------------------------------------------
+class ReleaseCallbackThread {
+public:
+ void addReleaseCallback(const ReleaseCallbackId, sp<Fence>);
+ void threadMain();
+
+private:
+ std::thread mThread;
+ std::mutex mMutex;
+ bool mStarted GUARDED_BY(mMutex) = false;
+ std::condition_variable mReleaseCallbackPending;
+ std::queue<std::tuple<const ReleaseCallbackId, const sp<Fence>>> mCallbackInfos
+ GUARDED_BY(mMutex);
+};
+
+// ---------------------------------------------------------------------------
+
class SurfaceComposerClient : public RefBase
{
friend class Composer;
@@ -348,8 +366,7 @@
class Transaction : public Parcelable {
private:
- static std::atomic<uint32_t> idCounter;
- int64_t generateId();
+ void releaseBufferIfOverwriting(const layer_state_t& state);
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -399,9 +416,7 @@
void cacheBuffers();
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
- void setReleaseBufferCallback(layer_state_t*, const ReleaseCallbackId&,
- ReleaseBufferCallback);
- void removeReleaseBufferCallback(layer_state_t*);
+ void setReleaseBufferCallback(BufferData*, const ReleaseCallbackId&, ReleaseBufferCallback);
public:
Transaction();
@@ -457,7 +472,7 @@
int backgroundBlurRadius);
Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
const std::vector<BlurRegion>& regions);
- Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
+ Transaction& setLayerStack(const sp<SurfaceControl>&, ui::LayerStack);
Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
/// Reparents the current layer to the new parent handle. The new parent must not be null.
@@ -473,10 +488,11 @@
Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc,
bool transformToDisplayInverse);
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
+ const std::optional<sp<Fence>>& fence = std::nullopt,
+ const std::optional<uint64_t>& frameNumber = std::nullopt,
const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
ReleaseBufferCallback callback = nullptr);
- Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId);
- Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence);
+ std::optional<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
@@ -501,8 +517,6 @@
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
- // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour.
- Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber);
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info);
Transaction& setFocusedWindow(const gui::FocusRequest& request);
@@ -568,7 +582,7 @@
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
- void setDisplayLayerStack(const sp<IBinder>& token, uint32_t layerStack);
+ void setDisplayLayerStack(const sp<IBinder>& token, ui::LayerStack);
void setDisplayFlags(const sp<IBinder>& token, uint32_t flags);
@@ -628,6 +642,9 @@
status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+protected:
+ ReleaseCallbackThread mReleaseCallbackThread;
+
private:
virtual void onFirstRef();
@@ -640,12 +657,9 @@
class ScreenshotClient {
public:
- static status_t captureDisplay(const DisplayCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener);
- static status_t captureLayers(const LayerCaptureArgs& captureArgs,
- const sp<IScreenCaptureListener>& captureListener);
+ static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&);
+ static status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&);
+ static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&);
};
// ---------------------------------------------------------------------------
@@ -731,13 +745,14 @@
void removeSurfaceStatsListener(void* context, void* cookie);
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
- void removeReleaseBufferCallback(const ReleaseCallbackId&);
// BnTransactionCompletedListener overrides
void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint,
+ void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) override;
+ void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
+
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 4727740..54a372c 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -71,8 +71,9 @@
SLIPPERY = 0x20000000,
LAYOUT_ATTACHED_IN_DECOR = 0x40000000,
DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000,
- }; // Window types from WindowManager.LayoutParams
+ };
+ // Window types from WindowManager.LayoutParams
enum class Type : int32_t {
UNKNOWN = 0,
FIRST_APPLICATION_WINDOW = 1,
@@ -87,40 +88,50 @@
APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
LAST_SUB_WINDOW = 1999,
- FIRST_SYSTEM_WINDOW = 2000,
- STATUS_BAR = FIRST_SYSTEM_WINDOW,
- SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
- PHONE = FIRST_SYSTEM_WINDOW + 2,
- SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
- KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
- TOAST = FIRST_SYSTEM_WINDOW + 5,
- SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
- PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
- SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
- KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
- SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
- INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
- INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
- WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
- STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
- SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
- DRAG = FIRST_SYSTEM_WINDOW + 16,
- STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
- POINTER = FIRST_SYSTEM_WINDOW + 18,
- NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
- VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
- BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
- INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
- NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
- MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
- ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
- DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
- ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
- NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+
+#define FIRST_SYSTEM_WINDOW_ 2000
+
+ STATUS_BAR = FIRST_SYSTEM_WINDOW_,
+ SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1,
+ PHONE = FIRST_SYSTEM_WINDOW_ + 2,
+ SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3,
+ KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4,
+ TOAST = FIRST_SYSTEM_WINDOW_ + 5,
+ SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6,
+ PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7,
+ SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8,
+ KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9,
+ SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10,
+ INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11,
+ INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12,
+ WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13,
+ STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14,
+ SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15,
+ DRAG = FIRST_SYSTEM_WINDOW_ + 16,
+ STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17,
+ POINTER = FIRST_SYSTEM_WINDOW_ + 18,
+ NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19,
+ VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20,
+ BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21,
+ INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22,
+ NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24,
+ MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27,
+ ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32,
+ DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34,
+ ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39,
+ NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40,
+
+ FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_,
LAST_SYSTEM_WINDOW = 2999,
+
+#undef FIRST_SYSTEM_WINDOW_
+
+ // Small range to limit LUT size.
+ ftl_first = FIRST_SYSTEM_WINDOW,
+ ftl_last = FIRST_SYSTEM_WINDOW + 15
};
- enum class Feature {
+ enum class Feature : uint32_t {
DISABLE_TOUCH_PAD_GESTURES = 1u << 0,
NO_INPUT_CHANNEL = 1u << 1,
DISABLE_USER_ACTIVITY = 1u << 2,
@@ -170,13 +181,6 @@
// Transform applied to individual windows.
ui::Transform transform;
- // Display orientation as ui::Transform::RotationFlags. Used for compatibility raw coordinates.
- uint32_t displayOrientation = ui::Transform::ROT_0;
-
- // Display size in its natural rotation. Used to rotate raw coordinates for compatibility.
- int32_t displayWidth = 0;
- int32_t displayHeight = 0;
-
/*
* This is filled in by the WM relative to the frame and then translated
* to absolute coordinates by SurfaceFlinger once the frame is computed.
@@ -267,4 +271,4 @@
WindowInfo mInfo;
};
-} // namespace android::gui
\ No newline at end of file
+} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfosListener.h b/libs/gui/include/gui/WindowInfosListener.h
index 8a70b9b..a18a498 100644
--- a/libs/gui/include/gui/WindowInfosListener.h
+++ b/libs/gui/include/gui/WindowInfosListener.h
@@ -16,6 +16,7 @@
#pragma once
+#include <gui/DisplayInfo.h>
#include <gui/WindowInfo.h>
#include <utils/RefBase.h>
@@ -23,6 +24,7 @@
class WindowInfosListener : public virtual RefBase {
public:
- virtual void onWindowInfosChanged(const std::vector<WindowInfo>& /*windowInfos*/) = 0;
+ virtual void onWindowInfosChanged(const std::vector<WindowInfo>&,
+ const std::vector<DisplayInfo>&) = 0;
};
} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h
index 7cb96e0..157a804 100644
--- a/libs/gui/include/gui/WindowInfosListenerReporter.h
+++ b/libs/gui/include/gui/WindowInfosListenerReporter.h
@@ -21,7 +21,6 @@
#include <binder/IBinder.h>
#include <gui/ISurfaceComposer.h>
#include <gui/WindowInfosListener.h>
-#include <utils/Mutex.h>
#include <unordered_set>
namespace android {
@@ -30,7 +29,8 @@
class WindowInfosListenerReporter : public gui::BnWindowInfosListener {
public:
static sp<WindowInfosListenerReporter> getInstance();
- binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos,
+ binder::Status onWindowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&,
const sp<gui::IWindowInfosReportedListener>&) override;
status_t addWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener,
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 3d26c3d..6dd1073 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -27,6 +27,7 @@
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
+ "DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
@@ -62,7 +63,7 @@
"libinput",
"libui",
"libutils",
- "libnativewindow"
+ "libnativewindow",
],
header_libs: ["libsurfaceflinger_headers"],
@@ -117,7 +118,7 @@
"libgui",
"libui",
"libutils",
- "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
+ "libbufferhubqueue", // TODO(b/70046255): Remove these once BufferHub is integrated into libgui.
"libpdx_default_transport",
],
@@ -146,5 +147,5 @@
"liblog",
"libui",
"libutils",
- ]
+ ],
}
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index fc75485..48b8621 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -66,26 +66,58 @@
int32_t mNumReleased GUARDED_BY(mMutex) = 0;
};
+class TestBLASTBufferQueue : public BLASTBufferQueue {
+public:
+ TestBLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
+ int height, int32_t format)
+ : BLASTBufferQueue(name, surface, width, height, format) {}
+
+ void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) override {
+ BLASTBufferQueue::transactionCallback(latchTime, presentFence, stats);
+ uint64_t frameNumber = stats[0].frameEventStats.frameNumber;
+
+ {
+ std::unique_lock lock{frameNumberMutex};
+ mLastTransactionFrameNumber = frameNumber;
+ mWaitForCallbackCV.notify_all();
+ }
+ }
+
+ void waitForCallback(int64_t frameNumber) {
+ std::unique_lock lock{frameNumberMutex};
+ // Wait until all but one of the submitted buffers have been released.
+ while (mLastTransactionFrameNumber < frameNumber) {
+ mWaitForCallbackCV.wait(lock);
+ }
+ }
+
+private:
+ std::mutex frameNumberMutex;
+ std::condition_variable mWaitForCallbackCV;
+ int64_t mLastTransactionFrameNumber = -1;
+};
+
class BLASTBufferQueueHelper {
public:
BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
- mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height,
- PIXEL_FORMAT_RGBA_8888);
+ mBlastBufferQueueAdapter = new TestBLASTBufferQueue("TestBLASTBufferQueue", sc, width,
+ height, PIXEL_FORMAT_RGBA_8888);
}
void update(const sp<SurfaceControl>& sc, int width, int height) {
mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
}
- void setNextTransaction(Transaction* next) {
- mBlastBufferQueueAdapter->setNextTransaction(next);
+ void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
+ mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
}
int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
- Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+ Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -107,28 +139,17 @@
}
}
- void setTransactionCompleteCallback(int64_t frameNumber) {
- mBlastBufferQueueAdapter->setTransactionCompleteCallback(frameNumber, [&](int64_t frame) {
- std::unique_lock lock{mMutex};
- mLastTransactionCompleteFrameNumber = frame;
- mCallbackCV.notify_all();
- });
+ void waitForCallback(int64_t frameNumber) {
+ mBlastBufferQueueAdapter->waitForCallback(frameNumber);
}
- void waitForCallback(int64_t frameNumber) {
- std::unique_lock lock{mMutex};
- // Wait until all but one of the submitted buffers have been released.
- while (mLastTransactionCompleteFrameNumber < frameNumber) {
- mCallbackCV.wait(lock);
- }
+ void validateNumFramesSubmitted(int64_t numFramesSubmitted) {
+ std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
private:
- sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
-
- std::mutex mMutex;
- std::condition_variable mCallbackCV;
- int64_t mLastTransactionCompleteFrameNumber = -1;
+ sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
class BLASTBufferQueueTest : public ::testing::Test {
@@ -152,7 +173,7 @@
mDisplayToken = mClient->getInternalDisplayToken();
ASSERT_NE(nullptr, mDisplayToken.get());
Transaction t;
- t.setDisplayLayerStack(mDisplayToken, 0);
+ t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
t.apply();
t.clear();
@@ -166,7 +187,7 @@
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
/*parent*/ nullptr);
- t.setLayerStack(mSurfaceControl, 0)
+ t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
.show(mSurfaceControl)
.setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
@@ -282,7 +303,7 @@
auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
nullptr, nullptr);
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR);
ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
uint32_t* bufData;
@@ -321,7 +342,7 @@
ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
ASSERT_EQ(mDisplayWidth, adapter.getWidth());
ASSERT_EQ(mDisplayHeight, adapter.getHeight());
- ASSERT_EQ(nullptr, adapter.getNextTransaction());
+ ASSERT_EQ(nullptr, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, Update) {
@@ -342,11 +363,11 @@
ASSERT_EQ(mDisplayHeight / 2, height);
}
-TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- Transaction next;
- adapter.setNextTransaction(&next);
- ASSERT_EQ(&next, adapter.getNextTransaction());
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
+ ASSERT_EQ(&sync, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -516,7 +537,7 @@
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -571,7 +592,7 @@
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -638,7 +659,7 @@
ISurfaceComposerClient::eFXSurfaceEffect);
ASSERT_NE(nullptr, bg.get());
Transaction t;
- t.setLayerStack(bg, 0)
+ t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
.setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
.setColor(bg, half3{0, 0, 0})
.setLayer(bg, 0)
@@ -785,8 +806,8 @@
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
// queue non sync buffer, so this one should get blocked
@@ -795,14 +816,14 @@
queueBuffer(igbProducer, r, g, b, presentTimeDelay);
CallbackHelper transactionCallback;
- next.addTransactionCompletedCallback(transactionCallback.function,
+ sync.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext())
.apply();
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
- // capture screen and verify that it is red
+ // capture screen and verify that it is green
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -825,16 +846,16 @@
Transaction mainTransaction;
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 1 buffer to be released even before sending to SurfaceFlinger
mProducerListener->waitOnNumberReleased(1);
@@ -865,24 +886,24 @@
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue another buffer without setting next transaction
+ // queue another buffer without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 1 buffer to be released because the non sync transaction should merge
// with the sync
mProducerListener->waitOnNumberReleased(1);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 2 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(2);
@@ -913,26 +934,26 @@
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 3 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 4 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(4);
@@ -970,14 +991,14 @@
// Send a buffer to SF
queueBuffer(igbProducer, 0, 255, 0, 0);
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
@@ -986,13 +1007,13 @@
mainTransaction.apply();
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 2 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
CallbackHelper transactionCallback;
mainTransaction
@@ -1009,6 +1030,45 @@
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
+TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction next;
+ adapter.setSyncTransaction(&next, false);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ // There should only be one frame submitted since the first frame will be released.
+ adapter.validateNumFramesSubmitted(1);
+ adapter.setSyncTransaction(nullptr);
+
+ // queue non sync buffer, so this one should get blocked
+ // Add a present delay to allow the first screenshot to get taken.
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(igbProducer, 255, 0, 0, presentTimeDelay);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is blue
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+ mProducerListener->waitOnNumberReleased(2);
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
class TestProducerListener : public BnProducerListener {
public:
sp<IGraphicBufferProducer> mIgbp;
@@ -1070,7 +1130,7 @@
ISurfaceComposerClient::eFXSurfaceBufferState);
ASSERT_NE(nullptr, bgSurface.get());
Transaction t;
- t.setLayerStack(bgSurface, 0)
+ t.setLayerStack(bgSurface, ui::DEFAULT_LAYER_STACK)
.show(bgSurface)
.setDataspace(bgSurface, ui::Dataspace::V0_SRGB)
.setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1)
@@ -1366,7 +1426,6 @@
IGraphicBufferProducer::QueueBufferOutput qbOutput;
nsecs_t requestedPresentTimeA = 0;
nsecs_t postedTimeA = 0;
- adapter.setTransactionCompleteCallback(1);
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
@@ -1435,7 +1494,6 @@
// queue another buffer so the first can be dropped
nsecs_t requestedPresentTimeB = 0;
nsecs_t postedTimeB = 0;
- adapter.setTransactionCompleteCallback(2);
presentTime = systemTime() + std::chrono::nanoseconds(1ms).count();
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true,
presentTime);
@@ -1501,7 +1559,6 @@
IGraphicBufferProducer::QueueBufferOutput qbOutput;
nsecs_t requestedPresentTimeA = 0;
nsecs_t postedTimeA = 0;
- adapter.setTransactionCompleteCallback(1);
setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
history.applyDelta(qbOutput.frameTimestamps);
adapter.waitForCallback(1);
diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp
new file mode 100644
index 0000000..df3329c
--- /dev/null
+++ b/libs/gui/tests/DisplayInfo_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+
+#include <gui/DisplayInfo.h>
+
+namespace android {
+
+using gui::DisplayInfo;
+
+namespace test {
+
+TEST(DisplayInfo, Parcelling) {
+ DisplayInfo info;
+ info.displayId = 42;
+ info.logicalWidth = 99;
+ info.logicalHeight = 78;
+ info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
+
+ Parcel p;
+ info.writeToParcel(&p);
+ p.setDataPosition(0);
+
+ DisplayInfo info2;
+ info2.readFromParcel(&p);
+ ASSERT_EQ(info.displayId, info2.displayId);
+ ASSERT_EQ(info.logicalWidth, info2.logicalWidth);
+ ASSERT_EQ(info.logicalHeight, info2.logicalHeight);
+ ASSERT_EQ(info.transform, info2.transform);
+}
+
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5daef0d..f960e07 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -220,7 +220,7 @@
t.apply(true);
}
- void requestFocus() {
+ void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
SurfaceComposerClient::Transaction t;
FocusRequest request;
request.token = mInputInfo.token;
@@ -228,7 +228,7 @@
request.focusedToken = nullptr;
request.focusedWindowName = "";
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- request.displayId = 0;
+ request.displayId = displayId;
t.setFocusedWindow(request);
t.apply(true);
}
@@ -255,11 +255,6 @@
mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
- // TODO: Fill in from SF?
- mInputInfo.ownerPid = 11111;
- mInputInfo.ownerUid = 11111;
- mInputInfo.displayId = 0;
-
InputApplicationInfo aInfo;
aInfo.token = new BBinder();
aInfo.name = "Test app info";
@@ -373,21 +368,31 @@
int32_t mBufferPostDelay;
};
-void injectTap(int x, int y) {
- char *buf1, *buf2;
+void injectTapOnDisplay(int x, int y, int displayId) {
+ char *buf1, *buf2, *bufDisplayId;
asprintf(&buf1, "%d", x);
asprintf(&buf2, "%d", y);
+ asprintf(&bufDisplayId, "%d", displayId);
if (fork() == 0) {
- execlp("input", "input", "tap", buf1, buf2, NULL);
+ execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL);
+ }
+}
+
+void injectTap(int x, int y) {
+ injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
+}
+
+void injectKeyOnDisplay(uint32_t keycode, int displayId) {
+ char *buf1, *bufDisplayId;
+ asprintf(&buf1, "%d", keycode);
+ asprintf(&bufDisplayId, "%d", displayId);
+ if (fork() == 0) {
+ execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL);
}
}
void injectKey(uint32_t keycode) {
- char *buf1;
- asprintf(&buf1, "%d", keycode);
- if (fork() == 0) {
- execlp("input", "input", "keyevent", buf1, NULL);
- }
+ injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE);
}
TEST_F(InputSurfacesTest, can_receive_input) {
@@ -564,7 +569,7 @@
bufferSurface->expectTap(1, 1);
}
-TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) {
+TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) {
std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
std::unique_ptr<BlastInputSurface> bufferSurface =
BlastInputSurface::makeBlastInputSurface(mComposerClient, 100, 100);
@@ -579,7 +584,7 @@
bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
- bufferSurface->expectTap(1, 1);
+ bgSurface->expectTap(1, 1);
}
TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) {
@@ -960,4 +965,74 @@
injectKey(AKEYCODE_V);
EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
+
+class MultiDisplayTests : public InputSurfacesTest {
+public:
+ MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
+ void TearDown() {
+ if (mVirtualDisplay) {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ }
+ InputSurfacesTest::TearDown();
+ }
+
+ void createDisplay(int32_t width, int32_t height, bool isSecure, ui::LayerStack layerStack) {
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(width, height);
+
+ mVirtualDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), isSecure);
+ SurfaceComposerClient::Transaction t;
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayFlags(mVirtualDisplay, 0x01 /* DisplayDevice::eReceivesInput */);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.apply(true);
+ }
+
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+};
+
+TEST_F(MultiDisplayTests, drop_input_for_secure_layer_on_nonsecure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, false /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ EXPECT_EQ(surface->consumeEvent(100), nullptr);
+}
+
+TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
+ ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
+ createDisplay(1000, 1000, true /*isSecure*/, layerStack);
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->doTransaction([&](auto &t, auto &sc) {
+ t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ t.setLayerStack(sc, layerStack);
+ });
+ surface->showAt(100, 100);
+
+ injectTapOnDisplay(101, 101, layerStack.id);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+ EXPECT_NE(surface->consumeEvent(), nullptr);
+
+ surface->requestFocus(layerStack.id);
+ surface->assertFocusChange(true);
+ injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+
+ surface->expectKey(AKEYCODE_V);
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 6746b0a..c9106be 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -17,9 +17,9 @@
#include <gtest/gtest.h>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/ProcessState.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -135,12 +135,13 @@
std::atomic<bool> poll_{true};
};
-struct Listener : BnRegionSamplingListener {
- void onSampleCollected(float medianLuma) override {
+struct Listener : android::gui::BnRegionSamplingListener {
+ binder::Status onSampleCollected(float medianLuma) override {
std::unique_lock<decltype(mutex)> lk(mutex);
received = true;
mLuma = medianLuma;
cv.notify_all();
+ return binder::Status::ok();
};
bool wait_event(std::chrono::milliseconds timeout) {
std::unique_lock<decltype(mutex)> lk(mutex);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 0cd150d..a083a22 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -20,9 +20,9 @@
#include <chrono>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
@@ -33,7 +33,7 @@
namespace android {
-class Button : public BnRegionSamplingListener {
+class Button : public gui::BnRegionSamplingListener {
public:
Button(const char* name, const Rect& samplingArea) {
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
@@ -99,9 +99,10 @@
.apply();
}
- void onSampleCollected(float medianLuma) override {
+ binder::Status onSampleCollected(float medianLuma) override {
ATRACE_CALL();
setColor(medianLuma);
+ return binder::Status::ok();
}
sp<SurfaceComposerClient> mClient;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c745505..d6ac3f9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,11 +19,11 @@
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
#include <gui/BufferItemConsumer.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -45,6 +45,8 @@
// retrieve wide-color and hdr settings from configstore
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using ui::ColorMode;
using Transaction = SurfaceComposerClient::Transaction;
@@ -753,21 +755,19 @@
}
status_t setActiveColorMode(const sp<IBinder>& /*display*/,
ColorMode /*colorMode*/) override { return NO_ERROR; }
- status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
- return NO_ERROR;
- }
void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
- status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
- virtual status_t captureLayers(
- const LayerCaptureArgs& /* captureArgs */,
- const sp<IScreenCaptureListener>& /* captureListener */) override {
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override {
return NO_ERROR;
}
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override {
+ return NO_ERROR;
+ }
+
status_t clearAnimationFrameStats() override { return NO_ERROR; }
status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override {
return NO_ERROR;
@@ -887,10 +887,6 @@
return NO_ERROR;
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
- return NO_ERROR;
- }
-
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/,
const FrameTimelineInfo& /*frameTimelineInfo*/) override {
return NO_ERROR;
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index a4f436c..dcdf76f 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -60,9 +60,6 @@
i.globalScaleFactor = 0.3;
i.alpha = 0.7;
i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
- i.displayOrientation = ui::Transform::ROT_0;
- i.displayWidth = 1000;
- i.displayHeight = 2000;
i.visible = false;
i.focusable = false;
i.hasWallpaper = false;
@@ -100,8 +97,6 @@
ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
ASSERT_EQ(i.alpha, i2.alpha);
ASSERT_EQ(i.transform, i2.transform);
- ASSERT_EQ(i.displayWidth, i2.displayWidth);
- ASSERT_EQ(i.displayHeight, i2.displayHeight);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.focusable, i2.focusable);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 5f440b7..f0b97a7 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,12 +20,11 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <inttypes.h>
-#include <limits.h>
#include <string.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gui/constants.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -43,15 +42,6 @@
namespace {
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-
- return PER_WINDOW_INPUT_ROTATION;
-}
-
float transformAngle(const ui::Transform& transform, float angleRadians) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
@@ -71,40 +61,17 @@
return atan2f(transformedPoint.x, -transformedPoint.y);
}
-// Rotates the given point to the specified orientation. If the display width and height are
-// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the
-// origin. This helper is used to avoid the extra overhead of creating new Transforms.
-vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0,
- int32_t displayHeight = 0) {
- if (orientation == ui::Transform::ROT_0) {
- return {x, y};
- }
-
- vec2 xy(x, y);
- if (orientation == ui::Transform::ROT_90) {
- xy.x = displayHeight - y;
- xy.y = x;
- } else if (orientation == ui::Transform::ROT_180) {
- xy.x = displayWidth - x;
- xy.y = displayHeight - y;
- } else if (orientation == ui::Transform::ROT_270) {
- xy.x = y;
- xy.y = displayWidth - x;
- }
- return xy;
+bool shouldDisregardTransformation(uint32_t source) {
+ // Do not apply any transformations to axes from joysticks or touchpads.
+ return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
}
-vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) {
- const vec2 transformedXy = transform.transform(x, y);
- const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
-}
-
-bool shouldDisregardWindowTranslation(uint32_t source) {
+bool shouldDisregardOffset(uint32_t source) {
// Pointer events are the only type of events that refer to absolute coordinates on the display,
// so we should apply the entire window transform. For other types of events, we should make
// sure to not apply the window translation/offset.
- return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+ return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
} // namespace
@@ -148,6 +115,12 @@
// --- InputEvent ---
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
+ const vec2 transformedXy = transform.transform(xy);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
const char* inputEventTypeToString(int32_t type) {
switch (type) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -165,16 +138,62 @@
case AINPUT_EVENT_TYPE_DRAG: {
return "DRAG";
}
+ case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+ return "TOUCH_MODE";
+ }
}
return "UNKNOWN";
}
+std::string inputEventSourceToString(int32_t source) {
+ if (source == AINPUT_SOURCE_UNKNOWN) {
+ return "UNKNOWN";
+ }
+ if (source == static_cast<int32_t>(AINPUT_SOURCE_ANY)) {
+ return "ANY";
+ }
+ static const std::map<int32_t, const char*> SOURCES{
+ {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"},
+ {AINPUT_SOURCE_DPAD, "DPAD"},
+ {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"},
+ {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"},
+ {AINPUT_SOURCE_MOUSE, "MOUSE"},
+ {AINPUT_SOURCE_STYLUS, "STYLUS"},
+ {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"},
+ {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"},
+ {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"},
+ {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"},
+ {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"},
+ {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"},
+ {AINPUT_SOURCE_HDMI, "HDMI"},
+ {AINPUT_SOURCE_SENSOR, "SENSOR"},
+ {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"},
+ };
+ std::string result;
+ for (const auto& [source_entry, str] : SOURCES) {
+ if ((source & source_entry) == source_entry) {
+ if (!result.empty()) {
+ result += " | ";
+ }
+ result += str;
+ }
+ }
+ if (result.empty()) {
+ result = StringPrintf("0x%08x", source);
+ }
+ return result;
+}
+
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
event.getAction(),
- event.getDownTime(),
event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+ event.getDownTime(),
event.getKeyCode(),
event.getScanCode(),
event.getMetaState(),
@@ -187,8 +206,8 @@
event.getRawX(0),
event.getRawY(0),
event.getActionMasked(),
- event.getDownTime(),
event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+ event.getDownTime(),
event.getMetaState(),
event.getButtonState()};
}
@@ -325,10 +344,6 @@
scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
}
-void PointerCoords::scale(float globalScaleFactor) {
- scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
-}
-
void PointerCoords::applyOffset(float xOffset, float yOffset) {
setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset);
setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
@@ -427,8 +442,7 @@
int32_t buttonState, MotionClassification classification,
const ui::Transform& transform, float xPrecision, float yPrecision,
float rawXCursorPosition, float rawYCursorPosition,
- uint32_t displayOrientation, int32_t displayWidth,
- int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
size_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
@@ -444,9 +458,7 @@
mYPrecision = yPrecision;
mRawXCursorPosition = rawXCursorPosition;
mRawYCursorPosition = rawYCursorPosition;
- mDisplayOrientation = displayOrientation;
- mDisplayWidth = displayWidth;
- mDisplayHeight = displayHeight;
+ mRawTransform = rawTransform;
mDownTime = downTime;
mPointerProperties.clear();
mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -470,9 +482,7 @@
mYPrecision = other->mYPrecision;
mRawXCursorPosition = other->mRawXCursorPosition;
mRawYCursorPosition = other->mRawYCursorPosition;
- mDisplayOrientation = other->mDisplayOrientation;
- mDisplayWidth = other->mDisplayWidth;
- mDisplayHeight = other->mDisplayHeight;
+ mRawTransform = other->mRawTransform;
mDownTime = other->mDownTime;
mPointerProperties = other->mPointerProperties;
@@ -497,6 +507,24 @@
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
+int MotionEvent::getSurfaceRotation() const {
+ // The surface rotation is the rotation from the window's coordinate space to that of the
+ // display. Since the event's transform takes display space coordinates to window space, the
+ // returned surface rotation is the inverse of the rotation for the surface.
+ switch (mTransform.getOrientation()) {
+ case ui::Transform::ROT_0:
+ return DISPLAY_ORIENTATION_0;
+ case ui::Transform::ROT_90:
+ return DISPLAY_ORIENTATION_270;
+ case ui::Transform::ROT_180:
+ return DISPLAY_ORIENTATION_180;
+ case ui::Transform::ROT_270:
+ return DISPLAY_ORIENTATION_90;
+ default:
+ return -1;
+ }
+}
+
float MotionEvent::getXCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
return vals.x;
@@ -533,56 +561,14 @@
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- // For compatibility, convert raw coordinates into "oriented screen space". Once app
- // developers are educated about getRaw, we can consider removing this.
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY())
- : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth,
- mDisplayHeight);
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- // For compatibility, since we convert raw coordinates into "oriented screen space", we
- // need to convert the relative axes into the same orientation for consistency.
- const vec2 relativeXy = rotatePoint(mDisplayOrientation,
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
- const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
-
- if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = shouldDisregardWindowTranslation(mSource)
- ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY())
- : mTransform.transform(coords->getXYValue());
- static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
- return xy[axis];
- }
-
- if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
- const vec2 relativeXy =
- applyTransformWithoutTranslation(mTransform,
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_X),
- coords->getAxisValue(
- AMOTION_EVENT_AXIS_RELATIVE_Y));
- return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
- }
-
- return coords->getAxisValue(axis);
+ const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
+ return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -603,6 +589,8 @@
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
+ mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
+ mRawTransform.ty() * globalScaleFactor);
mXPrecision *= globalScaleFactor;
mYPrecision *= globalScaleFactor;
@@ -619,15 +607,6 @@
ui::Transform newTransform;
newTransform.set(matrix);
mTransform = newTransform * mTransform;
-
- // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the
- // orientation angle is not affected by the initial transformation set in the MotionEvent.
- std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
- [&newTransform](PointerCoords& c) {
- float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(newTransform, orientation));
- });
}
void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
@@ -704,9 +683,11 @@
mYPrecision = parcel->readFloat();
mRawXCursorPosition = parcel->readFloat();
mRawYCursorPosition = parcel->readFloat();
- mDisplayOrientation = parcel->readUint32();
- mDisplayWidth = parcel->readInt32();
- mDisplayHeight = parcel->readInt32();
+
+ result = android::readFromParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
mDownTime = parcel->readInt64();
mPointerProperties.clear();
@@ -766,9 +747,11 @@
parcel->writeFloat(mYPrecision);
parcel->writeFloat(mRawXCursorPosition);
parcel->writeFloat(mRawYCursorPosition);
- parcel->writeUint32(mDisplayOrientation);
- parcel->writeInt32(mDisplayWidth);
- parcel->writeInt32(mDisplayHeight);
+
+ result = android::writeToParcel(mRawTransform, *parcel);
+ if (result != OK) {
+ return result;
+ }
parcel->writeInt64(mDownTime);
for (size_t i = 0; i < pointerCount; i++) {
@@ -792,7 +775,7 @@
#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
@@ -830,9 +813,9 @@
case AMOTION_EVENT_ACTION_OUTSIDE:
return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
- return "POINTER_DOWN";
+ return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_POINTER_UP:
- return "POINTER_UP";
+ return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action));
case AMOTION_EVENT_ACTION_HOVER_MOVE:
return "HOVER_MOVE";
case AMOTION_EVENT_ACTION_SCROLL:
@@ -849,19 +832,61 @@
return android::base::StringPrintf("%" PRId32, action);
}
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+ : transform.transform(xy);
+}
+
+vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ if (shouldDisregardTransformation(source)) {
+ return xy;
+ }
+ return calculateTransformedXYUnchecked(source, transform, xy);
+}
+
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+ const ui::Transform& transform,
+ const PointerCoords& coords) {
+ if (shouldDisregardTransformation(source)) {
+ return coords.getAxisValue(axis);
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
+ const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
+ static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
+ return xy[axis];
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) {
+ const vec2 relativeXy =
+ transformWithoutTranslation(transform,
+ {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
+ return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y;
+ }
+
+ if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
+ return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ }
+
+ return coords.getAxisValue(axis);
+}
+
// --- FocusEvent ---
-void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
+void FocusEvent::initialize(int32_t id, bool hasFocus) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
- mInTouchMode = inTouchMode;
}
void FocusEvent::initialize(const FocusEvent& from) {
InputEvent::initialize(from);
mHasFocus = from.mHasFocus;
- mInTouchMode = from.mInTouchMode;
}
// --- CaptureEvent ---
@@ -894,6 +919,19 @@
mY = from.mY;
}
+// --- TouchModeEvent ---
+
+void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
+ InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE, INVALID_HMAC);
+ mIsInTouchMode = isInTouchMode;
+}
+
+void TouchModeEvent::initialize(const TouchModeEvent& from) {
+ InputEvent::initialize(from);
+ mIsInTouchMode = from.mIsInTouchMode;
+}
+
// --- PooledInputEventFactory ---
PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) :
@@ -948,6 +986,15 @@
return event;
}
+TouchModeEvent* PooledInputEventFactory::createTouchModeEvent() {
+ if (mTouchModeEventPool.empty()) {
+ return new TouchModeEvent();
+ }
+ TouchModeEvent* event = mTouchModeEventPool.front().release();
+ mTouchModeEventPool.pop();
+ return event;
+}
+
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
@@ -981,6 +1028,13 @@
return;
}
break;
+ case AINPUT_EVENT_TYPE_TOUCH_MODE:
+ if (mTouchModeEventPool.size() < mMaxPoolSize) {
+ mTouchModeEventPool.push(
+ std::unique_ptr<TouchModeEvent>(static_cast<TouchModeEvent*>(event)));
+ return;
+ }
+ break;
}
delete event;
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 1aec477..ac84627 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -21,7 +21,7 @@
#include <ctype.h>
#include <android-base/stringprintf.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -208,10 +208,8 @@
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
int32_t axis, uint32_t source) const {
- size_t numRanges = mMotionRanges.size();
- for (size_t i = 0; i < numRanges; i++) {
- const MotionRange& range = mMotionRanges[i];
- if (range.axis == axis && range.source == source) {
+ for (const MotionRange& range : mMotionRanges) {
+ if (range.axis == axis && isFromSource(range.source, source)) {
return ⦥
}
}
@@ -235,7 +233,7 @@
void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) {
if (mSensors.find(info.type) != mSensors.end()) {
ALOGW("Sensor type %s already exists, will be replaced by new sensor added.",
- NamedEnum::string(info.type).c_str());
+ ftl::enum_string(info.type).c_str());
}
mSensors.insert_or_assign(info.type, info);
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ea8b9a7..a065ce2 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -30,10 +30,10 @@
#include <android-base/stringprintf.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <log/log.h>
#include <utils/Trace.h>
-#include <ftl/NamedEnum.h>
#include <input/InputTransport.h>
using android::base::StringPrintf;
@@ -116,6 +116,7 @@
case Type::FOCUS:
case Type::CAPTURE:
case Type::DRAG:
+ case Type::TOUCH_MODE:
return true;
case Type::TIMELINE: {
const nsecs_t gpuCompletedTime =
@@ -151,6 +152,8 @@
return sizeof(Header) + body.drag.size();
case Type::TIMELINE:
return sizeof(Header) + body.timeline.size();
+ case Type::TOUCH_MODE:
+ return sizeof(Header) + body.touchMode.size();
}
return sizeof(Header);
}
@@ -200,6 +203,8 @@
case InputMessage::Type::MOTION: {
// int32_t eventId
msg->body.motion.eventId = body.motion.eventId;
+ // uint32_t pointerCount
+ msg->body.motion.pointerCount = body.motion.pointerCount;
// nsecs_t eventTime
msg->body.motion.eventTime = body.motion.eventTime;
// int32_t deviceId
@@ -242,14 +247,14 @@
msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
// float yCursorPosition
msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
- // uint32_t displayOrientation
- msg->body.motion.displayOrientation = body.motion.displayOrientation;
- // int32_t displayWidth
- msg->body.motion.displayWidth = body.motion.displayWidth;
- // int32_t displayHeight
- msg->body.motion.displayHeight = body.motion.displayHeight;
- // uint32_t pointerCount
- msg->body.motion.pointerCount = body.motion.pointerCount;
+
+ msg->body.motion.dsdxRaw = body.motion.dsdxRaw;
+ msg->body.motion.dtdxRaw = body.motion.dtdxRaw;
+ msg->body.motion.dtdyRaw = body.motion.dtdyRaw;
+ msg->body.motion.dsdyRaw = body.motion.dsdyRaw;
+ msg->body.motion.txRaw = body.motion.txRaw;
+ msg->body.motion.tyRaw = body.motion.tyRaw;
+
//struct Pointer pointers[MAX_POINTERS]
for (size_t i = 0; i < body.motion.pointerCount; i++) {
// PointerProperties properties
@@ -273,7 +278,6 @@
case InputMessage::Type::FOCUS: {
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
- msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
case InputMessage::Type::CAPTURE: {
@@ -293,6 +297,10 @@
msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
break;
}
+ case InputMessage::Type::TOUCH_MODE: {
+ msg->body.touchMode.eventId = body.touchMode.eventId;
+ msg->body.touchMode.isInTouchMode = body.touchMode.isInTouchMode;
+ }
}
}
@@ -535,8 +543,8 @@
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation,
- int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime,
+ float yPrecision, float xCursorPosition, float yCursorPosition,
+ const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
if (ATRACE_ENABLED()) {
@@ -596,9 +604,12 @@
msg.body.motion.yPrecision = yPrecision;
msg.body.motion.xCursorPosition = xCursorPosition;
msg.body.motion.yCursorPosition = yCursorPosition;
- msg.body.motion.displayOrientation = displayOrientation;
- msg.body.motion.displayWidth = displayWidth;
- msg.body.motion.displayHeight = displayHeight;
+ msg.body.motion.dsdxRaw = rawTransform.dsdx();
+ msg.body.motion.dtdxRaw = rawTransform.dtdx();
+ msg.body.motion.dtdyRaw = rawTransform.dtdy();
+ msg.body.motion.dsdyRaw = rawTransform.dsdy();
+ msg.body.motion.txRaw = rawTransform.tx();
+ msg.body.motion.tyRaw = rawTransform.ty();
msg.body.motion.downTime = downTime;
msg.body.motion.eventTime = eventTime;
msg.body.motion.pointerCount = pointerCount;
@@ -610,13 +621,10 @@
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus,
- bool inTouchMode) {
+status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
- mChannel->getName().c_str(), toString(hasFocus),
- toString(inTouchMode));
+ std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
+ mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
@@ -625,7 +633,6 @@
msg.header.seq = seq;
msg.body.focus.eventId = eventId;
msg.body.focus.hasFocus = hasFocus;
- msg.body.focus.inTouchMode = inTouchMode;
return mChannel->sendMessage(&msg);
}
@@ -665,6 +672,22 @@
return mChannel->sendMessage(&msg);
}
+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());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TOUCH_MODE;
+ msg.header.seq = seq;
+ msg.body.touchMode.eventId = eventId;
+ msg.body.touchMode.isInTouchMode = isInTouchMode;
+ return mChannel->sendMessage(&msg);
+}
+
android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
@@ -691,7 +714,7 @@
}
ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
- mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str());
return android::base::Error(UNKNOWN_ERROR);
}
@@ -833,7 +856,7 @@
case InputMessage::Type::TIMELINE: {
LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
"InputConsumer!",
- NamedEnum::string(mMsg.header.type).c_str());
+ ftl::enum_string(mMsg.header.type).c_str());
break;
}
@@ -866,6 +889,16 @@
*outEvent = dragEvent;
break;
}
+
+ case InputMessage::Type::TOUCH_MODE: {
+ TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+ if (!touchModeEvent) return NO_MEMORY;
+
+ initializeTouchModeEvent(touchModeEvent, &mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = touchModeEvent;
+ break;
+ }
}
}
return OK;
@@ -1333,8 +1366,7 @@
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
- msg->body.focus.inTouchMode);
+ event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
}
void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
@@ -1358,6 +1390,10 @@
ui::Transform transform;
transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
+ msg->body.motion.txRaw, msg->body.motion.dtdyRaw,
+ msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});
event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,
msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,
msg->body.motion.actionButton, msg->body.motion.flags,
@@ -1365,9 +1401,12 @@
msg->body.motion.buttonState, msg->body.motion.classification, transform,
msg->body.motion.xPrecision, msg->body.motion.yPrecision,
msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,
- msg->body.motion.displayOrientation, msg->body.motion.displayWidth,
- msg->body.motion.displayHeight, msg->body.motion.downTime,
- msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,
+ pointerCount, pointerProperties, pointerCoords);
+}
+
+void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
}
void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
@@ -1412,14 +1451,14 @@
out = out + "mChannel = " + mChannel->getName() + "\n";
out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
if (mMsgDeferred) {
- out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n";
+ out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
}
out += "Batches:\n";
for (const Batch& batch : mBatches) {
out += " Batch:\n";
for (const InputMessage& msg : batch.samples) {
out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
- NamedEnum::string(msg.header.type).c_str());
+ ftl::enum_string(msg.header.type).c_str());
switch (msg.header.type) {
case InputMessage::Type::KEY: {
out += android::base::StringPrintf("action=%s keycode=%" PRId32,
@@ -1446,9 +1485,8 @@
break;
}
case InputMessage::Type::FOCUS: {
- out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
- toString(msg.body.focus.hasFocus),
- toString(msg.body.focus.inTouchMode));
+ out += android::base::StringPrintf("hasFocus=%s",
+ toString(msg.body.focus.hasFocus));
break;
}
case InputMessage::Type::CAPTURE: {
@@ -1476,6 +1514,11 @@
presentTime);
break;
}
+ case InputMessage::Type::TOUCH_MODE: {
+ out += android::base::StringPrintf("isInTouchMode=%s",
+ toString(msg.body.touchMode.isInTouchMode));
+ break;
+ }
}
out += "\n";
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index c365ab0..7c25cda 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,10 +16,8 @@
#define LOG_TAG "KeyLayoutMap"
-#include <stdlib.h>
-
#include <android/keycodes.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <input/InputEventLabels.h>
#include <input/KeyLayoutMap.h>
#include <input/Keyboard.h>
@@ -28,6 +26,10 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
+#include <cstdlib>
+#include <string_view>
+#include <unordered_map>
+
// Enables debug output for the parser.
#define DEBUG_PARSER 0
@@ -39,36 +41,38 @@
namespace android {
+namespace {
-static const char* WHITESPACE = " \t\r";
+constexpr const char* WHITESPACE = " \t\r";
-#define SENSOR_ENTRY(type) NamedEnum::string(type), type
-static const std::unordered_map<std::string, InputDeviceSensorType> SENSOR_LIST =
- {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)},
- {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)},
- {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)},
- {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)},
- {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)},
- {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)},
- {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)},
- {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)},
- {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}};
-
-// --- KeyLayoutMap ---
-
-KeyLayoutMap::KeyLayoutMap() {
+template <InputDeviceSensorType S>
+constexpr auto sensorPair() {
+ return std::make_pair(ftl::enum_name<S>(), S);
}
-KeyLayoutMap::~KeyLayoutMap() {
-}
+static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_LIST =
+ {sensorPair<InputDeviceSensorType::ACCELEROMETER>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD>(),
+ sensorPair<InputDeviceSensorType::ORIENTATION>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE>(),
+ sensorPair<InputDeviceSensorType::LIGHT>(),
+ sensorPair<InputDeviceSensorType::PRESSURE>(),
+ sensorPair<InputDeviceSensorType::TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::PROXIMITY>(),
+ sensorPair<InputDeviceSensorType::GRAVITY>(),
+ sensorPair<InputDeviceSensorType::LINEAR_ACCELERATION>(),
+ sensorPair<InputDeviceSensorType::ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::RELATIVE_HUMIDITY>(),
+ sensorPair<InputDeviceSensorType::AMBIENT_TEMPERATURE>(),
+ sensorPair<InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::GAME_ROTATION_VECTOR>(),
+ sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(),
+ sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()};
+
+} // namespace
+
+KeyLayoutMap::KeyLayoutMap() = default;
+KeyLayoutMap::~KeyLayoutMap() = default;
base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
const char* contents) {
@@ -160,8 +164,8 @@
const Sensor& sensor = it->second;
#if DEBUG_MAPPING
- ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode,
- NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex);
+ ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode,
+ ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex);
#endif
return std::make_pair(sensor.sensorType, sensor.sensorDataIndex);
}
@@ -513,7 +517,7 @@
}
static std::optional<InputDeviceSensorType> getSensorType(const char* token) {
- auto it = SENSOR_LIST.find(std::string(token));
+ auto it = SENSOR_LIST.find(token);
if (it == SENSOR_LIST.end()) {
return std::nullopt;
}
@@ -581,8 +585,8 @@
int32_t sensorDataIndex = indexOpt.value();
#if DEBUG_PARSER
- ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code,
- NamedEnum::string(sensorType).c_str(), sensorDataIndex);
+ ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ftl::enum_string(sensorType).c_str(), sensorDataIndex);
#endif
Sensor sensor;
@@ -591,4 +595,5 @@
map.emplace(code, sensor);
return NO_ERROR;
}
-};
+
+} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a44f0b7..a6465ee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -18,10 +18,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
+static constexpr bool DEBUG_VELOCITY = false;
// Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
+static constexpr bool DEBUG_STRATEGY = false;
#include <array>
#include <inttypes.h>
@@ -30,7 +30,6 @@
#include <optional>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -64,7 +63,6 @@
return sqrtf(r);
}
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
static std::string vectorToString(const float* a, uint32_t m) {
std::string str;
str += "[";
@@ -77,9 +75,11 @@
str += " ]";
return str;
}
-#endif
-#if DEBUG_STRATEGY
+static std::string vectorToString(const std::vector<float>& v) {
+ return vectorToString(v.data(), v.size());
+}
+
static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
std::string str;
str = "[";
@@ -99,7 +99,6 @@
str += " ]";
return str;
}
-#endif
// --- VelocityTracker ---
@@ -133,12 +132,18 @@
VelocityTracker::Strategy strategy) {
switch (strategy) {
case VelocityTracker::Strategy::IMPULSE:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing impulse strategy");
+ }
return std::make_unique<ImpulseVelocityTrackerStrategy>();
case VelocityTracker::Strategy::LSQ1:
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing lsq2 strategy");
+ }
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
case VelocityTracker::Strategy::LSQ3:
@@ -204,10 +209,10 @@
if ((mCurrentPointerIdBits.value & idBits.value)
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
-#endif
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
+ (eventTime - mLastEventTime) * 0.000001f);
+ }
// We have not received any movements for too long. Assume that all pointers
// have stopped.
mStrategy->clear();
@@ -221,24 +226,24 @@
mStrategy->addMovement(eventTime, idBits, positions);
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
- eventTime, idBits.value, mActivePointerId);
- for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
- uint32_t id = iterBits.firstMarkedBit();
- uint32_t index = idBits.getIndexOfBit(id);
- iterBits.clearBit(id);
- Estimator estimator;
- getEstimator(id, &estimator);
- ALOGD(" %d: position (%0.3f, %0.3f), "
- "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
- id, positions[index].x, positions[index].y,
- int(estimator.degree),
- vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
- vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
- estimator.confidence);
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
+ ", idBits=0x%08x, activePointerId=%d",
+ eventTime, idBits.value, mActivePointerId);
+ for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+ uint32_t id = iterBits.firstMarkedBit();
+ uint32_t index = idBits.getIndexOfBit(id);
+ iterBits.clearBit(id);
+ Estimator estimator;
+ getEstimator(id, &estimator);
+ ALOGD(" %d: position (%0.3f, %0.3f), "
+ "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+ id, positions[index].x, positions[index].y, int(estimator.degree),
+ vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+ vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+ estimator.confidence);
+ }
}
-#endif
}
void VelocityTracker::addMovement(const MotionEvent* event) {
@@ -419,11 +424,10 @@
static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
const size_t m = x.size();
-#if DEBUG_STRATEGY
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
- vectorToString(w, m).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+ vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+ }
LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -434,9 +438,9 @@
a[i][h] = a[i - 1][h] * x[h];
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+ }
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -455,9 +459,9 @@
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
- ALOGD(" - no solution, norm=%f", norm);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - no solution, norm=%f", norm);
+ }
return false;
}
@@ -469,22 +473,22 @@
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
- ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
+ ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
- // calculate QR, if we factored A correctly then QR should equal A
- float qr[n][m];
- for (uint32_t h = 0; h < m; h++) {
- for (uint32_t i = 0; i < n; i++) {
- qr[i][h] = 0;
- for (uint32_t j = 0; j < n; j++) {
- qr[i][h] += q[j][h] * r[j][i];
+ // calculate QR, if we factored A correctly then QR should equal A
+ float qr[n][m];
+ for (uint32_t h = 0; h < m; h++) {
+ for (uint32_t i = 0; i < n; i++) {
+ qr[i][h] = 0;
+ for (uint32_t j = 0; j < n; j++) {
+ qr[i][h] += q[j][h] * r[j][i];
+ }
}
}
+ ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
}
- ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
// We just work from bottom-right to top-left calculating B's coefficients.
@@ -500,9 +504,9 @@
}
outB[i] /= r[i][i];
}
-#if DEBUG_STRATEGY
- ALOGD(" - b=%s", vectorToString(outB, n).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - b=%s", vectorToString(outB, n).c_str());
+ }
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
// SSerr is the residual sum of squares (variance of the error),
@@ -528,11 +532,11 @@
sstot += w[h] * w[h] * var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - sserr=%f", sserr);
+ ALOGD(" - sstot=%f", sstot);
+ ALOGD(" - det=%f", *outDet);
+ }
return true;
}
@@ -655,13 +659,11 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree),
- vectorToString(outEstimator->xCoeff, n).c_str(),
- vectorToString(outEstimator->yCoeff, n).c_str(),
- outEstimator->confidence);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+ int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+ vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+ }
return true;
}
}
@@ -1169,9 +1171,9 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
-#if DEBUG_STRATEGY
- ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ }
return true;
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index b1ef753..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -226,39 +226,23 @@
static constexpr float Y_SCALE = 3.0;
static constexpr float X_OFFSET = 1;
static constexpr float Y_OFFSET = 1.1;
-
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
+ static constexpr float RAW_X_SCALE = 4.0;
+ static constexpr float RAW_Y_SCALE = -5.0;
+ static constexpr float RAW_X_OFFSET = 12;
+ static constexpr float RAW_Y_OFFSET = -41.1;
int32_t mId;
ui::Transform mTransform;
-
- void SetUp() override;
- void TearDown() override;
+ ui::Transform mRawTransform;
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
- ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
- : std::nullopt;
-
-void MotionEventTest::SetUp() {
- // Ensure per_window_input_rotation is enabled.
- base::SetProperty("persist.debug.per_window_input_rotation", "true");
-}
-
-void MotionEventTest::TearDown() {
- const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
- ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
- : "";
- base::SetProperty("persist.debug.per_window_input_rotation", val);
-}
-
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
+ mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1});
PointerProperties pointerProperties[2];
pointerProperties[0].clear();
@@ -294,9 +278,8 @@
AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
MotionClassification::NONE, mTransform, 2.0f, 2.1f,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
- pointerCoords);
+ mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
+ pointerProperties, pointerCoords);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -373,39 +356,37 @@
ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1));
ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime());
- ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(211, event->getRawPointerCoords(0)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(221, event->getRawPointerCoords(1)->
- getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y));
- ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
- ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
- ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
- ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
- ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
- ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE,
+ event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1));
- ASSERT_EQ(10, event->getHistoricalRawX(0, 0));
- ASSERT_EQ(20, event->getHistoricalRawX(1, 0));
- ASSERT_EQ(110, event->getHistoricalRawX(0, 1));
- ASSERT_EQ(120, event->getHistoricalRawX(1, 1));
- ASSERT_EQ(210, event->getRawX(0));
- ASSERT_EQ(220, event->getRawX(1));
+ ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0));
+ ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1));
+ ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0));
+ ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1));
- ASSERT_EQ(11, event->getHistoricalRawY(0, 0));
- ASSERT_EQ(21, event->getHistoricalRawY(1, 0));
- ASSERT_EQ(111, event->getHistoricalRawY(0, 1));
- ASSERT_EQ(121, event->getHistoricalRawY(1, 1));
- ASSERT_EQ(211, event->getRawY(0));
- ASSERT_EQ(221, event->getRawY(1));
+ ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0));
+ ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1));
+ ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0));
+ ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1));
ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
@@ -463,12 +444,19 @@
ASSERT_EQ(217, event->getToolMinor(0));
ASSERT_EQ(227, event->getToolMinor(1));
- ASSERT_EQ(18, event->getHistoricalOrientation(0, 0));
- ASSERT_EQ(28, event->getHistoricalOrientation(1, 0));
- ASSERT_EQ(118, event->getHistoricalOrientation(0, 1));
- ASSERT_EQ(128, event->getHistoricalOrientation(1, 1));
- ASSERT_EQ(218, event->getOrientation(0));
- ASSERT_EQ(228, event->getOrientation(1));
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is "up",
+ // and the positive y direction is "down".
+ auto toScaledOrientation = [](float angle) {
+ const float x = sinf(angle) * X_SCALE;
+ const float y = -cosf(angle) * Y_SCALE;
+ return atan2f(x, -y);
+ };
+ ASSERT_EQ(toScaledOrientation(18), event->getHistoricalOrientation(0, 0));
+ ASSERT_EQ(toScaledOrientation(28), event->getHistoricalOrientation(1, 0));
+ ASSERT_EQ(toScaledOrientation(118), event->getHistoricalOrientation(0, 1));
+ ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1));
+ ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0));
+ ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1));
}
TEST_F(MotionEventTest, Properties) {
@@ -537,14 +525,15 @@
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float unscaledOrientation = event.getOrientation(0);
event.scale(2.0f);
ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
- ASSERT_EQ(210 * 2, event.getRawX(0));
- ASSERT_EQ(211 * 2, event.getRawY(0));
+ ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0));
+ ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0));
ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
ASSERT_EQ(212, event.getPressure(0));
@@ -553,7 +542,7 @@
ASSERT_EQ(215 * 2, event.getTouchMinor(0));
ASSERT_EQ(216 * 2, event.getToolMajor(0));
ASSERT_EQ(217 * 2, event.getToolMinor(0));
- ASSERT_EQ(218, event.getOrientation(0));
+ ASSERT_EQ(unscaledOrientation, event.getOrientation(0));
}
TEST_F(MotionEventTest, Parcel) {
@@ -592,10 +581,10 @@
// The geometrical representation is irrelevant to the test, it's just easy to generate
// and check rotation. We set the orientation to the same angle.
// Coordinate system: down is increasing Y, right is increasing X.
- const float PI_180 = float(M_PI / 180);
- const float RADIUS = 10;
- const float ARC = 36;
- const float ROTATION = ARC * 2;
+ static constexpr float PI_180 = float(M_PI / 180);
+ static constexpr float RADIUS = 10;
+ static constexpr float ARC = 36;
+ static constexpr float ROTATION = ARC * 2;
const size_t pointerCount = 11;
PointerProperties pointerProperties[pointerCount];
@@ -616,9 +605,8 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
- pointerCoords);
+ identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount,
+ pointerProperties, pointerCoords);
float originalRawX = 0 + 3;
float originalRawY = -RADIUS + 2;
@@ -659,9 +647,8 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
- const ui::Transform& transform,
- uint32_t displayOrientation = ui::Transform::ROT_0) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+ const ui::Transform& transform, const ui::Transform& rawTransform) {
std::vector<PointerProperties> pointerProperties;
pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
std::vector<PointerCoords> pointerCoords;
@@ -672,24 +659,30 @@
pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
- /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+ /* displayId */ 0, INVALID_HMAC, action,
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation,
- /* displayWidth */ 400,
- /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
- pointerProperties.data(), pointerCoords.data());
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime,
+ pointerCoords.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+ const ui::Transform& transform,
+ const ui::Transform& rawTransform) {
+ return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+ transform, rawTransform);
+}
+
TEST_F(MotionEventTest, ApplyTransform) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform identity;
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+ ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400);
+ MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
ASSERT_EQ(700, event.getRawX(0));
ASSERT_EQ(60, event.getRawY(0));
ASSERT_NE(event.getRawX(0), event.getX(0));
@@ -698,10 +691,10 @@
ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity);
- const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
- xform[0][1], xform[1][1], xform[2][1],
- xform[0][2], xform[1][2], xform[2][2]};
+ MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity);
+ const std::array<float, 9> rowMajor{transform[0][0], transform[1][0], transform[2][0],
+ transform[0][1], transform[1][1], transform[2][1],
+ transform[0][2], transform[1][2], transform[2][2]};
changedEvent.applyTransform(rowMajor);
// transformContent effectively rotates the raw coordinates, so those should now include
@@ -721,16 +714,39 @@
changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
}
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+ constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonTransformedSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+ // These events should not be transformed in any way.
+ ASSERT_EQ(60, event.getX(0));
+ ASSERT_EQ(100, event.getY(0));
+ ASSERT_EQ(event.getRawX(0), event.getX(0));
+ ASSERT_EQ(event.getRawY(0), event.getY(0));
+ }
+}
+
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
- constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
- AINPUT_SOURCE_MOUSE_RELATIVE,
- AINPUT_SOURCE_JOYSTICK};
- for (uint32_t source : NON_POINTER_SOURCES) {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- event.setSource(source);
+ constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonPointerSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
// Since this event comes from a non-pointer source, it should include rotation but not
// translation/offset.
@@ -741,72 +757,34 @@
}
}
-TEST_F(MotionEventTest, RawCompatTransform) {
- {
- // Make sure raw is raw regardless of transform translation.
- ui::Transform xform;
- xform.set(20, 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform);
- ASSERT_EQ(60, event.getRawX(0));
- ASSERT_EQ(100, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should not be modified.
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) {
+ const ui::Transform identity;
+ ui::Transform transform;
+ transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1});
+ auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) {
+ auto newPoint = t.transform(x, y);
+ auto newOrigin = t.transform(0, 0);
+ return newPoint - newOrigin;
+ };
- // Next check that getRaw contains rotation (for compatibility) but otherwise is still
- // "Screen-space". The following tests check all 3 rotations.
- {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform xform(ui::Transform::ROT_90, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- ASSERT_NE(event.getRawX(0), event.getX(0));
- ASSERT_NE(event.getRawY(0), event.getY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform);
- {
- // Same as above, but check rotate-180.
- ui::Transform xform(ui::Transform::ROT_180, 400, 800);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180);
- ASSERT_EQ(340, event.getRawX(0));
- ASSERT_EQ(700, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The x and y axes should have the window transform applied.
+ const auto newPoint = transform.transform(60, 100);
+ ASSERT_EQ(newPoint.x, event.getX(0));
+ ASSERT_EQ(newPoint.y, event.getY(0));
- {
- // Same as above, but check rotate-270.
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270);
- ASSERT_EQ(100, event.getRawX(0));
- ASSERT_EQ(340, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // The raw values should have the display transform applied.
+ const auto raw = rawTransform.transform(60, 100);
+ ASSERT_EQ(raw.x, event.getRawX(0));
+ ASSERT_EQ(raw.y, event.getRawY(0));
- {
- // Finally, check that raw isn't effected by transform
- ui::Transform xform(ui::Transform::ROT_270, 800, 400);
- xform.set(xform.tx() + 20, xform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90);
- ASSERT_EQ(700, event.getRawX(0));
- ASSERT_EQ(60, event.getRawY(0));
- // Relative values should be rotated but not translated.
- ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
- ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
- }
+ // Relative values should have the window transform applied without any translation.
+ const auto rel = transformWithoutTranslation(transform, 42, 96);
+ ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0));
+ ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0));
}
TEST_F(MotionEventTest, Initialize_SetsClassification) {
@@ -832,8 +810,7 @@
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(classification, event.getClassification());
}
@@ -854,9 +831,9 @@
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/,
- pointerCount, pointerProperties, pointerCoords);
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform,
+ 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties,
+ pointerCoords);
event.offsetLocation(20, 60);
ASSERT_EQ(280, event.getRawXCursorPosition());
ASSERT_EQ(540, event.getRawYCursorPosition());
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 5d1f2c3..05bc0bc 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -56,6 +56,7 @@
void PublishAndConsumeFocusEvent();
void PublishAndConsumeCaptureEvent();
void PublishAndConsumeDragEvent();
+ void PublishAndConsumeTouchModeEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -159,13 +160,14 @@
constexpr float yScale = 3;
constexpr float xOffset = -10;
constexpr float yOffset = -20;
+ constexpr float rawXScale = 4;
+ constexpr float rawYScale = -5;
+ constexpr float rawXOffset = -11;
+ constexpr float rawYOffset = 42;
constexpr float xPrecision = 0.25;
constexpr float yPrecision = 0.5;
constexpr float xCursorPosition = 1.3;
constexpr float yCursorPosition = 50.6;
- constexpr uint32_t displayOrientation = ui::Transform::ROT_0;
- constexpr int32_t displayWidth = 1000;
- constexpr int32_t displayHeight = 2000;
constexpr nsecs_t downTime = 3;
constexpr size_t pointerCount = 3;
constexpr nsecs_t eventTime = 4;
@@ -191,12 +193,14 @@
ui::Transform transform;
transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
+ ui::Transform rawTransform;
+ rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action,
actionButton, flags, edgeFlags, metaState, buttonState,
classification, transform, xPrecision, yPrecision,
- xCursorPosition, yCursorPosition, displayOrientation,
- displayWidth, displayHeight, downTime, eventTime,
- pointerCount, pointerProperties, pointerCoords);
+ xCursorPosition, yCursorPosition, rawTransform,
+ downTime, eventTime, pointerCount, pointerProperties,
+ pointerCoords);
ASSERT_EQ(OK, status)
<< "publisher publishMotionEvent should return OK";
@@ -233,9 +237,7 @@
EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
- EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation());
- EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x);
- EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y);
+ EXPECT_EQ(rawTransform, motionEvent->getRawTransform());
EXPECT_EQ(downTime, motionEvent->getDownTime());
EXPECT_EQ(eventTime, motionEvent->getEventTime());
EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -246,28 +248,24 @@
EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i));
EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- motionEvent->getRawX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- motionEvent->getRawY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
- motionEvent->getX(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
- motionEvent->getY(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- motionEvent->getPressure(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- motionEvent->getSize(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- motionEvent->getTouchMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- motionEvent->getTouchMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- motionEvent->getToolMajor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- motionEvent->getToolMinor(i));
- EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
- motionEvent->getOrientation(i));
+ const auto& pc = pointerCoords[i];
+ EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i));
+ EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i));
+ EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i));
+ EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i));
+
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is
+ // "up", and the positive y direction is "down".
+ const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ const float x = sinf(unscaledOrientation) * xScale;
+ const float y = -cosf(unscaledOrientation) * yScale;
+ EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i));
}
status = mConsumer->sendFinishedSignal(seq, false);
@@ -292,10 +290,9 @@
constexpr uint32_t seq = 15;
int32_t eventId = InputEvent::nextId();
constexpr bool hasFocus = true;
- constexpr bool inTouchMode = true;
const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
- status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
+ status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
uint32_t consumeSeq;
@@ -311,7 +308,6 @@
EXPECT_EQ(seq, consumeSeq);
EXPECT_EQ(eventId, focusEvent->getId());
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
- EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
@@ -413,6 +409,46 @@
<< "finished signal's consume time should be greater than publish time";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeTouchModeEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool touchModeEnabled = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+ << "consumer should have returned a touch mode event";
+
+ const TouchModeEvent& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(eventId, touchModeEvent.getId());
+ EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
const int32_t inputEventId = 20;
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
@@ -449,6 +485,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishTouchModeEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -460,12 +500,12 @@
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -477,12 +517,12 @@
PointerCoords pointerCoords[pointerCount];
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -499,12 +539,12 @@
}
ui::Transform identityTransform;
- status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount,
- pointerProperties, pointerCoords);
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
+ 0, 0, 0, MotionClassification::NONE, identityTransform,
+ 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
+ 0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status)
<< "publisher publishMotionEvent should return BAD_VALUE";
}
@@ -520,6 +560,7 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeDragEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeTouchModeEvent());
}
} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 59fed1f..b6a9476 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -49,7 +49,7 @@
CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0);
- CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4);
CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
@@ -74,16 +74,17 @@
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124);
CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128);
CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132);
- CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136);
- CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140);
- CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144);
- CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148);
- CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140);
+ CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144);
+ CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148);
+ CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152);
+ CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156);
+ CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160);
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
- CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
+ CHECK_OFFSET(InputMessage::Body::Focus, empty, 5);
CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
@@ -102,6 +103,10 @@
CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
+
+ CHECK_OFFSET(InputMessage::Body::TouchMode, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::TouchMode, isInTouchMode, 4);
+ CHECK_OFFSET(InputMessage::Body::TouchMode, empty, 5);
}
void TestHeaderSize() {
@@ -123,6 +128,7 @@
static_assert(sizeof(InputMessage::Body::Focus) == 8);
static_assert(sizeof(InputMessage::Body::Capture) == 8);
static_assert(sizeof(InputMessage::Body::Drag) == 16);
+ static_assert(sizeof(InputMessage::Body::TouchMode) == 8);
// Timeline
static_assert(GraphicsTimeline::SIZE == 2);
static_assert(sizeof(InputMessage::Body::Timeline) == 24);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 13e2b02..a87b187 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -184,8 +184,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, identityTransform, 0 /*xPrecision*/,
0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/,
entry.eventTime.count(), pointerCount, properties, coords);
events.emplace_back(event);
@@ -344,7 +343,7 @@
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
- 872.794617);
+ 764.345703);
computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
951.698181);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index b29c9a4..f2b59ea 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -43,12 +43,12 @@
ui::Transform transform;
transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
+ ui::Transform identity;
event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/,
- 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/,
+ 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/,
200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
return event;
}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 2dd6c4f..fc9680b 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -77,6 +77,7 @@
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
+ AChoreographer_extendedFrameCallback extendedCallback;
void* data;
nsecs_t dueTime;
@@ -95,6 +96,20 @@
class Choreographer;
+/**
+ * Implementation of AChoreographerFrameCallbackData.
+ */
+struct ChoreographerFrameCallbackDataImpl {
+ int64_t frameTimeNanos{0};
+
+ std::array<VsyncEventData::FrameTimeline, DisplayEventReceiver::kFrameTimelinesLength>
+ frameTimelines;
+
+ size_t preferredFrameTimelineIndex;
+
+ const Choreographer* choreographer;
+};
+
struct {
std::mutex lock;
std::vector<Choreographer*> ptrs GUARDED_BY(lock);
@@ -107,7 +122,9 @@
public:
explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
- AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback, void* data,
+ nsecs_t delay);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -127,9 +144,8 @@
static Choreographer* getForThread();
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
- int64_t getVsyncId() const;
- int64_t getFrameDeadline() const;
int64_t getFrameInterval() const;
+ bool inCallback() const;
private:
Choreographer(const Choreographer&) = delete;
@@ -145,6 +161,8 @@
void scheduleCallbacks();
+ ChoreographerFrameCallbackDataImpl createFrameCallbackData(nsecs_t timestamp) const;
+
std::mutex mLock;
// Protected by mLock
std::priority_queue<FrameCallback> mFrameCallbacks;
@@ -152,6 +170,7 @@
nsecs_t mLatestVsyncPeriod = -1;
VsyncEventData mLastVsyncEventData;
+ bool mInCallback = false;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -192,6 +211,7 @@
// Only poke DisplayManagerGlobal to unregister if we previously registered
// callbacks.
if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+ gChoreographers.registeredToDisplayManager = false;
JNIEnv* env = getJniEnv();
if (env == nullptr) {
ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
@@ -210,10 +230,12 @@
}
}
-void Choreographer::postFrameCallbackDelayed(
- AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+ AChoreographer_frameCallback64 cb64,
+ AChoreographer_extendedFrameCallback extendedCallback,
+ void* data, nsecs_t delay) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, data, now + delay};
+ FrameCallback callback{cb, cb64, extendedCallback, data, now + delay};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
@@ -305,8 +327,9 @@
// Fortunately, these events are small so sending packets across the
// socket should be atomic across processes.
DisplayEventReceiver::Event event;
- event.header = DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
- PhysicalDisplayId(0), systemTime()};
+ event.header =
+ DisplayEventReceiver::Event::Header{DisplayEventReceiver::DISPLAY_EVENT_NULL,
+ PhysicalDisplayId::fromPort(0), systemTime()};
injectEvent(event);
}
}
@@ -368,7 +391,15 @@
}
mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
- if (cb.callback64 != nullptr) {
+ if (cb.extendedCallback != nullptr) {
+ const ChoreographerFrameCallbackDataImpl frameCallbackData =
+ createFrameCallbackData(timestamp);
+ mInCallback = true;
+ cb.extendedCallback(reinterpret_cast<const AChoreographerFrameCallbackData*>(
+ &frameCallbackData),
+ cb.data);
+ mInCallback = false;
+ } else if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
} else if (cb.callback != nullptr) {
cb.callback(timestamp, cb.data);
@@ -377,8 +408,8 @@
}
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
- ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.",
- this, to_string(displayId).c_str(), toString(connected));
+ ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
+ to_string(displayId).c_str(), toString(connected));
}
void Choreographer::dispatchModeChanged(nsecs_t, PhysicalDisplayId, int32_t, nsecs_t) {
@@ -397,30 +428,33 @@
void Choreographer::handleMessage(const Message& message) {
switch (message.what) {
- case MSG_SCHEDULE_CALLBACKS:
- scheduleCallbacks();
- break;
- case MSG_SCHEDULE_VSYNC:
- scheduleVsync();
- break;
- case MSG_HANDLE_REFRESH_RATE_UPDATES:
- handleRefreshRateUpdates();
- break;
+ case MSG_SCHEDULE_CALLBACKS:
+ scheduleCallbacks();
+ break;
+ case MSG_SCHEDULE_VSYNC:
+ scheduleVsync();
+ break;
+ case MSG_HANDLE_REFRESH_RATE_UPDATES:
+ handleRefreshRateUpdates();
+ break;
}
}
-int64_t Choreographer::getVsyncId() const {
- return mLastVsyncEventData.id;
-}
-
-int64_t Choreographer::getFrameDeadline() const {
- return mLastVsyncEventData.deadlineTimestamp;
-}
-
int64_t Choreographer::getFrameInterval() const {
return mLastVsyncEventData.frameInterval;
}
+bool Choreographer::inCallback() const {
+ return mInCallback;
+}
+
+ChoreographerFrameCallbackDataImpl Choreographer::createFrameCallbackData(nsecs_t timestamp) const {
+ return {.frameTimeNanos = timestamp,
+ .preferredFrameTimelineIndex = mLastVsyncEventData.preferredFrameTimelineIndex,
+ .frameTimelines = mLastVsyncEventData.frameTimelines,
+ .choreographer = this};
+}
+
} // namespace android
using namespace android;
@@ -433,6 +467,12 @@
return reinterpret_cast<const Choreographer*>(choreographer);
}
+static inline const ChoreographerFrameCallbackDataImpl*
+AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(
+ const AChoreographerFrameCallbackData* data) {
+ return reinterpret_cast<const ChoreographerFrameCallbackDataImpl*>(data);
+}
+
// Glue for private C api
namespace android {
void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
@@ -485,6 +525,11 @@
void* data, uint32_t delayMillis) {
return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
}
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ return AChoreographer_postExtendedFrameCallback(choreographer, callback, data);
+}
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data) {
@@ -495,13 +540,29 @@
void* data) {
return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
}
-
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) {
- return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimeNanos(data);
}
-
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
- return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getFrameTimelinesLength(data);
+}
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(data);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineVsyncId(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(data, index);
+}
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_getFrameTimelineDeadline(data, index);
}
int64_t AChoreographer_getFrameInterval(const AChoreographer* choreographer) {
@@ -521,24 +582,32 @@
}
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, 0);
+ AChoreographer_frameCallback callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data, long delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- callback, nullptr, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback callback, void* data,
+ long delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, 0);
+ AChoreographer_frameCallback64 callback, void* data) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
- AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
- AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
- nullptr, callback, data, ms2ns(delayMillis));
+ AChoreographer_frameCallback64 callback, void* data,
+ uint32_t delayMillis) {
+ AChoreographer_to_Choreographer(choreographer)
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
@@ -551,6 +620,58 @@
AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
}
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimeNanos;
+}
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->frameTimelines.size();
+}
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ return frameCallbackData->preferredFrameTimelineIndex;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].id;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].expectedPresentTime;
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ const ChoreographerFrameCallbackDataImpl* frameCallbackData =
+ AChoreographerFrameCallbackData_to_ChoreographerFrameCallbackDataImpl(data);
+ LOG_ALWAYS_FATAL_IF(!frameCallbackData->choreographer->inCallback(),
+ "Data is only valid in callback");
+ LOG_ALWAYS_FATAL_IF(index >= frameCallbackData->frameTimelines.size(), "Index out of bounds");
+ return frameCallbackData->frameTimelines[index].deadlineTimestamp;
+}
+
AChoreographer* AChoreographer_create() {
Choreographer* choreographer = new Choreographer(nullptr);
status_t result = choreographer->initialize();
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 7d25ce8..4aa7e69 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -29,19 +29,6 @@
// for consumption by callbacks.
void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
-// Returns the vsync id of the last frame callback. Client are expected to call
-// this function from their frame callback function to get the vsyncId and pass
-// it together with a buffer or transaction to the Surface Composer. Calling
-// this function from anywhere else will return an undefined value.
-int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
-
-// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
-// Client are expected to call this function from their frame callback function
-// to get the deadline and use it to know whether a frame is likely to miss
-// presentation. Calling this function from anywhere else will return an undefined
-// value.
-int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
-
// Returns the current interval in ns between frames.
// Client are expected to call this function from their frame callback function.
// Calling this function from anywhere else will return an undefined value.
@@ -63,11 +50,26 @@
void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback,
void* data, uint32_t delayMillis);
+void AChoreographer_routePostExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data);
void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data);
+size_t AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index);
+int64_t AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index);
} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
index 35ae3d2..6fd4b8f 100644
--- a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -42,7 +42,8 @@
typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& cb,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& cb,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle);
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index 6eaa84e..0f119f3 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -272,10 +272,11 @@
status_t attachToContext(uint32_t tex);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outQueueEmpty,
+ HdrMetadata* outHdrMetadata, float* outTransformMatrix,
+ uint32_t* outTransform, bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle);
+ void* fencePassThroughHandle, ARect* currentCrop);
/**
* takeConsumerOwnership attaches a SurfaceTexture that is currently in the
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 85fe42f..2987f3a 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,6 +19,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <android/hdr_metadata.h>
#include <jni.h>
#include <system/graphics.h>
@@ -82,12 +83,12 @@
* The caller gets ownership of the buffer and need to release it with
* AHardwareBuffer_release.
*/
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle);
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop);
} // namespace android
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 9ed4915..4dbfde8 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -7,6 +7,13 @@
AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
AChoreographer_registerRefreshRateCallback; # apex # introduced=30
AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+ AChoreographer_postExtendedFrameCallback; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimeNanos; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelinesLength; # apex # introduced=33
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # apex # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineDeadline; # apex # introduced=33
AChoreographer_create; # apex # introduced=30
AChoreographer_destroy; # apex # introduced=30
AChoreographer_getFd; # apex # introduced=30
@@ -28,9 +35,14 @@
android::AChoreographer_routePostFrameCallbackDelayed64*;
android::AChoreographer_routeRegisterRefreshRateCallback*;
android::AChoreographer_routeUnregisterRefreshRateCallback*;
+ android::AChoreographer_routePostExtendedFrameCallback*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimeNanos*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelinesLength*;
+ android::AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime*;
+ android::AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline*;
android::AChoreographer_signalRefreshRateCallbacks*;
- android::AChoreographer_getVsyncId*;
- android::AChoreographer_getFrameDeadline*;
android::AChoreographer_getFrameInterval*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 365e788..cf16739 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -28,7 +28,8 @@
}
sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& st,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& st,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle) {
@@ -121,6 +122,7 @@
st.computeCurrentTransformMatrixLocked();
*outDataspace = item.mDataSpace;
+ *outHdrMetadata = item.mHdrMetadata;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
}
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 62db6d0..d3d4cba 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -464,10 +464,12 @@
}
sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outQueueEmpty,
+ HdrMetadata* outHdrMetadata,
+ float* outTransformMatrix, uint32_t* outTransform,
+ bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle) {
+ void* fencePassThroughHandle, ARect* currentCrop) {
Mutex::Autolock _l(mMutex);
sp<GraphicBuffer> buffer;
@@ -481,9 +483,11 @@
return buffer;
}
- buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
- createFence, fenceWait, fencePassThroughHandle);
+ buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outHdrMetadata, outQueueEmpty,
+ *this, createFence, fenceWait, fencePassThroughHandle);
memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+ *outTransform = mCurrentTransform;
+ *currentCrop = mCurrentCrop;
return buffer;
}
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index c214ab7..39a925f 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -192,17 +192,23 @@
texture->consumer->releaseConsumerOwnership();
}
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait, void* handle) {
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle, ARect* currentCrop) {
sp<GraphicBuffer> buffer;
*outNewContent = false;
bool queueEmpty;
do {
- buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
- &queueEmpty, createFence, fenceWait, handle);
+ HdrMetadata metadata;
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, &metadata, outTransformMatrix,
+ outTransform, &queueEmpty, createFence, fenceWait,
+ handle, currentCrop);
+ *outHdrType = static_cast<AHdrMetadataType>(metadata.validTypes);
+ *outCta861_3 = metadata.cta8613;
+ *outSmpte2086 = metadata.smpte2086;
if (!queueEmpty) {
*outNewContent = true;
}
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e2f32e3..cb3361b 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -30,7 +30,7 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.1/types.h>
-
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints
@@ -370,7 +370,7 @@
if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
bool supported = false;
- GraphicBuffer* gBuffer = new GraphicBuffer();
+ sp<GraphicBuffer> gBuffer(new GraphicBuffer());
status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
desc->usage, &supported);
@@ -588,8 +588,12 @@
"HAL and AHardwareBuffer pixel format don't match");
static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
"HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+ AHARDWAREBUFFER_FORMAT_R8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
@@ -641,6 +645,8 @@
uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ return 1;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
return 2;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 75f2385..18a4b2d 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -133,16 +133,51 @@
int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
- static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR));
+ static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+ static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+ static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+ static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+ static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+ static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+ static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+ static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+ static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+ static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+ static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+ static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+ static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+ static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+ static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+ static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+ static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+ static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+ static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+ static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+ static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+ static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+ static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_PQ) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_PQ));
static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF));
+ static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625));
+ static_assert(static_cast<int>(ADATASPACE_BT601_525) == static_cast<int>(HAL_DATASPACE_V0_BT601_525));
static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_HLG));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
+ static_assert(static_cast<int>(DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
+ static_assert(static_cast<int>(DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
!isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index e759513..771844f 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -15,6 +15,13 @@
*/
/**
+ * @defgroup ADataSpace Data Space
+ *
+ * ADataSpace describes how to interpret colors.
+ * @{
+ */
+
+/**
* @file data_space.h
*/
@@ -43,6 +50,340 @@
ADATASPACE_UNKNOWN = 0,
/**
+ * Color-description aspects
+ *
+ * The following aspects define various characteristics of the color
+ * specification. These represent bitfields, so that a data space value
+ * can specify each of them independently.
+ */
+
+ /**
+ * Standard aspect
+ *
+ * Defines the chromaticity coordinates of the source primaries in terms of
+ * the CIE 1931 definition of x and y specified in ISO 11664-1.
+ */
+ STANDARD_MASK = 63 << 16,
+
+ /**
+ * Chromacity coordinates are unknown or are determined by the application.
+ * Implementations shall use the following suggested standards:
+ *
+ * All YCbCr formats: BT709 if size is 720p or larger (since most video
+ * content is letterboxed this corresponds to width is
+ * 1280 or greater, or height is 720 or greater).
+ * BT601_625 if size is smaller than 720p or is JPEG.
+ * All RGB formats: BT709.
+ *
+ * For all other formats standard is undefined, and implementations should use
+ * an appropriate standard for the data represented.
+ */
+ STANDARD_UNSPECIFIED = 0 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT709 = 1 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ STANDARD_BT601_625 = 2 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * KR = 0.299, KB = 0.114. This adjusts the luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ */
+ STANDARD_BT601_525 = 4 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ */
+ STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT2020 = 6 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290
+ *
+ * Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
+ * for RGB conversion using the linear domain.
+ */
+ STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.21 0.71
+ * blue 0.14 0.08
+ * red 0.67 0.33
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_BT470M = 8 << 16,
+
+ /**
+ * Primaries: x y
+ * green 0.243 0.692
+ * blue 0.145 0.049
+ * red 0.681 0.319
+ * white (C) 0.310 0.316
+ *
+ * Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
+ * for RGB conversion.
+ */
+ STANDARD_FILM = 9 << 16,
+
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
+ * Primaries: x y
+ * green 0.265 0.690
+ * blue 0.150 0.060
+ * red 0.680 0.320
+ * white (D65) 0.3127 0.3290
+ */
+ STANDARD_DCI_P3 = 10 << 16,
+
+ /**
+ * Adobe RGB
+ * Primaries: x y
+ * green 0.210 0.710
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290
+ */
+ STANDARD_ADOBE_RGB = 11 << 16,
+
+ /**
+ * Transfer aspect
+ *
+ * Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).
+ *
+ * For digital signals, E corresponds to the recorded value. Normally, the
+ * transfer function is applied in RGB space to each of the R, G and B
+ * components independently. This may result in color shift that can be
+ * minized by applying the transfer function in Lab space only for the L
+ * component. Implementation may apply the transfer function in RGB space
+ * for all pixel formats if desired.
+ */
+ TRANSFER_MASK = 31 << 22,
+
+ /**
+ * Transfer characteristics are unknown or are determined by the
+ * application.
+ *
+ * Implementations should use the following transfer functions:
+ *
+ * For YCbCr formats: use TRANSFER_SMPTE_170M
+ * For RGB formats: use TRANSFER_SRGB
+ *
+ * For all other formats transfer function is undefined, and implementations
+ * should use an appropriate standard for the data represented.
+ */
+ TRANSFER_UNSPECIFIED = 0 << 22,
+
+ /**
+ * Transfer characteristic curve:
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_LINEAR = 1 << 22,
+
+ /**
+ * Transfer characteristic curve:
+ *
+ * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
+ * = 12.92 * L for 0 <= L < 0.0031308
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_SRGB = 2 << 22,
+
+ /**
+ * BT.601 525, BT.601 625, BT.709, BT.2020
+ *
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_SMPTE_170M = 3 << 22,
+
+ /**
+ * Assumed display gamma 2.2.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_2 = 4 << 22,
+
+ /**
+ * display gamma 2.6.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.6)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_6 = 5 << 22,
+
+ /**
+ * display gamma 2.8.
+ *
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal
+ */
+ TRANSFER_GAMMA2_8 = 6 << 22,
+
+ /**
+ * SMPTE ST 2084 (Dolby Perceptual Quantizer)
+ *
+ * Transfer characteristic curve:
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal
+ */
+ TRANSFER_ST2084 = 7 << 22,
+
+ /**
+ * ARIB STD-B67 Hybrid Log Gamma
+ *
+ * Transfer characteristic curve:
+ * E = r * L^0.5 for 0 <= L <= 1
+ * = a * ln(L - b) + c for 1 < L
+ * a = 0.17883277
+ * b = 0.28466892
+ * c = 0.55991073
+ * r = 0.5
+ * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+ * to reference white level of 100 cd/m2
+ * E - corresponding electrical signal
+ */
+ TRANSFER_HLG = 8 << 22,
+
+ /**
+ * Range aspect
+ *
+ * Defines the range of values corresponding to the unit range of 0-1.
+ * This is defined for YCbCr only, but can be expanded to RGB space.
+ */
+ RANGE_MASK = 7 << 27,
+
+ /**
+ * Range is unknown or are determined by the application. Implementations
+ * shall use the following suggested ranges:
+ *
+ * All YCbCr formats: limited range.
+ * All RGB or RGBA formats (including RAW and Bayer): full range.
+ * All Y formats: full range
+ *
+ * For all other formats range is undefined, and implementations should use
+ * an appropriate range for the data represented.
+ */
+ RANGE_UNSPECIFIED = 0 << 27,
+
+ /**
+ * Full range uses all values for Y, Cb and Cr from
+ * 0 to 2^b-1, where b is the bit depth of the color format.
+ */
+ RANGE_FULL = 1 << 27,
+
+ /**
+ * Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
+ * 1/16*2^b to 15/16*2^b for Cb, Cr, R, G and B, where b is the bit depth of
+ * the color format.
+ *
+ * E.g. For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
+ */
+ RANGE_LIMITED = 2 << 27,
+
+ /**
+ * Extended range is used for scRGB. Intended for use with
+ * floating point pixel formats. [0.0 - 1.0] is the standard
+ * sRGB space. Values outside the range 0.0 - 1.0 can encode
+ * color outside the sRGB gamut.
+ * Used to blend / merge multiple dataspaces on a single display.
+ */
+ RANGE_EXTENDED = 3 << 27,
+
+ /**
* scRGB linear encoding:
*
* The red, green, and blue components are stored in extended sRGB space,
@@ -103,6 +444,15 @@
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
/**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ */
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+
+ /**
* Adobe RGB
*
* Use full range, gamma 2.2 transfer and Adobe RGB primaries
@@ -112,6 +462,33 @@
ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
/**
+ * JPEG File Interchange Format (JFIF)
+ *
+ * Same model as BT.601-625, but all values (Y, Cb, Cr) range from 0 to 255
+ *
+ * Use full range, SMPTE 170M transfer and BT.601_625 standard.
+ */
+ ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 601 (BT.601) - 525-line
+ *
+ * Standard-definition television, 525 Lines (NTSC)
+ *
+ * Use limited range, SMPTE 170M transfer and BT.601_525 standard.
+ */
+ ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television
+ *
+ * Use limited range, SMPTE 170M transfer and BT.709 standard.
+ */
+ ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
* ITU-R Recommendation 2020 (BT.2020)
*
* Ultra High-definition television
@@ -151,8 +528,38 @@
* components.
*/
ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+
+ /**
+ * Hybrid Log Gamma encoding:
+ *
+ * Use full range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+
+ /**
+ * ITU Hybrid Log Gamma encoding:
+ *
+ * Use limited range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+
+ /**
+ * Depth:
+ *
+ * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
+ */
+ DEPTH = 4096,
+
+ /**
+ * ISO 16684-1:2011(E) Dynamic Depth:
+ *
+ * Embedded depth metadata following the dynamic depth specification.
+ */
+ DYNAMIC_DEPTH = 4098
};
__END_DECLS
#endif // ANDROID_DATA_SPACE_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d93a84c..6f1f04d 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -158,6 +158,13 @@
* cube-maps or multi-layered textures.
*/
AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8_UNORM
+ * OpenGL ES: GR_GL_R8
+ */
+ AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38,
};
/**
@@ -556,6 +563,7 @@
int32_t* _Nonnull outBytesPerPixel,
int32_t* _Nonnull outBytesPerStride) __INTRODUCED_IN(29);
+
/**
* Get the system wide unique id for an AHardwareBuffer.
*
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7f01135..0bc2b5d 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1089,10 +1089,13 @@
/**
* Retrieves an identifier for the next frame to be queued by this window.
*
- * \return the next frame id.
+ * Frame ids start at 1 and are incremented on each new frame until the underlying surface changes,
+ * in which case the frame id is reset to 1.
+ *
+ * \return the next frame id (0 being uninitialized).
*/
-static inline int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
- int64_t value;
+static inline uint64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+ uint64_t value;
window->perform(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID, &value);
return value;
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 570c7bc..07c5dd8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -40,6 +40,11 @@
"libui",
"libutils",
],
+
+ static_libs: [
+ "libshaders",
+ "libtonemap",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -94,8 +99,10 @@
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
- "skia/filters/StretchShaderFactory.cpp"
+ "skia/filters/StretchShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 9e466b6..c7ad058 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -26,23 +26,7 @@
namespace android {
namespace renderengine {
-std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) {
- // Keep the ability to override by PROPERTIES:
- char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
- if (strcmp(prop, "gles") == 0) {
- args.renderEngineType = RenderEngineType::GLES;
- }
- if (strcmp(prop, "threaded") == 0) {
- args.renderEngineType = RenderEngineType::THREADED;
- }
- if (strcmp(prop, "skiagl") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL;
- }
- if (strcmp(prop, "skiaglthreaded") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL_THREADED;
- }
-
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
switch (args.renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
@@ -79,5 +63,16 @@
"output buffer not gpu writeable");
}
+std::future<RenderEngineResult> 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<RenderEngineResult>>();
+ std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+ drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache,
+ std::move(bufferFence));
+ return resultFuture;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
new file mode 100644
index 0000000..471159f
--- /dev/null
+++ b/libs/renderengine/benchmark/Android.bp
@@ -0,0 +1,59 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+ name: "librenderengine_bench",
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
+ srcs: [
+ "main.cpp",
+ "Codec.cpp",
+ "Flags.cpp",
+ "RenderEngineBench.cpp",
+ ],
+ static_libs: [
+ "librenderengine",
+ "libshaders",
+ "libtonemap",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"RenderEngineBench\"",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libjnigraphics",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ data: ["resources/*"],
+}
diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp
new file mode 100644
index 0000000..80e4fc4
--- /dev/null
+++ b/libs/renderengine/benchmark/Codec.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include <RenderEngineBench.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+using namespace android;
+using namespace android::renderengine;
+
+namespace {
+struct DecoderDeleter {
+ void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); }
+};
+
+using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+bool ok(int aImageDecoderResult, const char* path, const char* method) {
+ if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) {
+ return true;
+ }
+
+ ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path,
+ AImageDecoder_resultToString(aImageDecoderResult));
+ return false;
+}
+} // namespace
+
+namespace renderenginebench {
+
+void decode(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_RDONLY)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ AImageDecoder* decoder{nullptr};
+ auto result = AImageDecoder_createFromFd(fd.get(), &decoder);
+ if (!ok(result, path, "createFromFd")) {
+ return;
+ }
+
+ AutoDecoderDeleter deleter(decoder);
+
+ LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0,
+ "Impossible buffer size!");
+ auto width = static_cast<int32_t>(buffer->getWidth());
+ auto height = static_cast<int32_t>(buffer->getHeight());
+ result = AImageDecoder_setTargetSize(decoder, width, height);
+ if (!ok(result, path, "setTargetSize")) {
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride),
+ static_cast<size_t>(stride * height));
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+
+ // For the side effect of logging.
+ (void)ok(result, path, "decodeImage");
+}
+
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ AndroidBitmapInfo info{
+ .width = buffer->getWidth(),
+ .height = buffer->getHeight(),
+ .stride = static_cast<uint32_t>(stride),
+ .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+ .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+ };
+ int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+ ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd,
+ [](void* fdPtr, const void* data, size_t size) -> bool {
+ const ssize_t bytesWritten =
+ write(reinterpret_cast<base::unique_fd*>(fdPtr)
+ ->get(),
+ data, size);
+ return bytesWritten > 0 &&
+ static_cast<size_t>(bytesWritten) == size;
+ });
+ if (result == ANDROID_BITMAP_RESULT_SUCCESS) {
+ ALOGD("Successfully encoded to '%s'", path);
+ } else {
+ ALOGE("Failed to encode to %s with error %d", path, result);
+ }
+
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+}
+
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp
new file mode 100644
index 0000000..c5d5156
--- /dev/null
+++ b/libs/renderengine/benchmark/Flags.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#include <log/log.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace {
+bool gSave = false;
+}
+
+namespace renderenginebench {
+
+void parseFlagsForHelp(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--help")) {
+ printf("RenderEngineBench-specific flags:\n");
+ printf("[--save]: Save the output to the device to confirm drawing result.\n");
+ break;
+ }
+ }
+}
+
+void parseFlags(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--save")) {
+ gSave = true;
+ }
+ }
+}
+
+bool save() {
+ return gSave;
+}
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
new file mode 100644
index 0000000..6c8f8e8
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#include <RenderEngineBench.h>
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
+
+#include <mutex>
+
+using namespace android;
+using namespace android::renderengine;
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for Benchmark::Apply
+///////////////////////////////////////////////////////////////////////////////
+
+std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
+ switch (type) {
+ case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
+ return "skiaglthreaded";
+ case RenderEngine::RenderEngineType::SKIA_GL:
+ return "skiagl";
+ case RenderEngine::RenderEngineType::GLES:
+ case RenderEngine::RenderEngineType::THREADED:
+ LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
+ return "unused";
+ }
+}
+
+/**
+ * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a
+ * Benchmark which specifies which RenderEngineType it uses.
+ *
+ * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
+ * it obvious which version is being run.
+ *
+ * @param b The benchmark family
+ * @param type The type of RenderEngine to use.
+ */
+static void AddRenderEngineType(benchmark::internal::Benchmark* b,
+ RenderEngine::RenderEngineType type) {
+ b->Arg(static_cast<int64_t>(type));
+ b->ArgName(RenderEngineTypeName(type));
+}
+
+/**
+ * Run a benchmark once using SKIA_GL_THREADED.
+ */
+static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) {
+ AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for calling drawLayers
+///////////////////////////////////////////////////////////////////////////////
+
+std::pair<uint32_t, uint32_t> getDisplaySize() {
+ // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
+ // to GraphicBuffer, which wants uint32_t.
+ static uint32_t width, height;
+ std::once_flag once;
+ std::call_once(once, []() {
+ auto surfaceComposerClient = SurfaceComposerClient::getDefault();
+ auto displayToken = surfaceComposerClient->getInternalDisplayToken();
+ ui::DisplayMode displayMode;
+ if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+ LOG_ALWAYS_FATAL("Failed to get active display mode!");
+ }
+ auto w = displayMode.resolution.width;
+ auto h = displayMode.resolution.height;
+ LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
+ width = static_cast<uint32_t>(w);
+ height = static_cast<uint32_t>(h);
+ });
+ 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))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(true)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(RenderEngine::ContextPriority::REALTIME)
+ .setRenderEngineType(type)
+ .setUseColorManagerment(true)
+ .build();
+ return RenderEngine::create(args);
+}
+
+static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
+ uint32_t height,
+ uint64_t extraUsageFlags = 0,
+ std::string name = "output") {
+ return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ extraUsageFlags,
+ std::move(name)),
+ re,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
+}
+
+static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
+ std::shared_ptr<ExternalTexture> original,
+ uint64_t extraUsageFlags, std::string name) {
+ const uint32_t width = original->getBuffer()->getWidth();
+ const uint32_t height = original->getBuffer()->getHeight();
+ auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = original,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+
+ auto [status, drawFence] =
+ re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ return texture;
+}
+
+/**
+ * Helper for timing calls to drawLayers.
+ *
+ * Caller needs to create RenderEngine and the LayerSettings, and this takes
+ * care of setting up the display, starting and stopping the timer, calling
+ * drawLayers, and saving (if --save is used).
+ *
+ * This times both the CPU and GPU work initiated by drawLayers. All work done
+ * outside of the for loop is excluded from the timing measurements.
+ */
+static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
+ benchmark::State& benchState, const char* saveFileName) {
+ auto [width, height] = getDisplaySize();
+ auto outputBuffer = allocateBuffer(re, width, height);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ // This loop starts and stops the timer.
+ for (auto _ : benchState) {
+ auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer,
+ kUseFrameBufferCache, base::unique_fd())
+ .get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ }
+
+ if (renderenginebench::save() && saveFileName) {
+ // Copy to a CPU-accessible buffer so we can encode it.
+ outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
+
+ std::string outFile = base::GetExecutableDirectory();
+ outFile.append("/");
+ outFile.append(saveFileName);
+ outFile.append(".jpg");
+ renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Benchmarks
+///////////////////////////////////////////////////////////////////////////////
+
+void BM_blur(benchmark::State& benchState) {
+ auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+
+ // Initially use cpu access so we can decode into it with AImageDecoder.
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer =
+ allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+ {
+ std::string srcImage = base::GetExecutableDirectory();
+ srcImage.append("/resources/homescreen.png");
+ renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+
+ // Now copy into GPU-only buffer for more realistic timing.
+ srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
+ }
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ LayerSettings blurLayer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .alpha = half(1.0f),
+ .skipContentDraw = true,
+ .backgroundBlurRadius = 60,
+ };
+
+ auto layers = std::vector<LayerSettings>{layer, blurLayer};
+ benchDrawLayers(*re, layers, benchState, "blurred");
+}
+
+BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded);
diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h
new file mode 100644
index 0000000..1a25d77
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.h
@@ -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.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+/**
+ * Utilities for running benchmarks.
+ */
+namespace renderenginebench {
+/**
+ * Parse RenderEngineBench-specific flags from the command line.
+ *
+ * --save Save the output buffer to a file to verify that it drew as
+ * expected.
+ */
+void parseFlags(int argc, char** argv);
+
+/**
+ * Parse flags for '--help'
+ */
+void parseFlagsForHelp(int argc, char** argv);
+
+/**
+ * Whether to save the drawing result to a file.
+ *
+ * True if --save was used on the command line.
+ */
+bool save();
+
+/**
+ * Decode the image at 'path' into 'buffer'.
+ *
+ * Currently only used for debugging. The image will be scaled to fit the
+ * buffer if necessary.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void decode(const char* path, const sp<GraphicBuffer>& buffer);
+
+/**
+ * Encode the buffer to a jpeg.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer);
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp
new file mode 100644
index 0000000..7a62853
--- /dev/null
+++ b/libs/renderengine/benchmark/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include <RenderEngineBench.h>
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ // Initialize will exit if it sees '--help', so check for it and print info
+ // about our flags first.
+ renderenginebench::parseFlagsForHelp(argc, argv);
+ benchmark::Initialize(&argc, argv);
+
+ // Calling this separately from parseFlagsForHelp prevents collisions with
+ // google-benchmark's flags, since Initialize will consume and remove flags
+ // it recognizes.
+ renderenginebench::parseFlags(argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ return 0;
+}
diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png
new file mode 100644
index 0000000..997b72d
--- /dev/null
+++ b/libs/renderengine/benchmark/resources/homescreen.png
Binary files differ
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 467f848..22dd866 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1078,15 +1078,16 @@
return image;
}
-status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) {
+void GLESRenderEngine::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& 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");
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
}
if (bufferFence.get() >= 0) {
@@ -1100,7 +1101,8 @@
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
validateOutputBufferUsage(buffer->getBuffer());
@@ -1108,10 +1110,10 @@
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;
+ std::deque<const LayerSettings> blurLayers;
if (CC_LIKELY(mBlurFilter != nullptr)) {
- for (auto layer : layers) {
- if (layer->backgroundBlurRadius > 0) {
+ for (const auto& layer : layers) {
+ if (layer.backgroundBlurRadius > 0) {
blurLayers.push_back(layer);
}
}
@@ -1128,18 +1130,20 @@
ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors();
- return fbo->getStatus();
+ resultPromise->set_value({fbo->getStatus(), base::unique_fd()});
+ return;
}
setViewportAndProjection(display.physicalDisplay, display.clip);
} else {
setViewportAndProjection(display.physicalDisplay, display.clip);
auto status =
- mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+ 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();
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
}
@@ -1156,10 +1160,6 @@
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
- if (!display.clearRegion.isEmpty()) {
- glDisable(GL_BLEND);
- fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
- }
Mesh mesh = Mesh::Builder()
.setPrimitive(Mesh::TRIANGLE_FAN)
@@ -1167,7 +1167,7 @@
.setTexCoords(2 /* size */)
.setCropCoords(2 /* size */)
.build();
- for (auto const layer : layers) {
+ for (const auto& layer : layers) {
if (blurLayers.size() > 0 && blurLayers.front() == layer) {
blurLayers.pop_front();
@@ -1176,7 +1176,8 @@
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors("Can't render first blur pass");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
if (blurLayers.size() == 0) {
@@ -1192,13 +1193,14 @@
// 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);
+ 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");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
status = mBlurFilter->render(blurLayersSize > 1);
@@ -1206,47 +1208,48 @@
ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
buffer->getBuffer()->handle);
checkErrors("Can't render blur filter");
- return status;
+ resultPromise->set_value({status, base::unique_fd()});
+ return;
}
}
// Ensure luminance is at least 100 nits to avoid div-by-zero
- const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits);
+ const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits);
mState.maxMasteringLuminance = maxLuminance;
mState.maxContentLuminance = maxLuminance;
- mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform;
+ mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
- const FloatRect bounds = layer->geometry.boundaries;
+ 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);
+ setupLayerCropping(layer, mesh);
+ setColorTransform(layer.colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
bool isOpaque = false;
- if (layer->source.buffer.buffer != nullptr) {
+ if (layer.source.buffer.buffer != nullptr) {
disableTexture = false;
- isOpaque = layer->source.buffer.isOpaque;
+ isOpaque = layer.source.buffer.isOpaque;
- sp<GraphicBuffer> gBuf = layer->source.buffer.buffer->getBuffer();
+ sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer();
validateInputBufferUsage(gBuf);
- bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
- layer->source.buffer.fence);
+ 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;
+ 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.setFiltering(layer.source.buffer.useTextureFiltering);
texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
- setSourceY410BT2020(layer->source.buffer.isY410BT2020);
+ setSourceY410BT2020(layer.source.buffer.isY410BT2020);
renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
texCoords[0] = vec2(0.0, 0.0);
@@ -1261,62 +1264,63 @@
}
}
- const half3 solidColor = layer->source.solidColor;
- const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer->alpha);
+ const half3 solidColor = layer.source.solidColor;
+ const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
// 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,
- layer->geometry.roundedCornersRadius);
- if (layer->disableBlending) {
+ layer.geometry.roundedCornersRadius);
+ if (layer.disableBlending) {
glDisable(GL_BLEND);
}
- setSourceDataSpace(layer->sourceDataspace);
+ setSourceDataSpace(layer.sourceDataspace);
- if (layer->shadow.length > 0.0f) {
- handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
- layer->shadow);
+ if (layer.shadow.length > 0.0f) {
+ handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+ 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 (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
- handleRoundedCorners(display, *layer, mesh);
+ else if (layer.geometry.roundedCornersRadius > 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) {
+ if (layer.source.buffer.buffer != nullptr) {
disableBlending();
setSourceY410BT2020(false);
disableTexturing();
}
}
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
+ 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 == nullptr || drawFence->get() < 0) {
+ 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).
- return INVALID_OPERATION;
+ resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+ return;
}
mLastDrawFence = nullptr;
} else {
// The caller takes ownership of drawFence, so we need to duplicate the
// fd here.
- mLastDrawFence = new Fence(dup(drawFence->get()));
+ mLastDrawFence = new Fence(dup(drawFence.get()));
}
mPriorResourcesCleaned = false;
checkErrors();
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+ return;
}
void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 14627ce..1d7c2ca 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -63,11 +63,6 @@
bool isProtected() const override { return mInProtectedContext; }
bool supportsProtectedContent() const override;
void useProtectedContext(bool useProtectedContext) override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
void cleanupPostRender() override;
int getContextPriority() override;
bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; }
@@ -107,6 +102,11 @@
EXCLUDES(mRenderingMutex);
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& 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;
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 53fa622..b4cab39 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -51,25 +51,22 @@
// dataspace, in non-linear space.
mat4 colorTransform = mat4();
- // Region that will be cleared to (0, 0, 0, 1) prior to rendering.
- // This is specified in layer-stack space.
- Region clearRegion = Region::INVALID_REGION;
-
// An additional orientation flag to be applied after clipping the output.
// By way of example, this may be used for supporting fullscreen screenshot
// capture of a device in landscape while the buffer is in portrait
// orientation.
uint32_t orientation = ui::Transform::ROT_0;
- // SDR white point, -1f if unknown
- float sdrWhitePointNits = -1.f;
+ // Target luminance of the display. -1f if unknown.
+ // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
+ // If the target luminance is unknown, then no display-level dimming occurs.
+ float targetLuminanceNits = -1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
lhs.maxLuminance == rhs.maxLuminance && lhs.outputDataspace == rhs.outputDataspace &&
- lhs.colorTransform == rhs.colorTransform &&
- lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+ lhs.colorTransform == rhs.colorTransform && lhs.orientation == rhs.orientation;
}
// Defining PrintTo helps with Google Tests.
@@ -84,9 +81,6 @@
PrintTo(settings.outputDataspace, os);
*os << "\n .colorTransform = " << settings.colorTransform;
*os << "\n .clearRegion = ";
- PrintTo(settings.clearRegion, os);
- *os << "\n .orientation = " << settings.orientation;
- *os << "\n}";
}
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 715f3f8..702e8b0 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -171,6 +171,12 @@
// Name associated with the layer for debugging purposes.
std::string name;
+
+ // Luminance of the white point for this layer. Used for linear dimming.
+ // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
+ // If white point nits are unknown, then this layer is assumed to have the
+ // same luminance as the brightest layer in the scene.
+ float whitePointNits = -1.f;
};
// Keep in sync with custom comparison function in
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index f555cdb..b9cc648 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -68,6 +68,7 @@
class Mesh;
class Texture;
struct RenderEngineCreationArgs;
+struct RenderEngineResult;
namespace threaded {
class RenderEngineThreaded;
@@ -98,7 +99,7 @@
SKIA_GL_THREADED = 4,
};
- static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args);
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
@@ -156,17 +157,12 @@
// parameter does nothing.
// @param bufferFence Fence signalling that the buffer is ready to be drawn
// to.
- // @param drawFence A pointer to a fence, which will fire when the buffer
- // has been drawn to and is ready to be examined. The fence will be
- // initialized by this method. The caller will be responsible for owning the
- // fence.
- // @return An error code indicating whether drawing was successful. For
- // now, this always returns NO_ERROR.
- virtual status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) = 0;
+ // @return A future object of RenderEngineResult struct indicating whether
+ // drawing was successful in async mode.
+ virtual std::future<RenderEngineResult> 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
// drawFence returned by drawLayers fires. This method will free up
@@ -232,6 +228,12 @@
friend class threaded::RenderEngineThreaded;
friend class RenderEngineTest_cleanupPostRender_cleansUpOnce_Test;
const RenderEngineType mRenderEngineType;
+
+ virtual void drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) = 0;
};
struct RenderEngineCreationArgs {
@@ -318,6 +320,13 @@
RenderEngine::RenderEngineType::SKIA_GL_THREADED;
};
+struct RenderEngineResult {
+ // status indicates if drawing is successful
+ status_t status;
+ // drawFence will fire when the buffer has been drawn to and is ready to be examined.
+ base::unique_fd drawFence;
+};
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 0be3ba6..248bd65 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -47,10 +47,15 @@
MOCK_METHOD1(useProtectedContext, void(bool));
MOCK_METHOD0(cleanupPostRender, void());
MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool());
- MOCK_METHOD6(drawLayers,
- status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
- const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&,
- base::unique_fd*));
+ MOCK_METHOD5(drawLayers,
+ std::future<RenderEngineResult>(const DisplaySettings&,
+ const std::vector<LayerSettings>&,
+ const std::shared_ptr<ExternalTexture>&,
+ const bool, base::unique_fd&&));
+ MOCK_METHOD6(drawLayersInternal,
+ void(const std::shared_ptr<std::promise<RenderEngineResult>>&&,
+ const DisplaySettings&, const std::vector<LayerSettings>&,
+ const std::shared_ptr<ExternalTexture>&, const bool, base::unique_fd&&));
MOCK_METHOD0(cleanFramebufferCache, void());
MOCK_METHOD0(getContextPriority, int());
MOCK_METHOD0(supportsBackgroundBlur, bool());
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index cc627b8..b18a872 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -95,25 +95,27 @@
.alpha = 1,
};
- auto layers = std::vector<const LayerSettings*>{&layer, &caster};
- // When sourceDataspace matches dest, the general shadow fragment shader doesn't
- // have color correction added.
- // independently, when it is not srgb, the *vertex* shader has color correction added.
- // This may be a bug, but the shader still needs to be cached as it is triggered
- // during youtube pip.
- for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
- layer.sourceDataspace = dataspace;
- // The 2nd matrix, which has different scales for x and y, will
- // generate the slower (more general case) shadow shader
- for (auto transform : {mat4(), kScaleAndTranslate, kFlip}) {
- layer.geometry.positionTransform = transform;
- caster.geometry.positionTransform = transform;
- for (bool translucent : {false, true}){
- layer.shadow.casterIsTranslucent = translucent;
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
- }
- }
+ auto layers = std::vector<LayerSettings>{layer, caster};
+ // Four combinations of settings are used (two transforms here, and drawShadowLayers is
+ // called with two different destination data spaces) They're all rounded rect.
+ // Three of these are cache misses that generate new shaders.
+ // The first combination generates a short and simple shadow shader.
+ // The second combination, flip transform, generates two shaders. The first appears to involve
+ // gaussian_fp. The second is a long and general purpose shadow shader with a device space
+ // transformation stage.
+ // The third combination is a cache hit, nothing new.
+ // The fourth combination, flip transform with a non-SRGB destination dataspace, is new.
+ // It is unique in that nearly everything is done in the vertex shader, and that vertex shader
+ // requires color correction. This is triggered differently from every other instance of color
+ // correction. All other instances are triggered when src and dst dataspaces differ, while
+ // this one is triggered by the destination being non-srgb. Apparently since the third
+ // combination is a cache hit, this color correction is only added when the vertex shader is
+ // doing something non-trivial.
+ for (auto transform : {mat4(), kFlip}) {
+ layer.geometry.positionTransform = transform;
+ caster.geometry.positionTransform = transform;
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
+ base::unique_fd());
}
}
@@ -138,7 +140,7 @@
}},
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
layer.sourceDataspace = dataspace;
// Cache shaders for both rects and round rects.
@@ -151,7 +153,7 @@
for (auto alpha : {half(.2f), half(1.0f)}) {
layer.alpha = alpha;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -174,13 +176,13 @@
.alpha = 0.5,
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto transform : {mat4(), kScaleAndTranslate}) {
layer.geometry.positionTransform = transform;
for (float roundedCornersRadius : {0.0f, 50.f}) {
layer.geometry.roundedCornersRadius = roundedCornersRadius;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -199,12 +201,12 @@
.skipContentDraw = true,
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
// Different blur code is invoked for radii less and greater than 30 pixels
for (int radius : {9, 60}) {
layer.backgroundBlurRadius = radius;
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
@@ -240,7 +242,7 @@
},
};
- auto layers = std::vector<const LayerSettings*>{&layer};
+ auto layers = std::vector<LayerSettings>{layer};
for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) {
layer.source = pixelSource;
for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) {
@@ -251,7 +253,7 @@
for (float alpha : {0.5f, 1.f}) {
layer.alpha = alpha,
renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ base::unique_fd());
}
}
}
@@ -287,9 +289,8 @@
};
- auto layers = std::vector<const LayerSettings*>{&layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
}
static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
@@ -316,9 +317,8 @@
};
- auto layers = std::vector<const LayerSettings*>{&layer};
- renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache,
- base::unique_fd(), nullptr);
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd());
}
//
@@ -381,6 +381,7 @@
ExternalTexture::Usage::WRITEABLE);
drawHolePunchLayer(renderengine, display, dstTexture);
drawSolidLayers(renderengine, display, dstTexture);
+
drawShadowLayers(renderengine, display, srcTexture);
drawShadowLayers(renderengine, p3Display, srcTexture);
@@ -424,6 +425,16 @@
drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
+ // draw one final layer synchronously to force GL submit
+ LayerSettings layer{
+ .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)},
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ // call get() to make it synchronous
+ renderengine
+ ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd())
+ .get();
+
const nsecs_t timeAfter = systemTime();
const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
const int shadersCompiled = renderengine->reportShadersCompiled();
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
index ff4d348..f367a84 100644
--- a/libs/renderengine/skia/ColorSpaces.cpp
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -20,6 +20,7 @@
namespace renderengine {
namespace skia {
+// please keep in sync with hwui/utils/Color.cpp
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
skcms_Matrix3x3 gamut;
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -32,6 +33,17 @@
case HAL_DATASPACE_STANDARD_DCI_P3:
gamut = SkNamedGamut::kDisplayP3;
break;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ gamut = SkNamedGamut::kAdobeRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
default:
gamut = SkNamedGamut::kSRGB;
break;
@@ -42,10 +54,19 @@
return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
case HAL_DATASPACE_TRANSFER_SRGB:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
case HAL_DATASPACE_TRANSFER_ST2084:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
case HAL_DATASPACE_TRANSFER_HLG:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
default:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index e42b5b9..cc90946 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -46,6 +46,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
+#include <numeric>
#include "../gl/GLExtensions.h"
#include "Cache.h"
@@ -53,6 +54,8 @@
#include "SkBlendMode.h"
#include "SkImageInfo.h"
#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
@@ -328,7 +331,7 @@
if (args.supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
- mBlurFilter = new BlurFilter();
+ mBlurFilter = new KawaseBlurFilter();
}
mCapture = std::make_unique<SkiaCapture>();
}
@@ -611,31 +614,32 @@
};
sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
- sk_sp<SkShader> shader,
- const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- const auto stretchEffect = layer->stretchEffect;
+ const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
// Determine later on if we need to leverage the stertch shader within
// surface flinger
+ const auto& stretchEffect = parameters.layer.stretchEffect;
+ auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = layer->source.buffer.buffer;
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && shader) {
+ if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
}
- if (requiresLinearEffect) {
- const ui::Dataspace inputDataspace =
- mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- const ui::Dataspace outputDataspace =
- mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+ if (parameters.requiresLinearEffect) {
+ const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+ const ui::Dataspace outputDataspace = mUseColorManagement
+ ? parameters.display.outputDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
- LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
- .outputDataspace = outputDataspace,
- .undoPremultipliedAlpha = undoPremultipliedAlpha};
+ auto effect =
+ shaders::LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -645,16 +649,16 @@
} else {
runtimeEffect = effectIter->second;
}
- float maxLuminance = layer->source.buffer.maxLuminanceNits;
- // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
- // white point
- if (maxLuminance <= 0.f) {
- maxLuminance = display.sdrWhitePointNits;
- }
- return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform,
- display.maxLuminance, maxLuminance);
+ mat4 colorTransform = parameters.layer.colorTransform;
+
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+ parameters.display.maxLuminance,
+ parameters.layer.source.buffer.maxLuminanceNits);
}
- return shader;
+ return parameters.shader;
}
void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -727,22 +731,29 @@
return roundedRect;
}
-status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool /*useFramebufferCache*/,
- base::unique_fd&& bufferFence, base::unique_fd* drawFence) {
+static bool equalsWithinMargin(float expected, float value, float margin) {
+ LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+ return std::abs(expected - value) < margin;
+}
+
+void SkiaGLRenderEngine::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/,
+ base::unique_fd&& bufferFence) {
ATRACE_NAME("SkiaGL::drawLayers");
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
}
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
validateOutputBufferUsage(buffer->getBuffer());
@@ -774,7 +785,8 @@
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
ALOGE("Cannot acquire canvas from Skia.");
- return BAD_VALUE;
+ resultPromise->set_value({BAD_VALUE, base::unique_fd()});
+ return;
}
// setup color filter if necessary
@@ -785,6 +797,18 @@
const bool ctModifiesAlpha =
displayColorTransform && !displayColorTransform->isAlphaUnchanged();
+ // Find the max layer white point to determine the max luminance of the scene...
+ const float maxLayerWhitePoint = std::transform_reduce(
+ layers.cbegin(), layers.cend(), 0.f,
+ [](float left, float right) { return std::max(left, right); },
+ [&](const auto& l) { return l.whitePointNits; });
+
+ // ...and compute the dimming ratio if dimming is requested
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+ maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ ? maxLayerWhitePoint / display.targetLuminanceNits
+ : 1.f;
+
// Find if any layers have requested blur, we'll use that info to decide when to render to an
// offscreen buffer and when to render to the native buffer.
sk_sp<SkSurface> activeSurface(dstSurface);
@@ -798,19 +822,19 @@
if (!layerHasBlur(layer, ctModifiesAlpha)) {
continue;
}
- if (layer->backgroundBlurRadius > 0 &&
- layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ if (layer.backgroundBlurRadius > 0 &&
+ layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
- for (auto region : layer->blurRegions) {
- if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ for (auto region : layer.blurRegions) {
+ if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
}
if (requiresCompositionLayer) {
activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
- blurCompositionLayer = layer;
+ blurCompositionLayer = &layer;
break;
}
}
@@ -821,34 +845,12 @@
canvas->clear(SK_ColorTRANSPARENT);
initCanvas(canvas, display);
- // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the
- // view is still on-screen. The clear region could be re-specified as a black color layer,
- // however.
- if (!display.clearRegion.isEmpty()) {
- ATRACE_NAME("ClearRegion");
- size_t numRects = 0;
- Rect const* rects = display.clearRegion.getArray(&numRects);
- SkIRect skRects[numRects];
- for (int i = 0; i < numRects; ++i) {
- skRects[i] =
- SkIRect::MakeLTRB(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom);
- }
- SkRegion clearRegion;
- SkPaint paint;
- sk_sp<SkShader> shader =
- SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
- toSkColorSpace(dstDataspace));
- paint.setShader(shader);
- clearRegion.setRects(skRects, numRects);
- canvas->drawRegion(clearRegion, paint);
- }
-
for (const auto& layer : layers) {
- ATRACE_FORMAT("DrawLayer: %s", layer->name.c_str());
+ ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str());
if (kPrintLayerSettings) {
std::stringstream ls;
- PrintTo(*layer, &ls);
+ PrintTo(layer, &ls);
auto debugs = ls.str();
int pos = 0;
while (pos < debugs.size()) {
@@ -858,7 +860,7 @@
}
sk_sp<SkImage> blurInput;
- if (blurCompositionLayer == layer) {
+ if (blurCompositionLayer == &layer) {
LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface);
LOG_ALWAYS_FATAL_IF(canvas == dstCanvas);
@@ -897,17 +899,17 @@
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
// Record the name of the layer if the capture is running.
std::stringstream layerSettings;
- PrintTo(*layer, &layerSettings);
+ PrintTo(layer, &layerSettings);
// Store the LayerSettings in additional information.
- canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(),
+ canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(),
SkData::MakeWithCString(layerSettings.str().c_str()));
}
// Layers have a local transform that should be applied to them
- canvas->concat(getSkM44(layer->geometry.positionTransform).asM33());
+ canvas->concat(getSkM44(layer.geometry.positionTransform).asM33());
const auto [bounds, roundRectClip] =
- getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop,
- layer->geometry.roundedCornersRadius);
+ getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha)) {
std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs;
@@ -928,20 +930,19 @@
// TODO(b/182216890): Filter out empty layers earlier
if (blurRect.width() > 0 && blurRect.height() > 0) {
- if (layer->backgroundBlurRadius > 0) {
+ if (layer.backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
- auto blurredImage =
- mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput,
- blurRect);
+ auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
+ blurInput, blurRect);
- cachedBlurs[layer->backgroundBlurRadius] = blurredImage;
+ cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
- mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f,
+ mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f,
blurRect, blurredImage, blurInput);
}
- canvas->concat(getSkM44(layer->blurRegionTransform).asM33());
- for (auto region : layer->blurRegions) {
+ canvas->concat(getSkM44(layer.blurRegionTransform).asM33());
+ for (auto region : layer.blurRegions) {
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
@@ -956,19 +957,18 @@
}
}
- if (layer->shadow.length > 0) {
+ if (layer.shadow.length > 0) {
// This would require a new parameter/flag to SkShadowUtils::DrawShadow
- LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow");
+ LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow");
SkRRect shadowBounds, shadowClip;
- if (layer->geometry.boundaries == layer->shadow.boundaries) {
+ if (layer.geometry.boundaries == layer.shadow.boundaries) {
shadowBounds = bounds;
shadowClip = roundRectClip;
} else {
std::tie(shadowBounds, shadowClip) =
- getBoundsAndClip(layer->shadow.boundaries,
- layer->geometry.roundedCornersCrop,
- layer->geometry.roundedCornersRadius);
+ getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
}
// Technically, if bounds is a rect and roundRectClip is not empty,
@@ -979,18 +979,21 @@
// looks more like the intent.
const auto& rrect =
shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds;
- drawShadow(canvas, rrect, layer->shadow);
+ drawShadow(canvas, rrect, layer.shadow);
}
- const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+ const float layerDimmingRatio = layer.whitePointNits <= 0.f
+ ? displayDimmingRatio
+ : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
+ const bool requiresLinearEffect = layer.colorTransform != mat4() ||
(mUseColorManagement &&
- needsToneMapping(layer->sourceDataspace, display.outputDataspace)) ||
- (display.sdrWhitePointNits > 0.f &&
- display.sdrWhitePointNits != display.maxLuminance);
+ needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
+ !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);
// quick abort from drawing the remaining portion of the layer
- if (layer->skipContentDraw ||
- (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending &&
+ if (layer.skipContentDraw ||
+ (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending &&
(!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) {
continue;
}
@@ -1000,13 +1003,13 @@
// management is a no-op.
const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
? dstDataspace
- : layer->sourceDataspace;
+ : layer.sourceDataspace;
SkPaint paint;
- if (layer->source.buffer.buffer) {
+ if (layer.source.buffer.buffer) {
ATRACE_NAME("DrawImage");
- validateInputBufferUsage(layer->source.buffer.buffer->getBuffer());
- const auto& item = layer->source.buffer;
+ validateInputBufferUsage(layer.source.buffer.buffer->getBuffer());
+ const auto& item = layer.source.buffer;
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = nullptr;
if (const auto& iter = cache.find(item.buffer->getBuffer()->getId());
@@ -1025,8 +1028,8 @@
// if the layer's buffer has a fence, then we must must respect the fence prior to using
// the buffer.
- if (layer->source.buffer.fence != nullptr) {
- waitFence(layer->source.buffer.fence->get());
+ if (layer.source.buffer.fence != nullptr) {
+ waitFence(layer.source.buffer.fence->get());
}
// isOpaque means we need to ignore the alpha in the image,
@@ -1070,7 +1073,7 @@
sk_sp<SkShader> shader;
- if (layer->source.buffer.useTextureFiltering) {
+ if (layer.source.buffer.useTextureFiltering) {
shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(
{SkFilterMode::kLinear, SkMipmapMode::kNone}),
@@ -1085,24 +1088,39 @@
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- !item.isOpaque && item.usePremultipliedAlpha,
- requiresLinearEffect));
- paint.setAlphaf(layer->alpha);
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
+
+ // Turn on dithering when dimming beyond this threshold.
+ static constexpr float kDimmingThreshold = 0.2f;
+ if (layerDimmingRatio <= kDimmingThreshold) {
+ paint.setDither(true);
+ }
+ paint.setAlphaf(layer.alpha);
} else {
ATRACE_NAME("DrawColor");
- const auto color = layer->source.solidColor;
+ const auto color = layer.source.solidColor;
sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r,
.fG = color.g,
.fB = color.b,
- .fA = layer->alpha},
+ .fA = layer.alpha},
toSkColorSpace(layerDataspace));
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- /* undoPremultipliedAlpha */ false,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = false,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
}
- if (layer->disableBlending) {
+ if (layer.disableBlending) {
paint.setBlendMode(SkBlendMode::kSrc);
}
@@ -1131,13 +1149,11 @@
activeSurface->flush();
}
- if (drawFence != nullptr) {
- *drawFence = flush();
- }
+ 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.
- bool requireSync = drawFence == nullptr || drawFence->get() < 0;
+ bool requireSync = drawFence.get() < 0;
if (requireSync) {
ATRACE_BEGIN("Submit(sync=true)");
} else {
@@ -1149,11 +1165,13 @@
ALOGE("Failed to flush RenderEngine commands");
// Chances are, something illegal happened (either the caller passed
// us bad parameters, or we messed up our shader generation).
- return INVALID_OPERATION;
+ resultPromise->set_value({INVALID_OPERATION, std::move(drawFence)});
+ return;
}
// checkErrors();
- return NO_ERROR;
+ resultPromise->set_value({NO_ERROR, std::move(drawFence)});
+ return;
}
inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) {
@@ -1164,6 +1182,73 @@
return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
+/**
+ * Verifies that common, simple bounds + clip combinations can be converted into
+ * a single RRect draw call returning true if possible. If true the radii parameter
+ * will be filled with the correct radii values that combined with bounds param will
+ * produce the insected roundRect. If false, the returned state of the radii param is undefined.
+ */
+static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop,
+ const SkRect& insetCrop, float cornerRadius,
+ SkVector radii[4]) {
+ const bool leftEqual = bounds.fLeft == crop.fLeft;
+ const bool topEqual = bounds.fTop == crop.fTop;
+ const bool rightEqual = bounds.fRight == crop.fRight;
+ const bool bottomEqual = bounds.fBottom == crop.fBottom;
+
+ // In the event that the corners of the bounds only partially align with the crop we
+ // need to ensure that the resulting shape can still be represented as a round rect.
+ // In particular the round rect implementation will scale the value of all corner radii
+ // if the sum of the radius along any edge is greater than the length of that edge.
+ // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
+ const bool requiredWidth = bounds.width() > (cornerRadius * 2);
+ const bool requiredHeight = bounds.height() > (cornerRadius * 2);
+ if (!requiredWidth || !requiredHeight) {
+ return false;
+ }
+
+ // Check each cropped corner to ensure that it exactly matches the crop or its corner is
+ // contained within the cropped shape and does not need rounded.
+ // compute the UpperLeft corner radius
+ if (leftEqual && topEqual) {
+ radii[0].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[0].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the UpperRight corner radius
+ if (rightEqual && topEqual) {
+ radii[1].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
+ (topEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[1].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomRight corner radius
+ if (rightEqual && bottomEqual) {
+ radii[2].set(cornerRadius, cornerRadius);
+ } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fRight <= insetCrop.fRight)) {
+ radii[2].set(0, 0);
+ } else {
+ return false;
+ }
+ // compute the BottomLeft corner radius
+ if (leftEqual && bottomEqual) {
+ radii[3].set(cornerRadius, cornerRadius);
+ } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
+ (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) {
+ radii[3].set(0, 0);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect,
const FloatRect& cropRect,
const float cornerRadius) {
@@ -1181,66 +1266,20 @@
// converting them to a single RRect draw. It is possible there are other cases
// that can be converted.
if (crop.contains(bounds)) {
- bool intersectionIsRoundRect = true;
- // check each cropped corner to ensure that it exactly matches the crop or is full
- SkVector radii[4];
-
const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius);
-
- const bool leftEqual = bounds.fLeft == crop.fLeft;
- const bool topEqual = bounds.fTop == crop.fTop;
- const bool rightEqual = bounds.fRight == crop.fRight;
- const bool bottomEqual = bounds.fBottom == crop.fBottom;
-
- // compute the UpperLeft corner radius
- if (leftEqual && topEqual) {
- radii[0].set(cornerRadius, cornerRadius);
- } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fLeft >= insetCrop.fLeft) ||
- insetCrop.contains(bounds.fLeft, bounds.fTop)) {
- radii[0].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the UpperRight corner radius
- if (rightEqual && topEqual) {
- radii[1].set(cornerRadius, cornerRadius);
- } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) ||
- (topEqual && bounds.fRight <= insetCrop.fRight) ||
- insetCrop.contains(bounds.fRight, bounds.fTop)) {
- radii[1].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the BottomRight corner radius
- if (rightEqual && bottomEqual) {
- radii[2].set(cornerRadius, cornerRadius);
- } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fRight <= insetCrop.fRight) ||
- insetCrop.contains(bounds.fRight, bounds.fBottom)) {
- radii[2].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
- }
- // compute the BottomLeft corner radius
- if (leftEqual && bottomEqual) {
- radii[3].set(cornerRadius, cornerRadius);
- } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) ||
- (bottomEqual && bounds.fLeft >= insetCrop.fLeft) ||
- insetCrop.contains(bounds.fLeft, bounds.fBottom)) {
- radii[3].set(0, 0);
- } else {
- intersectionIsRoundRect = false;
+ if (insetCrop.contains(bounds)) {
+ return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required
}
- if (intersectionIsRoundRect) {
+ SkVector radii[4];
+ if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) {
SkRRect intersectionBounds;
intersectionBounds.setRectRadii(bounds, radii);
return {intersectionBounds, clip};
}
}
- // we didn't it any of our fast paths so set the clip to the cropRect
+ // we didn't hit any of our fast paths so set the clip to the cropRect
clip.setRectXY(crop, cornerRadius, cornerRadius);
}
@@ -1249,13 +1288,13 @@
return {SkRRect::MakeRect(bounds), clip};
}
-inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer,
+inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings& layer,
bool colorTransformModifiesAlpha) {
- if (layer->backgroundBlurRadius > 0 || layer->blurRegions.size()) {
+ if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) {
// return false if the content is opaque and would therefore occlude the blur
- const bool opaqueContent = !layer->source.buffer.buffer || layer->source.buffer.isOpaque;
- const bool opaqueAlpha = layer->alpha == 1.0f && !colorTransformModifiesAlpha;
- return layer->skipContentDraw || !(opaqueContent && opaqueAlpha);
+ const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque;
+ const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha;
+ return layer.skipContentDraw || !(opaqueContent && opaqueAlpha);
}
return false;
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index e1162f5..a650313 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -54,11 +54,6 @@
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
std::future<void> primeCache() override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
void cleanupPostRender() override;
void cleanFramebufferCache() override{};
int getContextPriority() override;
@@ -77,6 +72,11 @@
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
private:
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
@@ -92,7 +92,7 @@
inline SkRect getSkRect(const Rect& layer);
inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds,
const FloatRect& crop, float cornerRadius);
- inline bool layerHasBlur(const LayerSettings* layer, bool colorTransformModifiesAlpha);
+ inline bool layerHasBlur(const LayerSettings& layer, bool colorTransformModifiesAlpha);
inline SkColor getSkColor(const vec4& color);
inline SkM44 getSkM44(const mat4& matrix);
inline SkPoint3 getSkPoint3(const vec3& vector);
@@ -106,13 +106,18 @@
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
+
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings* layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect);
+ struct RuntimeEffectShaderParameters {
+ sk_sp<SkShader> shader;
+ const LayerSettings& layer;
+ const DisplaySettings& display;
+ bool undoPremultipliedAlpha;
+ bool requiresLinearEffect;
+ float layerDimmingRatio;
+ };
+ sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
@@ -134,7 +139,8 @@
// Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
+ mRuntimeEffects;
AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 7cd9eca..eb65e83 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -44,14 +44,6 @@
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual bool isProtected() const override { return false; } // mInProtectedContext; }
virtual bool supportsProtectedContent() const override { return false; };
- virtual status_t drawLayers(const DisplaySettings& /*display*/,
- const std::vector<const LayerSettings*>& /*layers*/,
- const std::shared_ptr<ExternalTexture>& /*buffer*/,
- const bool /*useFramebufferCache*/,
- base::unique_fd&& /*bufferFence*/,
- base::unique_fd* /*drawFence*/) override {
- return 0;
- };
virtual int getContextPriority() override { return 0; }
virtual void assertShadersCompiled(int numShaders) {}
virtual int reportShadersCompiled() { return 0; }
@@ -60,6 +52,14 @@
virtual void mapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/,
bool /*isRenderable*/) override = 0;
virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/) override = 0;
+
+ virtual void drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) override {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ };
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 7c5bee9..6746e47 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -15,7 +15,6 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
@@ -32,40 +31,14 @@
namespace renderengine {
namespace skia {
-BlurFilter::BlurFilter() {
- SkString blurString(R"(
- uniform shader input;
- uniform float2 in_blurOffset;
- uniform float2 in_maxSizeXY;
-
- half4 main(float2 xy) {
- half4 c = sample(input, xy);
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += sample(input, float2( clamp( -in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
-
- auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
- if (!blurEffect) {
- LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
- }
- mBlurEffect = std::move(blurEffect);
-
+static sk_sp<SkRuntimeEffect> createMixEffect() {
SkString mixString(R"(
uniform shader blurredInput;
uniform shader originalInput;
uniform float mixFactor;
half4 main(float2 xy) {
- return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor));
+ return half4(mix(originalInput.eval(xy), blurredInput.eval(xy), mixFactor));
}
)");
@@ -73,58 +46,12 @@
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
- mMixEffect = std::move(mixEffect);
+ return mixEffect;
}
-sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect) const {
- // 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.
- float tmpRadius = (float)blurRadius / 2.0f;
- float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
- float radiusByPasses = tmpRadius / (float)numberOfPasses;
-
- // create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
- std::ceil(blurRect.height() * kInputScale));
-
- const float stepX = radiusByPasses;
- const float stepY = radiusByPasses;
-
- // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
- // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
- // but instead we must do the inverse.
- SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
- blurMatrix.postScale(kInputScale, kInputScale);
-
- // start by downscaling and doing the first blur pass
- SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("input") =
- input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
-
- // And now we'll build our chain of scaled blur stages
- for (auto i = 1; i < numberOfPasses; i++) {
- const float stepScale = (float)i * kInputScale;
- blurBuilder.child("input") =
- tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
- }
-
- return tmpBlur;
-}
-
-static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
- // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect,
+ const float scale) {
+ // 1. Apply the blur shader matrix, which scales up the blurred surface to its real size
auto matrix = SkMatrix::Scale(scale, scale);
// 2. Since the blurred surface has the size of the layer, we align it with the
// top left corner of the layer position.
@@ -139,6 +66,14 @@
return matrix;
}
+BlurFilter::BlurFilter(const float maxCrossFadeRadius)
+ : mMaxCrossFadeRadius(maxCrossFadeRadius),
+ mMixEffect(maxCrossFadeRadius > 0 ? createMixEffect() : nullptr) {}
+
+float BlurFilter::getMaxCrossFadeRadius() const {
+ return mMaxCrossFadeRadius;
+}
+
void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
@@ -153,7 +88,7 @@
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < mMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -166,7 +101,7 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / mMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 7110018..9cddc75 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -27,29 +27,19 @@
namespace renderengine {
namespace skia {
-/**
- * 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 kInputScale = 0.25f;
// Downsample scale factor used to improve performance
static constexpr float kInverseInputScale = 1.0f / kInputScale;
- // 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 = 10.0f;
- explicit BlurFilter();
- virtual ~BlurFilter(){};
+ explicit BlurFilter(float maxCrossFadeRadius = 10.0f);
+ virtual ~BlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
- const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+ virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
/**
* Draw the blurred content (from the generate method) into the canvas.
@@ -61,13 +51,20 @@
* @param blurredImage down-sampled blurred content that was produced by the generate() method
* @param input original unblurred input that is used to crossfade with the blurredImage
*/
- void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
- const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
- sk_sp<SkImage> input);
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
+ const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
+
+ float getMaxCrossFadeRadius() const;
private:
- sk_sp<SkRuntimeEffect> mBlurEffect;
- sk_sp<SkRuntimeEffect> mMixEffect;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ const float mMaxCrossFadeRadius;
+
+ // Optional blend used for crossfade only if mMaxCrossFadeRadius > 0
+ const sk_sp<SkRuntimeEffect> mMixEffect;
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..55867a9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkImageFilters.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
+
+sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo);
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setImageFilter(SkImageFilters::Blur(
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ SkTileMode::kClamp, nullptr));
+
+ surface->getCanvas()->drawImageRect(
+ input,
+ blurRect,
+ SkRect::MakeWH(scaledInfo.width(), scaledInfo.height()),
+ SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone},
+ &paint,
+ SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..a4febd2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Gaussian blur using Skia's built-in GaussianBlur filter.
+ */
+class GaussianBlurFilter: public BlurFilter {
+public:
+ explicit GaussianBlurFilter();
+ virtual ~GaussianBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..bfde06f
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+
+ half4 main(float2 xy) {
+ half4 c = child.eval(xy);
+ c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
+ c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // 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.
+ float tmpRadius = (float)blurRadius / 2.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ // create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+
+ // start by downscaling and doing the first blur pass
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+ blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
+
+ sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+ // And now we'll build our chain of scaled blur stages
+ for (auto i = 1; i < numberOfPasses; i++) {
+ blurBuilder.child("child") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+ tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ }
+
+ return tmpBlur;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..0ac5ac8
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * 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 KawaseBlurFilter: public BlurFilter {
+public:
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+
+ explicit KawaseBlurFilter();
+ virtual ~KawaseBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index fc45af9..36305ae 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,423 +19,19 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <SkString.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
#include <utils/Trace.h>
-#include <optional>
-
-#include "log/log.h"
-#include "math/mat4.h"
-#include "system/graphics-base-v1.0.h"
-#include "ui/ColorSpace.h"
+#include <math/mat4.h>
namespace android {
namespace renderengine {
namespace skia {
-static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 EOTF(float3 color) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / float3(m1));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float EOTF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- float3 EOTF(float3 color) {
- return float3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 EOTF(float3 color) {
- return color;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateXYZTransforms(SkString& shader) {
- shader.append(R"(
- uniform float4x4 in_rgbToXyz;
- uniform float4x4 in_xyzToRgb;
- float3 ToXYZ(float3 rgb) {
- return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
- }
-
- float3 ToRGB(float3 xyz) {
- return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
- }
- )");
-}
-
-// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 1000.0 * pow(xyz.y, 0.2);
- }
- )");
- break;
- default:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_inputMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
- ui::Dataspace outputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
- // we'll clamp the luminance range in case we're mapping from PQ input to HLG
- // output.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return clamp(xyz, 0.0, 1000.0);
- }
- )");
- break;
- default:
- // Here we're mapping from HDR to SDR content, so interpolate using a Hermitian
- // polynomial onto the smaller luminance range.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- float maxInLumi = in_inputMaxLuminance;
- float maxOutLumi = in_displayMaxLuminance;
-
- float nits = xyz.y;
-
- // if the max input luminance is less than what we can output then
- // no tone mapping is needed as all color values will be in range.
- if (maxInLumi <= maxOutLumi) {
- return xyz;
- } 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 xyz * 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 xyz * (nits / xyz.y);
- }
- )");
- break;
- }
- break;
- default:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // Map from SDR onto an HDR output buffer
- // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
- // [0, maxOutLumi] which is hard-coded to be 3000 nits.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- const float maxOutLumi = 3000.0;
-
- const float x0 = 5.0;
- const float y0 = 2.5;
- float x1 = in_displayMaxLuminance * 0.7;
- float y1 = maxOutLumi * 0.15;
- float x2 = in_displayMaxLuminance * 0.9;
- float y2 = maxOutLumi * 0.45;
- float x3 = in_displayMaxLuminance;
- float y3 = maxOutLumi;
-
- float c1 = y1 / 3.0;
- float c2 = y2 / 2.0;
- float c3 = y3 / 1.5;
-
- float nits = xyz.y;
-
- if (nits <= x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return xyz * 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;
- }
-
- // xyz.y is greater than x0 and is thus non-zero
- return xyz * (nits / xyz.y);
- }
- )");
- break;
- default:
- // For completeness, this is tone-mapping from SDR to SDR, where this is just a
- // no-op.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- }
- break;
- }
-}
-
-// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
- }
- )");
- break;
- default:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / in_displayMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- SkString& shader) {
- // Input uniforms
- shader.append(R"(
- uniform float in_displayMaxLuminance;
- uniform float in_inputMaxLuminance;
- )");
-
- generateLuminanceScalesForOOTF(inputDataspace, shader);
- generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
-
- shader.append(R"(
- float3 OOTF(float3 xyz) {
- return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
- }
- )");
-}
-
-static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 OETF(float3 xyz) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(xyz, float3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, float3(m2));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float OETF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- float3 OETF(float3 linear) {
- return float3(OETF_channel(linear.r), OETF_channel(linear.g),
- OETF_channel(linear.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 OETF(float3 linear) {
- return linear;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
- shader.append(R"(
- uniform shader input;
- half4 main(float2 xy) {
- float4 c = float4(sample(input, xy));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb / (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb * (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- return c;
- }
- )");
-}
-static ColorSpace toColorSpace(ui::Dataspace dataspace) {
- switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- return ColorSpace::sRGB();
- break;
- case HAL_DATASPACE_STANDARD_DCI_P3:
- return ColorSpace::DisplayP3();
- break;
- case HAL_DATASPACE_STANDARD_BT2020:
- return ColorSpace::BT2020();
- break;
- default:
- return ColorSpace::sRGB();
- break;
- }
-}
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
ATRACE_CALL();
- SkString shaderString;
- generateEOTF(linearEffect.inputDataspace, shaderString);
- generateXYZTransforms(shaderString);
- generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
- generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
if (!shader) {
@@ -444,32 +40,23 @@
return shader;
}
-sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance) {
ATRACE_CALL();
SkRuntimeShaderBuilder effectBuilder(runtimeEffect);
- effectBuilder.child("input") = shader;
+ effectBuilder.child("child") = shader;
- if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
- effectBuilder.uniform("in_rgbToXyz") = mat4();
- effectBuilder.uniform("in_xyzToRgb") = colorTransform;
- } else {
- ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
- ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
+ maxDisplayLuminance, maxLuminance);
- effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
- effectBuilder.uniform("in_xyzToRgb") =
- colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
}
- effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance;
- // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes)
- // This will be the case for eg screenshots in addition to uncalibrated displays
- effectBuilder.uniform("in_inputMaxLuminance") =
- maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
return effectBuilder.makeShader(nullptr, false);
}
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 14a3b61..8eb6670 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -20,6 +20,7 @@
#include <optional>
+#include <shaders/shaders.h>
#include "SkRuntimeEffect.h"
#include "SkShader.h"
#include "ui/GraphicTypes.h"
@@ -28,61 +29,7 @@
namespace renderengine {
namespace skia {
-/**
- * Arguments for creating an effect that applies color transformations in linear XYZ space.
- * A linear effect is decomposed into the following steps when operating on an image:
- * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
- * relative display brightness of the scene in nits for each RGB channel
- * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
- * luminance.
- * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
- * mapping to display SDR content alongside HDR content, or any number of subjective transformations
- * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
- * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
- * output RGB colors.
- *
- * For further reading, consult the recommendation in ITU-R BT.2390-4:
- * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
- *
- * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
- * intended to be the output surface. However, Skia does not support complex tone mapping such as
- * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
- * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
- * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
- * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
- * and intermediate values are interpolated.
- */
-struct LinearEffect {
- // Input dataspace of the source colors.
- const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
-
- // Working dataspace for the output surface, for conversion from linear space.
- const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
-
- // Sets whether alpha premultiplication must be undone.
- // This is required if the source colors use premultiplied alpha and is not opaque.
- const bool undoPremultipliedAlpha = false;
-};
-
-static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
- return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
- lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
-}
-
-struct LinearEffectHasher {
- // Inspired by art/runtime/class_linker.cc
- // Also this is what boost:hash_combine does
- static size_t HashCombine(size_t seed, size_t val) {
- return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
- }
- size_t operator()(const LinearEffect& le) const {
- size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
- result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
- return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
- }
-};
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect);
// Generates a shader resulting from applying the a linear effect created from
// LinearEffectArgs::buildEffect to an inputShader.
@@ -93,7 +40,7 @@
// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
// or as the max light level from the CTA 861.3 standard.
sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
- const LinearEffect& linearEffect,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance);
diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
index 4ac5c40..c262e35 100644
--- a/libs/renderengine/skia/filters/StretchShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp
@@ -184,7 +184,7 @@
);
coord.x = (outU - uScrollX) * viewportWidth;
coord.y = (outV - uScrollY) * viewportHeight;
- return sample(uContentTexture, coord);
+ return uContentTexture.eval(coord);
})");
const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index d0e19dd..a426850 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -23,7 +23,10 @@
cc_test {
name: "librenderengine_test",
- defaults: ["skia_deps", "surfaceflinger_defaults"],
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
@@ -36,6 +39,8 @@
"libgmock",
"librenderengine",
"librenderengine_mocks",
+ "libshaders",
+ "libtonemap",
],
shared_libs: [
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 33e3773..eb2b2dc 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -27,6 +27,9 @@
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
+#include <system/graphics-base-v1.0.h>
+#include <tonemap/tonemap.h>
+#include <ui/ColorSpace.h>
#include <ui/PixelFormat.h>
#include <chrono>
@@ -205,6 +208,21 @@
renderengine::ExternalTexture::Usage::WRITEABLE);
}
+ std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
+ uint32_t height,
+ ubyte4 color) {
+ const auto buffer = allocateSourceBuffer(width, height);
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = color.r;
+ pixels[1] = color.g;
+ pixels[2] = color.b;
+ pixels[3] = color.a;
+ buffer->getBuffer()->unlock();
+ return buffer;
+ }
+
RenderEngineTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
@@ -282,6 +300,13 @@
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
+ auto generator = [=](Point) { return ubyte4(r, g, b, a); };
+ expectBufferColor(rect, generator, tolerance);
+ }
+
+ using ColorGenerator = std::function<ubyte4(Point location)>;
+
+ void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
uint8_t tmp = a >= b ? a - b : b - a;
@@ -290,10 +315,10 @@
return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
- expectBufferColor(rect, r, g, b, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ void expectBufferColor(const Rect& region, ColorGenerator generator,
std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
uint8_t* pixels;
mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -304,19 +329,22 @@
const uint8_t* src = pixels +
(mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
- const uint8_t expected[4] = {r, g, b, a};
- bool equal = colorCompare(src, expected);
- EXPECT_TRUE(equal)
+ const auto location = Point(region.left + i, region.top + j);
+ const ubyte4 colors = generator(location);
+ const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a};
+ bool colorMatches = colorCompare(src, expected);
+ EXPECT_TRUE(colorMatches)
<< GetParam()->name().c_str() << ": "
- << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
- << "expected (" << static_cast<uint32_t>(r) << ", "
- << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
- << static_cast<uint32_t>(a) << "), "
+ << "pixel @ (" << location.x << ", " << location.y << "): "
+ << "expected (" << static_cast<uint32_t>(colors.r) << ", "
+ << static_cast<uint32_t>(colors.g) << ", "
+ << static_cast<uint32_t>(colors.b) << ", "
+ << static_cast<uint32_t>(colors.a) << "), "
<< "got (" << static_cast<uint32_t>(src[0]) << ", "
<< static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
<< ", " << static_cast<uint32_t>(src[3]) << ")";
src += 4;
- if (!equal && ++fails >= maxFails) {
+ if (!colorMatches && ++fails >= maxFails) {
break;
}
}
@@ -328,10 +356,11 @@
}
void expectAlpha(const Rect& rect, uint8_t a) {
+ auto generator = [=](Point) { return ubyte4(0, 0, 0, a); };
auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
return colorA[3] == colorB[3];
};
- expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
void expectShadowColor(const renderengine::LayerSettings& castingLayer,
@@ -417,19 +446,19 @@
DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
}
- void invokeDraw(renderengine::DisplaySettings settings,
- std::vector<const renderengine::LayerSettings*> layers) {
- base::unique_fd fence;
- status_t status =
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence);
+ void invokeDraw(const renderengine::DisplaySettings& settings,
+ const std::vector<renderengine::LayerSettings>& layers) {
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
- int fd = fence.release();
- if (fd >= 0) {
- sync_wait(fd, -1);
- close(fd);
- }
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
+ }
+
if (layers.size() > 0 && mGLESRE != nullptr) {
ASSERT_TRUE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
}
@@ -437,7 +466,7 @@
void drawEmptyLayers() {
renderengine::DisplaySettings settings;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
invokeDraw(settings, layers);
}
@@ -490,6 +519,18 @@
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferWithColorTransformAndSourceDataspace(const ui::Dataspace sourceDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndSourceDataspace();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransformAndOutputDataspace(const ui::Dataspace outputDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndOutputDataspace();
+
+ template <typename SourceVariant>
void fillBufferWithColorTransformZeroLayerAlpha();
template <typename SourceVariant>
@@ -524,10 +565,6 @@
void fillGreenColorBufferThenClearRegion();
- void clearLeftRegion();
-
- void clearRegion();
-
template <typename SourceVariant>
void drawShadow(const renderengine::LayerSettings& castingLayer,
const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
@@ -634,7 +671,7 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -642,7 +679,7 @@
SourceVariant::fillColor(layer, r, g, b, this);
layer.alpha = a;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -678,7 +715,7 @@
settings.physicalDisplay = offsetRect();
settings.clip = offsetRectAtZero();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -686,7 +723,7 @@
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -713,7 +750,7 @@
settings.clip = Rect(2, 2);
settings.orientation = orientationFlag;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layerOne;
layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -736,9 +773,9 @@
SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this);
layerThree.alpha = 1.0f;
- layers.push_back(&layerOne);
- layers.push_back(&layerTwo);
- layers.push_back(&layerThree);
+ layers.push_back(layerOne);
+ layers.push_back(layerTwo);
+ layers.push_back(layerThree);
invokeDraw(settings, layers);
}
@@ -815,7 +852,7 @@
settings.clip = Rect(2, 2);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -826,7 +863,7 @@
layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -848,7 +885,7 @@
settings.clip = Rect(1, 1);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -865,7 +902,37 @@
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace(
+ const ui::Dataspace sourceDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = sourceDataspace;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -877,12 +944,74 @@
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255};
+ for (const auto& [sourceDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace(
+ const ui::Dataspace outputDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = outputDataspace;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255};
+ for (const auto& [outputDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -895,7 +1024,7 @@
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -913,7 +1042,7 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -923,7 +1052,7 @@
SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0f;
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -954,14 +1083,14 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings backgroundLayer;
backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
backgroundLayer.alpha = 1.0f;
- layers.push_back(&backgroundLayer);
+ layers.emplace_back(backgroundLayer);
renderengine::LayerSettings leftLayer;
leftLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -969,7 +1098,7 @@
Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
leftLayer.alpha = 1.0f;
- layers.push_back(&leftLayer);
+ layers.emplace_back(leftLayer);
renderengine::LayerSettings blurLayer;
blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -977,7 +1106,7 @@
blurLayer.backgroundBlurRadius = blurRadius;
SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
blurLayer.alpha = 0;
- layers.push_back(&blurLayer);
+ layers.emplace_back(blurLayer);
invokeDraw(settings, layers);
@@ -999,14 +1128,14 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings backgroundLayer;
backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this);
backgroundLayer.alpha = 1.0f;
- layers.push_back(&backgroundLayer);
+ layers.push_back(backgroundLayer);
renderengine::LayerSettings blurLayer;
blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1014,7 +1143,7 @@
blurLayer.backgroundBlurRadius = blurRadius;
SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this);
blurLayer.alpha = 0;
- layers.push_back(&blurLayer);
+ layers.push_back(blurLayer);
invokeDraw(settings, layers);
@@ -1031,7 +1160,7 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layersFirst;
+ std::vector<renderengine::LayerSettings> layersFirst;
renderengine::LayerSettings layerOne;
layerOne.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1040,14 +1169,14 @@
SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
layerOne.alpha = 0.2;
- layersFirst.push_back(&layerOne);
+ layersFirst.push_back(layerOne);
invokeDraw(settings, layersFirst);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
0, 0, 0, 0);
- std::vector<const renderengine::LayerSettings*> layersSecond;
+ std::vector<renderengine::LayerSettings> layersSecond;
renderengine::LayerSettings layerTwo;
layerTwo.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
layerTwo.geometry.boundaries =
@@ -1056,7 +1185,7 @@
SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
layerTwo.alpha = 1.0f;
- layersSecond.push_back(&layerTwo);
+ layersSecond.push_back(layerTwo);
invokeDraw(settings, layersSecond);
expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
@@ -1071,7 +1200,7 @@
settings.clip = Rect(1, 1);
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1103,11 +1232,11 @@
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.2, 0.2, 1, 1));
+ layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1123,7 +1252,7 @@
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
@@ -1146,7 +1275,7 @@
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1162,7 +1291,7 @@
// Here logical space is 1x1
settings.clip = Rect(1, 1);
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
const auto buf = allocateSourceBuffer(1, 1);
@@ -1185,7 +1314,7 @@
layer.alpha = 0.5f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1195,28 +1324,6 @@
expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
-void RenderEngineTest::clearLeftRegion() {
- renderengine::DisplaySettings settings;
- settings.physicalDisplay = fullscreenRect();
- // Here logical space is 4x4
- settings.clip = Rect(4, 4);
- settings.clearRegion = Region(Rect(2, 4));
- std::vector<const renderengine::LayerSettings*> layers;
- // fake layer, without bounds should not render anything
- renderengine::LayerSettings layer;
- layers.push_back(&layer);
- invokeDraw(settings, layers);
-}
-
-void RenderEngineTest::clearRegion() {
- // Reuse mBuffer
- clearLeftRegion();
- expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255);
- expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH,
- DEFAULT_DISPLAY_HEIGHT),
- 0, 0, 0, 0);
-}
-
template <typename SourceVariant>
void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
const renderengine::ShadowSettings& shadow,
@@ -1226,7 +1333,7 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
// add background layer
renderengine::LayerSettings bgLayer;
@@ -1235,7 +1342,7 @@
ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
backgroundColor.b / 255.0f, this);
bgLayer.alpha = backgroundColor.a / 255.0f;
- layers.push_back(&bgLayer);
+ layers.push_back(bgLayer);
// add shadow layer
renderengine::LayerSettings shadowLayer;
@@ -1243,14 +1350,14 @@
shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
shadowLayer.alpha = castingLayer.alpha;
shadowLayer.shadow = shadow;
- layers.push_back(&shadowLayer);
+ layers.push_back(shadowLayer);
// add layer casting the shadow
renderengine::LayerSettings layer = castingLayer;
layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
casterColor.b / 255.0f, this);
- layers.push_back(&layer);
+ layers.push_back(layer);
invokeDraw(settings, layers);
}
@@ -1263,7 +1370,7 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
// add background layer
renderengine::LayerSettings bgLayer;
@@ -1272,7 +1379,7 @@
ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
backgroundColor.b / 255.0f, this);
bgLayer.alpha = backgroundColor.a / 255.0f;
- layers.push_back(&bgLayer);
+ layers.push_back(bgLayer);
// add shadow layer
renderengine::LayerSettings shadowLayer;
@@ -1282,7 +1389,7 @@
shadowLayer.alpha = 1.0f;
ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this);
shadowLayer.shadow = shadow;
- layers.push_back(&shadowLayer);
+ layers.push_back(shadowLayer);
invokeDraw(settings, layers);
}
@@ -1307,7 +1414,8 @@
settings.clip = fullscreenRect();
// 255, 255, 255, 255 is full opaque white.
- const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
// Create layer with given color.
renderengine::LayerSettings bgLayer;
bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1318,8 +1426,8 @@
// Transform the red color.
bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
- std::vector<const renderengine::LayerSettings*> layers;
- layers.push_back(&bgLayer);
+ std::vector<renderengine::LayerSettings> layers;
+ layers.push_back(bgLayer);
invokeDraw(settings, layers);
@@ -1333,35 +1441,18 @@
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layers.push_back(&layer);
- base::unique_fd fence;
- status_t status = mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
+ layers.push_back(layer);
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd());
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
ASSERT_EQ(BAD_VALUE, status);
-}
-
-TEST_P(RenderEngineTest, drawLayers_nullOutputFence) {
- initializeRenderEngine();
-
- renderengine::DisplaySettings settings;
- settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- settings.physicalDisplay = fullscreenRect();
- settings.clip = fullscreenRect();
-
- std::vector<const renderengine::LayerSettings*> layers;
- renderengine::LayerSettings layer;
- layer.geometry.boundaries = fullscreenRect().toFloatRect();
- BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
- layer.alpha = 1.0;
- layers.push_back(&layer);
-
- status_t status = mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr);
- ASSERT_EQ(NO_ERROR, status);
- expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+ ASSERT_FALSE(fence.ok());
}
TEST_P(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) {
@@ -1379,15 +1470,23 @@
settings.physicalDisplay = fullscreenRect();
settings.clip = fullscreenRect();
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(&layer);
+ layers.push_back(layer);
- status_t status = mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr);
+ std::future<renderengine::RenderEngineResult> result =
+ mRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd());
+ ASSERT_TRUE(result.valid());
+ auto [status, fence] = result.get();
+
ASSERT_EQ(NO_ERROR, status);
+ if (fence.ok()) {
+ sync_wait(fence.get(), -1);
+ }
+
ASSERT_FALSE(mGLESRE->isFramebufferImageCachedForTesting(mBuffer->getBuffer()->getId()));
expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
}
@@ -1447,6 +1546,36 @@
fillBufferColorTransform<ColorSourceVariant>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<ColorSourceVariant>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<ColorSourceVariant>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
@@ -1527,6 +1656,36 @@
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1607,6 +1766,36 @@
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1647,15 +1836,11 @@
fillBufferWithoutPremultiplyAlpha();
}
-TEST_P(RenderEngineTest, drawLayers_clearRegion) {
- initializeRenderEngine();
- clearRegion();
-}
-
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
initializeRenderEngine();
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1670,8 +1855,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(1, 1);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1689,8 +1876,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1709,8 +1898,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1730,8 +1921,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1784,22 +1977,28 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings layer;
layer.geometry.boundaries = fullscreenRect().toFloatRect();
BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
layer.alpha = 1.0;
- layers.push_back(&layer);
+ layers.push_back(layer);
- base::unique_fd fenceOne;
- mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
- base::unique_fd fenceTwo;
- mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+ std::future<renderengine::RenderEngineResult> resultOne =
+ mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd());
+ ASSERT_TRUE(resultOne.valid());
+ auto [statusOne, fenceOne] = resultOne.get();
+ ASSERT_EQ(NO_ERROR, statusOne);
- const int fd = fenceTwo.get();
- if (fd >= 0) {
- sync_wait(fd, -1);
+ std::future<renderengine::RenderEngineResult> resultTwo =
+ mRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne));
+ ASSERT_TRUE(resultTwo.valid());
+ auto [statusTwo, fenceTwo] = resultTwo.get();
+ ASSERT_EQ(NO_ERROR, statusTwo);
+ if (fenceTwo.ok()) {
+ sync_wait(fenceTwo.get(), -1);
}
+
// Only cleanup the first time.
EXPECT_FALSE(mRE->canSkipPostRenderCleanup());
mRE->cleanupPostRender();
@@ -1814,7 +2013,7 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1825,7 +2024,7 @@
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
redLayer.alpha = 1.0f;
- layers.push_back(&redLayer);
+ layers.push_back(redLayer);
// Green layer with 1/3 size.
renderengine::LayerSettings greenLayer;
@@ -1840,7 +2039,7 @@
greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
greenLayer.alpha = 1.0f;
- layers.push_back(&greenLayer);
+ layers.push_back(greenLayer);
invokeDraw(settings, layers);
@@ -1863,7 +2062,7 @@
settings.clip = fullscreenRect();
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
renderengine::LayerSettings redLayer;
redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1874,7 +2073,7 @@
redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
redLayer.alpha = 1.0f;
- layers.push_back(&redLayer);
+ layers.push_back(redLayer);
// Green layer with 1/2 size with parent crop rect.
renderengine::LayerSettings greenLayer = redLayer;
@@ -1882,7 +2081,7 @@
FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2);
greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f);
- layers.push_back(&greenLayer);
+ layers.push_back(greenLayer);
invokeDraw(settings, layers);
@@ -1900,6 +2099,40 @@
expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255);
}
+TEST_P(RenderEngineTest, testRoundedCornersParentCropSmallBounds) {
+ initializeRenderEngine();
+
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings redLayer;
+ redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+ redLayer.geometry.boundaries = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 32);
+ redLayer.geometry.roundedCornersRadius = 64;
+ redLayer.geometry.roundedCornersCrop = FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, 128);
+ // Red background.
+ redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
+ redLayer.alpha = 1.0f;
+
+ layers.push_back(redLayer);
+ invokeDraw(settings, layers);
+
+ // Due to roundedCornersRadius, the top corners are untouched.
+ expectBufferColor(Point(0, 0), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0);
+
+ // ensure that the entire height of the red layer was clipped by the rounded corners crop.
+ expectBufferColor(Point(0, 31), 0, 0, 0, 0);
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 31), 0, 0, 0, 0);
+
+ // the bottom middle should be red
+ expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 31), 255, 0, 0, 255);
+}
+
TEST_P(RenderEngineTest, testClear) {
initializeRenderEngine();
@@ -1924,7 +2157,7 @@
.disableBlending = true,
};
- std::vector<const renderengine::LayerSettings*> layers{&redLayer, &clearLayer};
+ std::vector<renderengine::LayerSettings> layers{redLayer, clearLayer};
invokeDraw(display, layers);
expectBufferColor(rect, 0, 0, 0, 0);
}
@@ -1972,11 +2205,138 @@
.disableBlending = true,
};
- std::vector<const renderengine::LayerSettings*> layers{&redLayer, &greenLayer};
+ std::vector<renderengine::LayerSettings> layers{redLayer, greenLayer};
invokeDraw(display, layers);
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, testDimming) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+ initializeRenderEngine();
+
+ const auto displayRect = Rect(3, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 1000.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+ const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f / 51.f,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ // When the white point is not set for a layer, just ignore it and treat it as the same
+ // as the max layer
+ .whitePointNits = -1.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
+ expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
+}
+
+TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+ initializeRenderEngine();
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ const auto displayRect = Rect(2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = -1.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
+}
+
TEST_P(RenderEngineTest, test_isOpaque) {
initializeRenderEngine();
@@ -2018,7 +2378,7 @@
.alpha = 1.0f,
};
- std::vector<const renderengine::LayerSettings*> layers{&greenLayer};
+ std::vector<renderengine::LayerSettings> layers{greenLayer};
invokeDraw(display, layers);
if (GetParam()->useColorManagement()) {
@@ -2027,6 +2387,155 @@
expectBufferColor(rect, 0, 255, 0, 255);
}
}
+
+double EOTF_PQ(double channel) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2);
+ tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return std::pow(tmp, 1.0 / m1);
+}
+
+vec3 EOTF_PQ(vec3 color) {
+ return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b));
+}
+
+double OETF_sRGB(double channel) {
+ return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055;
+}
+
+int sign(float in) {
+ return in >= 0.0 ? 1 : -1;
+}
+
+vec3 OETF_sRGB(vec3 linear) {
+ return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g),
+ sign(linear.b) * OETF_sRGB(linear.b));
+}
+
+TEST_P(RenderEngineTest, test_tonemapPQMatches) {
+ if (!GetParam()->useColorManagement()) {
+ return;
+ }
+
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+
+ constexpr int32_t kGreyLevels = 256;
+
+ const auto rect = Rect(0, 0, kGreyLevels, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .maxLuminance = 750.0f,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ auto buf = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, buf->getBuffer()->initCheck());
+
+ {
+ uint8_t* pixels;
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ uint8_t color = 0;
+ for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+ uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4);
+ for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
+ dest[0] = color;
+ dest[1] = color;
+ dest[2] = color;
+ dest[3] = 255;
+ color++;
+ dest += 4;
+ }
+ }
+ buf->getBuffer()->unlock();
+ }
+
+ mBuffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
+
+ const renderengine::LayerSettings layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = std::move(buf),
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ };
+
+ std::vector<renderengine::LayerSettings> layers{layer};
+ invokeDraw(display, layers);
+
+ ColorSpace displayP3 = ColorSpace::DisplayP3();
+ ColorSpace bt2020 = ColorSpace::BT2020();
+
+ tonemap::Metadata metadata{.displayMaxLuminance = 750.0f};
+
+ auto generator = [=](Point location) {
+ const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1);
+ const vec3 rgb = vec3(normColor, normColor, normColor);
+
+ const vec3 linearRGB = EOTF_PQ(rgb);
+
+ static constexpr float kMaxPQLuminance = 10000.f;
+ const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance;
+ const double gain =
+ tonemap::getToneMapper()
+ ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ ui::Dataspace::DISPLAY_P3),
+ linearRGB * 10000.0, xyz, metadata);
+ const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance;
+
+ const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255;
+ return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g),
+ static_cast<uint8_t>(targetRGB.b), 255);
+ };
+
+ expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
+}
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index 830f463..db7e12b 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -169,25 +169,32 @@
status_t result = mThreadedRE->supportsBackgroundBlur();
ASSERT_EQ(true, result);
}
+
TEST_F(RenderEngineThreadedTest, drawLayers) {
renderengine::DisplaySettings settings;
- std::vector<const renderengine::LayerSettings*> layers;
+ std::vector<renderengine::LayerSettings> layers;
std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared<
renderengine::ExternalTexture>(new GraphicBuffer(), *mRenderEngine,
renderengine::ExternalTexture::Usage::READABLE |
renderengine::ExternalTexture::Usage::WRITEABLE);
+
base::unique_fd bufferFence;
- base::unique_fd drawFence;
- EXPECT_CALL(*mRenderEngine, drawLayers)
- .WillOnce([](const renderengine::DisplaySettings&,
- const std::vector<const renderengine::LayerSettings*>&,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> status_t { return NO_ERROR; });
+ EXPECT_CALL(*mRenderEngine, drawLayersInternal)
+ .WillOnce([&](const std::shared_ptr<std::promise<renderengine::RenderEngineResult>>&&
+ resultPromise,
+ const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> void {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ });
- status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false,
- std::move(bufferFence), &drawFence);
- ASSERT_EQ(NO_ERROR, result);
+ std::future<renderengine::RenderEngineResult> result =
+ mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence));
+ ASSERT_TRUE(result.valid());
+ auto [status, _] = result.get();
+ ASSERT_EQ(NO_ERROR, status);
}
} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 8e666d5..3d446e8 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -292,7 +292,7 @@
{
std::lock_guard lock(mThreadMutex);
mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::unmapExternalTextureBuffer");
+ ATRACE_NAME("REThreaded::cleanupPostRender");
instance.cleanupPostRender();
});
}
@@ -304,27 +304,34 @@
return mRenderEngine->canSkipPostRenderCleanup();
}
-status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache,
- base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) {
+void RenderEngineThreaded::drawLayersInternal(
+ const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
+ resultPromise->set_value({NO_ERROR, base::unique_fd()});
+ return;
+}
+
+std::future<RenderEngineResult> RenderEngineThreaded::drawLayers(
+ const DisplaySettings& display, const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache,
+ base::unique_fd&& bufferFence) {
ATRACE_CALL();
- std::promise<status_t> resultPromise;
- std::future<status_t> resultFuture = resultPromise.get_future();
+ const auto resultPromise = std::make_shared<std::promise<RenderEngineResult>>();
+ std::future<RenderEngineResult> resultFuture = resultPromise->get_future();
+ int fd = bufferFence.release();
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
- &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache,
+ fd](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::drawLayers");
- status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
- std::move(bufferFence), drawFence);
- resultPromise.set_value(status);
+ instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
+ useFramebufferCache, base::unique_fd(fd));
});
}
mCondition.notify_one();
- return resultFuture.get();
+ return resultFuture;
}
void RenderEngineThreaded::cleanFramebufferCache() {
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index b197df7..0159cfa 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -56,11 +56,11 @@
void useProtectedContext(bool useProtectedContext) override;
void cleanupPostRender() override;
- status_t drawLayers(const DisplaySettings& display,
- const std::vector<const LayerSettings*>& layers,
- const std::shared_ptr<ExternalTexture>& buffer,
- const bool useFramebufferCache, base::unique_fd&& bufferFence,
- base::unique_fd* drawFence) override;
+ std::future<RenderEngineResult> 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;
@@ -71,6 +71,11 @@
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override;
void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
bool canSkipPostRenderCleanup() const override;
+ void drawLayersInternal(const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
+ const DisplaySettings& display,
+ const std::vector<LayerSettings>& layers,
+ const std::shared_ptr<ExternalTexture>& buffer,
+ const bool useFramebufferCache, base::unique_fd&& bufferFence) override;
private:
void threadMain(CreateInstanceFactory factory);
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
new file mode 100644
index 0000000..390b228
--- /dev/null
+++ b/libs/shaders/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libshaders",
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common@1.2",
+ ],
+
+ static_libs: [
+ "libmath",
+ "libtonemap",
+ "libui-types",
+ ],
+
+ srcs: [
+ "shaders.cpp",
+ ],
+}
diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/shaders/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com
\ No newline at end of file
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
new file mode 100644
index 0000000..712a27a
--- /dev/null
+++ b/libs/shaders/include/shaders/shaders.h
@@ -0,0 +1,105 @@
+/*
+ * 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 <math/mat4.h>
+#include <tonemap/tonemap.h>
+#include <ui/GraphicTypes.h>
+#include <cstddef>
+
+namespace android::shaders {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+ // Input dataspace of the source colors.
+ const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+ // Working dataspace for the output surface, for conversion from linear space.
+ const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+ // Sets whether alpha premultiplication must be undone.
+ // This is required if the source colors use premultiplied alpha and is not opaque.
+ const bool undoPremultipliedAlpha = false;
+
+ // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
+ // RGB. This is used when Skia is expected to color manage the input image based on the
+ // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
+ // destination color space as the working color space. RenderEngine deliberately sets the color
+ // space for input images and destination surfaces to be the same whenever LinearEffects are
+ // expected to be used so that color-management is controlled by RenderEngine, but other users
+ // of a LinearEffect may not be able to control the color space of the images and surfaces. So
+ // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
+ // dataspace for correct conversion to linear colors.
+ ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+};
+
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+ return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
+ lhs.fakeInputDataspace == rhs.fakeInputDataspace;
+}
+
+struct LinearEffectHasher {
+ // Inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ size_t operator()(const LinearEffect& le) const {
+ size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+ result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+ result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+ return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
+ }
+};
+
+// Generates a shader string that applies color transforms in linear space.
+// Typical use-cases supported:
+// 1. Apply tone-mapping
+// 2. Apply color transform matrices in linear space
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance);
+
+} // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
new file mode 100644
index 0000000..6019c4a
--- /dev/null
+++ b/libs/shaders/shaders.cpp
@@ -0,0 +1,497 @@
+/*
+ * 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.
+ */
+
+#include <shaders/shaders.h>
+
+#include <tonemap/tonemap.h>
+
+#include <optional>
+
+#include <math/mat4.h>
+#include <system/graphics-base-v1.0.h>
+#include <ui/ColorSpace.h>
+
+namespace android::shaders {
+
+static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
+ ui::Dataspace dataspace) {
+ return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
+}
+
+static void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 EOTF(float3 color) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / float3(m1));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float EOTF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ float3 EOTF(float3 color) {
+ return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 EOTF(float3 color) {
+ return color;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.2);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.6);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.8);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateXYZTransforms(std::string& shader) {
+ shader.append(R"(
+ uniform float4x4 in_rgbToXyz;
+ uniform float4x4 in_xyzToRgb;
+ float3 ToXYZ(float3 rgb) {
+ return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
+ }
+
+ float3 ToRGB(float3 xyz) {
+ return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ }
+ )");
+}
+
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace, std::string& shader) {
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 1000.0 * pow(xyz.y, 0.2);
+ }
+ )");
+ break;
+ default:
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // SDR -> HDR tonemap
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_inputMaxLuminance;
+ }
+ )");
+ break;
+ default:
+ // Input and output are both SDR, so no tone-mapping is expected so
+ // no-op the luminance normalization.
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+ }
+}
+
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+ std::string& shader) {
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ std::string& shader) {
+ shader.append(tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
+ toAidlDataspace(outputDataspace))
+ .c_str());
+
+ generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+
+ shader.append(R"(
+ float3 OOTF(float3 linearRGB, float3 xyz) {
+ float3 scaledLinearRGB = ScaleLuminance(linearRGB);
+ float3 scaledXYZ = ScaleLuminance(xyz);
+
+ float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
+
+ return NormalizeLuminance(scaledXYZ * gain);
+ }
+ )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 OETF(float3 xyz) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(xyz, float3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float3(m2));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float OETF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ float3 OETF(float3 linear) {
+ return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+ OETF_channel(linear.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 OETF(float3 linear) {
+ return linear;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.018 ?
+ linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.2));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.6));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.8));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb / (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ float3 linearRGB = EOTF(c.rgb);
+ float3 xyz = ToXYZ(linearRGB);
+ c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb * (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ return c;
+ }
+ )");
+}
+
+// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ // TODO(b/208290320): BT601 format and variants return different primaries
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ // TODO(b/208290329): BT407M format returns different primaries
+ case HAL_DATASPACE_STANDARD_BT470M:
+ // TODO(b/208290904): FILM format returns different primaries
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
+ std::string shaderString;
+ generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
+ ? linearEffect.inputDataspace
+ : linearEffect.fakeInputDataspace,
+ shaderString);
+ generateXYZTransforms(shaderString);
+ generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+ generateOETF(linearEffect.outputDataspace, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ return shaderString;
+}
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance) {
+ std::vector<tonemap::ShaderUniform> uniforms;
+ if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+ uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
+ uniforms.push_back(
+ {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
+ } else {
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ uniforms.push_back({.name = "in_rgbToXyz",
+ .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
+ uniforms.push_back({.name = "in_xyzToRgb",
+ .value = buildUniformValue<mat4>(
+ colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
+ }
+
+ tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
+ // If the input luminance is unknown, use display luminance (aka,
+ // no-op any luminance changes)
+ // This will be the case for eg screenshots in addition to
+ // uncalibrated displays
+ .contentMaxLuminance =
+ maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};
+
+ for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
+ uniforms.push_back(uniform);
+ }
+
+ return uniforms;
+}
+
+} // namespace android::shaders
\ No newline at end of file
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
new file mode 100644
index 0000000..5360fe2
--- /dev/null
+++ b/libs/tonemap/Android.bp
@@ -0,0 +1,43 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libtonemap",
+ vendor_available: true,
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libmath",
+ ],
+
+ srcs: [
+ "tonemap.cpp",
+ ],
+}
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/tonemap/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com
\ No newline at end of file
diff --git a/libs/tonemap/TEST_MAPPING b/libs/tonemap/TEST_MAPPING
new file mode 100644
index 0000000..00f83ba
--- /dev/null
+++ b/libs/tonemap/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ },
+ {
+ "name": "libtonemap_test"
+ }
+ ]
+}
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
new file mode 100644
index 0000000..bd7b72d
--- /dev/null
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <math/vec3.h>
+
+#include <string>
+#include <vector>
+
+namespace android::tonemap {
+
+// Describes a shader uniform
+// The shader uniform is intended to be passed into a SkRuntimeShaderBuilder, i.e.:
+//
+// SkRuntimeShaderBuilder builder;
+// builder.uniform(<uniform name>).set(<uniform value>.data(), <uniform value>.size());
+struct ShaderUniform {
+ // The name of the uniform, used for binding into a shader.
+ // The shader must contain a uniform whose name matches this.
+ std::string name;
+
+ // The value for the uniform, which should be bound to the uniform identified by <name>
+ std::vector<uint8_t> value;
+};
+
+// Describes metadata which may be used for constructing the shader uniforms.
+// This metadata should not be used for manipulating the source code of the shader program directly,
+// as otherwise caching by other system of these shaders may break.
+struct Metadata {
+ float displayMaxLuminance = 0.0;
+ float contentMaxLuminance = 0.0;
+};
+
+class ToneMapper {
+public:
+ virtual ~ToneMapper() {}
+ // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an
+ // input whose dataspace is described by sourceDataspace, to an output whose dataspace
+ // is described by destinationDataspace
+ //
+ // The returned shader string *must* contain a function with the following signature:
+ // float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz);
+ //
+ // The arguments are:
+ // * linearRGB is the absolute nits of the RGB pixels in linear space
+ // * xyz is linearRGB converted into XYZ
+ //
+ // libtonemap_LookupTonemapGain() returns a float representing the amount by which to scale the
+ // absolute nits of the pixels. This function may be plugged into any existing SkSL shader, and
+ // is expected to look something like this:
+ //
+ // vec3 rgb = ...;
+ // // apply the EOTF based on the incoming dataspace to convert to linear nits.
+ // vec3 linearRGB = applyEOTF(rgb);
+ // // apply a RGB->XYZ matrix float3
+ // vec3 xyz = toXYZ(linearRGB);
+ // // Scale the luminance based on the content standard
+ // vec3 absoluteRGB = ScaleLuminance(linearRGB);
+ // vec3 absoluteXYZ = ScaleLuminance(xyz);
+ // float gain = libtonemap_LookupTonemapGain(absoluteRGB, absoluteXYZ);
+ // // Normalize the luminance back down to a [0, 1] range
+ // xyz = NormalizeLuminance(absoluteXYZ * gain);
+ // // apply a XYZ->RGB matrix and apply the output OETf.
+ // vec3 finalColor = applyOETF(ToRGB(xyz));
+ // ...
+ //
+ // Helper methods in this shader should be prefixed with "libtonemap_". Accordingly, libraries
+ // which consume this shader must *not* contain any methods prefixed with "libtonemap_" to
+ // guarantee that there are no conflicts in name resolution.
+ virtual std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) = 0;
+
+ // Constructs uniform descriptions that correspond to those that are generated for the tonemap
+ // shader. Uniforms must be prefixed with "in_libtonemap_". Libraries which consume this shader
+ // must not bind any new uniforms that begin with this prefix.
+ //
+ // Downstream shaders may assume the existence of the uniform in_libtonemap_displayMaxLuminance
+ // and in_libtonemap_inputMaxLuminance, in order to assist with scaling and normalizing
+ // luminance as described in the documentation for generateTonemapGainShaderSkSL(). That is,
+ // shaders plugging in a tone-mapping shader returned by generateTonemapGainShaderSkSL() may
+ // assume that there are predefined floats in_libtonemap_displayMaxLuminance and
+ // in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader.
+ virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0;
+
+ // CPU implementation of the tonemapping gain. This must match the GPU implementation returned
+ // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for
+ // differences in hardware precision.
+ //
+ // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output
+ // described by destinationDataspace. To compute the gain, the input colors are provided by
+ // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also
+ // provided. Metadata is also provided for helping to compute the tonemapping curve.
+ virtual double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0;
+};
+
+// Retrieves a tonemapper instance.
+// This instance is globally constructed.
+ToneMapper* getToneMapper();
+
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
new file mode 100644
index 0000000..f46f3fa
--- /dev/null
+++ b/libs/tonemap/tests/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "libtonemap_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "tonemap_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ ],
+ static_libs: [
+ "libmath",
+ "libgmock",
+ "libgtest",
+ "libtonemap",
+ ],
+}
diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp
new file mode 100644
index 0000000..7a7958f
--- /dev/null
+++ b/libs/tonemap/tests/tonemap_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <tonemap/tonemap.h>
+#include <cmath>
+
+namespace android {
+
+using testing::HasSubstr;
+
+struct TonemapTest : public ::testing::Test {};
+
+TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) {
+ static const constexpr float kDisplayMaxLuminance = 1.f;
+ static const constexpr float kContentMaxLuminance = 2.f;
+ tonemap::Metadata metadata{.displayMaxLuminance = kDisplayMaxLuminance,
+ .contentMaxLuminance = kContentMaxLuminance};
+ const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);
+
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ }));
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ }));
+
+ // Smoke check that metadata values are "real", specifically that they're non-zero and actually
+ // numbers. This is to help avoid shaders using these uniforms from dividing by zero or other
+ // catastrophic errors.
+ const auto& displayLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ })->value;
+
+ float displayLumFloat = 0.f;
+ std::memcpy(&displayLumFloat, displayLum.data(), displayLum.size());
+ EXPECT_FALSE(std::isnan(displayLumFloat));
+ EXPECT_GT(displayLumFloat, 0);
+
+ const auto& contentLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ })->value;
+
+ float contentLumFloat = 0.f;
+ std::memcpy(&contentLumFloat, contentLum.data(), contentLum.size());
+ EXPECT_FALSE(std::isnan(contentLumFloat));
+ EXPECT_GT(contentLumFloat, 0);
+}
+
+TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) {
+ const auto shader =
+ tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common::
+ Dataspace::BT2020_ITU_PQ,
+ aidl::android::hardware::graphics::common::
+ Dataspace::DISPLAY_P3);
+
+ // Other tests such as librenderengine_test will plug in the shader to check compilation.
+ EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)"));
+}
+
+} // namespace android
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
new file mode 100644
index 0000000..c2372fe
--- /dev/null
+++ b/libs/tonemap/tonemap.cpp
@@ -0,0 +1,666 @@
+/*
+ * 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.
+ */
+
+#include <tonemap/tonemap.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <mutex>
+#include <type_traits>
+
+namespace android::tonemap {
+
+namespace {
+
+// Flag containing the variant of tone map algorithm to use.
+enum class ToneMapAlgorithm {
+ AndroidO, // Default algorithm in place since Android O,
+ Android13, // Algorithm used in Android 13.
+};
+
+static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13;
+
+static const constexpr auto kTransferMask =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK);
+static const constexpr auto kTransferST2084 =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084);
+static const constexpr auto kTransferHLG =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG);
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+class ToneMapperO : public ToneMapper {
+public:
+ std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Define required uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return xyz.y;
+ }
+ )");
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return clamp(xyz.y, 0.0, 1000.0);
+ }
+ )");
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = xyz.y;
+
+ // if the max input luminance is less than what we can
+ // output then no tone mapping is needed as all color
+ // values will be in range.
+ if (maxInLumi <= maxOutLumi) {
+ return xyz.y;
+ } 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 nits * 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;
+ }
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = in_libtonemap_displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = in_libtonemap_displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = in_libtonemap_displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = xyz.y;
+
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return nits * 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;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return xyz.y;
+ }
+ )");
+ break;
+ }
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(xyz) / xyz.y;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ std::vector<ShaderUniform> uniforms;
+
+ uniforms.reserve(2);
+
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .value = buildUniformValue<float>(metadata.contentMaxLuminance)});
+ return uniforms;
+ }
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = xyz.y;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ targetNits = xyz.y;
+ // if the max input luminance is less than what we can output then
+ // no tone mapping is needed as all color values will be in range.
+ if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
+ // three control points
+ const double x0 = 10.0;
+ const double y0 = 17.0;
+ double x1 = metadata.displayMaxLuminance * 0.75;
+ double y1 = x1;
+ double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
+ double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ double h12 = x2 - x1;
+ double h23 = metadata.contentMaxLuminance - x2;
+ // tangents at the last three control points
+ double m1 = (y2 - y1) / h12;
+ double m3 = (metadata.displayMaxLuminance - y2) / h23;
+ double m2 = (m1 + m3) / 2.0;
+
+ if (targetNits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ double slope = (y1 - y0) / (x1 - x0);
+ targetNits = y0 + (targetNits - x0) * slope;
+ } else if (targetNits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ double t = (targetNits - x1) / h12;
+ targetNits = (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
+ double t = (targetNits - x2) / h23;
+ targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
+ (1.0 - t) +
+ (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
+ h23 * m3 * (t - 1.0)) *
+ t * t;
+ }
+ }
+ break;
+ }
+ break;
+ default:
+ // source is SDR
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG: {
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ const double maxOutLumi = 3000.0;
+
+ double x0 = 5.0;
+ double y0 = 2.5;
+ double x1 = metadata.displayMaxLuminance * 0.7;
+ double y1 = maxOutLumi * 0.15;
+ double x2 = metadata.displayMaxLuminance * 0.9;
+ double y2 = maxOutLumi * 0.45;
+ double x3 = metadata.displayMaxLuminance;
+ double y3 = maxOutLumi;
+
+ double c1 = y1 / 3.0;
+ double c2 = y2 / 2.0;
+ double c3 = y3 / 1.5;
+
+ targetNits = xyz.y;
+
+ if (targetNits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ double t = (targetNits - x0) / (x1 - x0);
+ targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
+ t * t * y1;
+ } else if (targetNits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ double t = (targetNits - x1) / (x2 - x1);
+ targetNits = (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
+ double t = (targetNits - x2) / (x3 - x2);
+ targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
+ t * t * y3;
+ }
+ } break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ targetNits = xyz.y;
+ break;
+ }
+ }
+
+ return targetNits / xyz.y;
+ }
+};
+
+class ToneMapper13 : public ToneMapper {
+private:
+ double OETF_ST2084(double nits) {
+ nits = nits / 10000.0;
+ double m1 = (2610.0 / 4096.0) / 4.0;
+ double m2 = (2523.0 / 4096.0) * 128.0;
+ double c1 = (3424.0 / 4096.0);
+ double c2 = (2413.0 / 4096.0) * 32.0;
+ double c3 = (2392.0 / 4096.0) * 32.0;
+
+ double tmp = std::pow(nits, m1);
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return std::pow(tmp, m2);
+ }
+
+ double OETF_HLG(double nits) {
+ nits = nits / 1000.0;
+ const double a = 0.17883277;
+ const double b = 0.28466892;
+ const double c = 0.55991073;
+ return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c;
+ }
+
+public:
+ std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Input uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return maxRGB;
+ }
+ )");
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return clamp(maxRGB, 0.0, 1000.0);
+ }
+ )");
+ break;
+
+ default:
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 10000.0;
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float tmp = pow(channel, float(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float(m2));
+ }
+ )");
+ break;
+ case kTransferHLG:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 1000.0;
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+ )");
+ break;
+ }
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = maxRGB;
+
+ float x1 = maxOutLumi * 0.65;
+ float y1 = x1;
+
+ float x3 = maxInLumi;
+ float y3 = maxOutLumi;
+
+ float x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ float y2 = maxOutLumi * 0.9;
+
+ float greyNorm1 = libtonemap_OETFTone(x1);
+ float greyNorm2 = libtonemap_OETFTone(x2);
+ float greyNorm3 = libtonemap_OETFTone(x3);
+
+ float slope1 = 0;
+ float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2);
+
+ if (nits < x1) {
+ return nits;
+ }
+
+ if (nits > maxInLumi) {
+ return maxOutLumi;
+ }
+
+ float greyNits = libtonemap_OETFTone(nits);
+
+ if (greyNits <= greyNorm2) {
+ nits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ nits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ nits = maxOutLumi;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ // Inverse tone-mapping and SDR-SDR mapping is not supported.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return maxRGB;
+ }
+ )");
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b));
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ // Hardcode the max content luminance to a "reasonable" level
+ static const constexpr float kContentMaxLuminance = 4000.f;
+ std::vector<ShaderUniform> uniforms;
+ uniforms.reserve(2);
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .value = buildUniformValue<float>(kContentMaxLuminance)});
+ return uniforms;
+ }
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override {
+ double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
+
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = maxRGB;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(maxRGB, 0.0, 1000.0);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ double maxInLumi = 4000;
+ double maxOutLumi = metadata.displayMaxLuminance;
+
+ targetNits = maxRGB;
+
+ double x1 = maxOutLumi * 0.65;
+ double y1 = x1;
+
+ double x3 = maxInLumi;
+ double y3 = maxOutLumi;
+
+ double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ double y2 = maxOutLumi * 0.9;
+
+ double greyNorm1 = 0.0;
+ double greyNorm2 = 0.0;
+ double greyNorm3 = 0.0;
+
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNorm1 = OETF_ST2084(x1);
+ greyNorm2 = OETF_ST2084(x2);
+ greyNorm3 = OETF_ST2084(x3);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNorm1 = OETF_HLG(x1);
+ greyNorm2 = OETF_HLG(x2);
+ greyNorm3 = OETF_HLG(x3);
+ }
+
+ double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
+
+ if (targetNits < x1) {
+ break;
+ }
+
+ if (targetNits > maxInLumi) {
+ targetNits = maxOutLumi;
+ break;
+ }
+
+ double greyNits = 0.0;
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNits = OETF_ST2084(targetNits);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNits = OETF_HLG(targetNits);
+ }
+
+ if (greyNits <= greyNorm2) {
+ targetNits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ targetNits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ targetNits = maxOutLumi;
+ }
+ break;
+ }
+ break;
+ default:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ default:
+ targetNits = maxRGB;
+ break;
+ }
+ break;
+ }
+
+ return targetNits / maxRGB;
+ }
+};
+
+} // namespace
+
+ToneMapper* getToneMapper() {
+ static std::once_flag sOnce;
+ static std::unique_ptr<ToneMapper> sToneMapper;
+
+ std::call_once(sOnce, [&] {
+ switch (kToneMapAlgorithm) {
+ case ToneMapAlgorithm::AndroidO:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO());
+ break;
+ case ToneMapAlgorithm::Android13:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13());
+ }
+ });
+
+ return sToneMapper.get();
+}
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d4d0ee4..a7b5900 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -86,6 +86,7 @@
export_include_dirs: [
"include",
+ "include_mock",
"include_private",
"include_types",
],
@@ -137,7 +138,6 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
- "Size.cpp",
"StaticDisplayInfo.cpp",
],
@@ -159,7 +159,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -176,7 +176,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 1f006ce..073da89 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -302,6 +302,8 @@
return std::string("RGB_565");
case android::PIXEL_FORMAT_BGRA_8888:
return std::string("BGRA_8888");
+ case android::PIXEL_FORMAT_R_8:
+ return std::string("R_8");
default:
return StringPrintf("Unknown %#08x", format);
}
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 80f6c82..8ac08fb 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -634,6 +634,12 @@
outSmpte2094_40);
}
+status_t Gralloc4Mapper::getSmpte2094_10(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2094_10, gralloc4::decodeSmpte2094_10,
+ outSmpte2094_10);
+}
+
template <class T>
status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index d20bd7a..82d6cd5 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -301,6 +301,11 @@
return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
}
+status_t GraphicBufferMapper::getSmpte2094_10(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_10) {
+ return mMapper->getSmpte2094_10(bufferHandle, outSmpte2094_10);
+}
+
status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount,
uint64_t usage,
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index e88fdd5..799fbc9 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -35,25 +35,8 @@
case PIXEL_FORMAT_RGBA_5551:
case PIXEL_FORMAT_RGBA_4444:
return 2;
- }
- return 0;
-}
-
-uint32_t bitsPerPixel(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_FP16:
- return 64;
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_BGRA_8888:
- case PIXEL_FORMAT_RGBA_1010102:
- return 32;
- case PIXEL_FORMAT_RGB_888:
- return 24;
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_RGBA_5551:
- case PIXEL_FORMAT_RGBA_4444:
- return 16;
+ case PIXEL_FORMAT_R_8:
+ return 1;
}
return 0;
}
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index cd68c1c..b34d906 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -396,6 +396,11 @@
result.mMatrix[1][0] = -b*idet;
result.mMatrix[1][1] = a*idet;
result.mType = mType;
+ if (getOrientation() & ROT_90) {
+ // Recalculate the type if there is a 90-degree rotation component, since the inverse
+ // of ROT_90 is ROT_270 and vice versa.
+ result.mType |= UNKNOWN_TYPE;
+ }
vec2 T(-x, -y);
T = result.transform(T);
diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index f196ab9..9120972 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -38,12 +38,22 @@
uint64_t value;
+ // For deserialization.
+ static constexpr std::optional<DisplayId> fromValue(uint64_t);
+
+ // As above, but also upcast to Id.
+ template <typename Id>
+ static constexpr std::optional<Id> fromValue(uint64_t value) {
+ if (const auto id = Id::tryCast(DisplayId(value))) {
+ return id;
+ }
+ return {};
+ }
+
protected:
explicit constexpr DisplayId(uint64_t id) : value(id) {}
};
-static_assert(sizeof(DisplayId) == sizeof(uint64_t));
-
inline bool operator==(DisplayId lhs, DisplayId rhs) {
return lhs.value == rhs.value;
}
@@ -80,11 +90,8 @@
// TODO(b/162612135) Remove default constructor
PhysicalDisplayId() = default;
- // TODO(b/162612135) Remove constructor
- explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {}
constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); }
-
constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); }
private:
@@ -96,10 +103,9 @@
explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {}
};
-static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
-
struct VirtualDisplayId : DisplayId {
using BaseId = uint32_t;
+
// Flag indicating that this virtual display is backed by the GPU.
static constexpr uint64_t FLAG_GPU = 1ULL << 61;
@@ -163,10 +169,23 @@
explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {}
};
+constexpr std::optional<DisplayId> DisplayId::fromValue(uint64_t value) {
+ if (const auto id = fromValue<PhysicalDisplayId>(value)) {
+ return id;
+ }
+ if (const auto id = fromValue<VirtualDisplayId>(value)) {
+ return id;
+ }
+ return {};
+}
+
+static_assert(sizeof(DisplayId) == sizeof(uint64_t));
+static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t));
+
+static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t));
static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t));
static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t));
-static_assert(sizeof(HalDisplayId) == sizeof(uint64_t));
} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
index 70a0d50..98ee356 100644
--- a/libs/ui/include/ui/DisplayState.h
+++ b/libs/ui/include/ui/DisplayState.h
@@ -16,21 +16,18 @@
#pragma once
+#include <ui/LayerStack.h>
#include <ui/Rotation.h>
#include <ui/Size.h>
-#include <cstdint>
#include <type_traits>
namespace android::ui {
-using LayerStack = uint32_t;
-constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
-
// Transactional state of physical or virtual display. Note that libgui defines
// android::DisplayState as a superset of android::ui::DisplayState.
struct DisplayState {
- LayerStack layerStack = NO_LAYER_STACK;
+ LayerStack layerStack;
Rotation orientation = ROTATION_0;
Size layerStackSpaceRect;
};
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 6efecd3..9aae145 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -26,6 +26,10 @@
namespace android {
+namespace mock {
+class MockFence;
+}
+
class String8;
// ===========================================================================
@@ -109,7 +113,7 @@
// fence transitioned to the signaled state. If the fence is not signaled
// then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
// error occurs then SIGNAL_TIME_INVALID is returned.
- nsecs_t getSignalTime() const;
+ virtual nsecs_t getSignalTime() const;
enum class Status {
Invalid, // Fence is invalid
@@ -120,7 +124,7 @@
// getStatus() returns whether the fence has signaled yet. Prefer this to
// getSignalTime() or wait() if all you care about is whether the fence has
// signaled.
- inline Status getStatus() {
+ virtual inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
// getSignalTime(), which might otherwise appear to be the more obvious
@@ -144,7 +148,10 @@
private:
// Only allow instantiation using ref counting.
friend class LightRefBase<Fence>;
- ~Fence() = default;
+ virtual ~Fence() = default;
+
+ // Allow mocking for unit testing
+ friend class mock::MockFence;
base::unique_fd mFenceFd;
};
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index e199648..753b0a6 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -178,6 +178,11 @@
std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
return INVALID_OPERATION;
}
+ virtual status_t getSmpte2094_10(
+ buffer_handle_t /*bufferHandle*/,
+ std::optional<std::vector<uint8_t>>* /*outSmpte2094_10*/) const {
+ return INVALID_OPERATION;
+ }
virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
PixelFormat /*format*/, uint32_t /*layerCount*/,
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 4729cba..62f9e4a 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -108,6 +108,8 @@
std::optional<ui::Cta861_3>* outCta861_3) const override;
status_t getSmpte2094_40(buffer_handle_t bufferHandle,
std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+ status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10) const override;
status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 837e3d8..257c155 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -126,6 +126,8 @@
status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
status_t getSmpte2094_40(buffer_handle_t bufferHandle,
std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+ status_t getSmpte2094_10(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_10);
/**
* Gets the default metadata for a gralloc buffer allocated with the given parameters.
diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h
new file mode 100644
index 0000000..d6ffeb7
--- /dev/null
+++ b/libs/ui/include/ui/LayerStack.h
@@ -0,0 +1,78 @@
+/*
+ * 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 <cstdint>
+
+#include <ftl/cast.h>
+#include <ftl/string.h>
+#include <log/log.h>
+
+namespace android::ui {
+
+// A LayerStack identifies a Z-ordered group of layers. A layer can only be associated to a single
+// LayerStack, but a LayerStack can be associated to multiple displays, mirroring the same content.
+struct LayerStack {
+ uint32_t id = UINT32_MAX;
+
+ template <typename T>
+ static constexpr LayerStack fromValue(T v) {
+ if (ftl::cast_safety<uint32_t>(v) == ftl::CastSafety::kSafe) {
+ return {static_cast<uint32_t>(v)};
+ }
+
+ ALOGW("Invalid layer stack %s", ftl::to_string(v).c_str());
+ return {};
+ }
+};
+
+constexpr LayerStack INVALID_LAYER_STACK;
+constexpr LayerStack DEFAULT_LAYER_STACK{0u};
+
+inline bool operator==(LayerStack lhs, LayerStack rhs) {
+ return lhs.id == rhs.id;
+}
+
+inline bool operator!=(LayerStack lhs, LayerStack rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator>(LayerStack lhs, LayerStack rhs) {
+ return lhs.id > rhs.id;
+}
+
+// A LayerFilter determines if a layer is included for output to a display.
+struct LayerFilter {
+ LayerStack layerStack;
+
+ // True if the layer is only output to internal displays, i.e. excluded from screenshots, screen
+ // recordings, and mirroring to virtual or external displays. Used for display cutout overlays.
+ bool toInternalDisplay = false;
+
+ // Returns true if the input filter can be output to this filter.
+ bool includes(LayerFilter other) const {
+ // The layer stacks must match.
+ if (other.layerStack == INVALID_LAYER_STACK || other.layerStack != layerStack) {
+ return false;
+ }
+
+ // The output must be to an internal display if the input filter has that constraint.
+ return !other.toInternalDisplay || toInternalDisplay;
+ }
+};
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index 02773d9..f422ce4 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -62,12 +62,12 @@
PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+ PIXEL_FORMAT_R_8 = 0x38,
};
typedef int32_t PixelFormat;
uint32_t bytesPerPixel(PixelFormat format);
-uint32_t bitsPerPixel(PixelFormat format);
}; // namespace android
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index f1e8252..ecc192d 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -23,103 +23,70 @@
#include <type_traits>
#include <utility>
-namespace android {
-namespace ui {
+namespace android::ui {
-// Forward declare a few things.
-struct Size;
-bool operator==(const Size& lhs, const Size& rhs);
-
-/**
- * A simple value type representing a two-dimensional size
- */
+// A simple value type representing a two-dimensional size.
struct Size {
- int32_t width;
- int32_t height;
+ int32_t width = -1;
+ int32_t height = -1;
- // Special values
- static const Size INVALID;
- static const Size EMPTY;
+ constexpr Size() = default;
- // ------------------------------------------------------------------------
- // Construction
- // ------------------------------------------------------------------------
-
- Size() : Size(INVALID) {}
template <typename T>
- Size(T&& w, T&& h)
- : width(Size::clamp<int32_t, T>(std::forward<T>(w))),
- height(Size::clamp<int32_t, T>(std::forward<T>(h))) {}
-
- // ------------------------------------------------------------------------
- // Accessors
- // ------------------------------------------------------------------------
+ constexpr Size(T w, T h) : width(clamp<int32_t>(w)), height(clamp<int32_t>(h)) {}
int32_t getWidth() const { return width; }
int32_t getHeight() const { return height; }
- template <typename T>
- void setWidth(T&& v) {
- width = Size::clamp<int32_t, T>(std::forward<T>(v));
- }
- template <typename T>
- void setHeight(T&& v) {
- height = Size::clamp<int32_t, T>(std::forward<T>(v));
- }
-
- // ------------------------------------------------------------------------
- // Assignment
- // ------------------------------------------------------------------------
-
- void set(const Size& size) { *this = size; }
- template <typename T>
- void set(T&& w, T&& h) {
- set(Size(std::forward<T>(w), std::forward<T>(h)));
- }
-
- // Sets the value to INVALID
- void makeInvalid() { set(INVALID); }
-
- // Sets the value to EMPTY
- void clear() { set(EMPTY); }
-
- // ------------------------------------------------------------------------
- // Semantic checks
- // ------------------------------------------------------------------------
-
// Valid means non-negative width and height
bool isValid() const { return width >= 0 && height >= 0; }
// Empty means zero width and height
- bool isEmpty() const { return *this == EMPTY; }
+ bool isEmpty() const;
- // ------------------------------------------------------------------------
- // Clamp Helpers
- // ------------------------------------------------------------------------
-
- // Note: We use only features available in C++11 here for compatibility with
- // external targets which include this file directly or indirectly and which
- // themselves use C++11.
-
- // C++11 compatible replacement for std::remove_cv_reference_t [C++20]
template <typename T>
- using remove_cv_reference_t =
- typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+ void setWidth(T v) {
+ width = clamp<int32_t>(v);
+ }
+
+ template <typename T>
+ void setHeight(T v) {
+ height = clamp<int32_t>(v);
+ }
+
+ void set(Size size) { *this = size; }
+
+ template <typename T>
+ void set(T w, T h) {
+ set(Size(w, h));
+ }
+
+ // Sets the value to kInvalidSize
+ void makeInvalid();
+
+ // Sets the value to kEmptySize
+ void clear();
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename T>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType>
- clamp(typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
- FromType>::type v) {
- using BareToType = remove_cv_reference_t<ToType>;
- using BareFromType = remove_cv_reference_t<FromType>;
- static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
- static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
- static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
- static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
+ static constexpr remove_cvref_t<ToType> clamp(FromType v) {
+ using BareToType = remove_cvref_t<ToType>;
+ using ToLimits = std::numeric_limits<BareToType>;
+
+ using BareFromType = remove_cvref_t<FromType>;
+ using FromLimits = std::numeric_limits<BareFromType>;
+
+ static_assert(ToLimits::is_specialized && FromLimits::is_specialized);
+
+ constexpr auto toHighest = ToLimits::max();
+ constexpr auto toLowest = ToLimits::lowest();
+ constexpr auto fromHighest = FromLimits::max();
+ constexpr auto fromLowest = FromLimits::lowest();
// Get the closest representation of [toLowest, toHighest] in type
// FromType to use to clamp the input value before conversion.
@@ -127,37 +94,35 @@
// std::common_type<...> is used to get a value-preserving type for the
// top end of the range.
using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+ using CommonLimits = std::numeric_limits<CommonHighestType>;
// std::make_signed<std::common_type<...>> is used to get a
// value-preserving type for the bottom end of the range, except this is
// a bit trickier for non-integer types like float.
- using CommonLowestType =
- std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
- std::make_signed_t<std::conditional_t<
- std::numeric_limits<CommonHighestType>::is_integer,
- CommonHighestType, int /* not used */>>,
- CommonHighestType>;
+ using CommonLowestType = std::conditional_t<
+ CommonLimits::is_integer,
+ std::make_signed_t<std::conditional_t<CommonLimits::is_integer, CommonHighestType,
+ int /* not used */>>,
+ CommonHighestType>;
// We can then compute the clamp range in a way that can be later
// trivially converted to either the 'from' or 'to' types, and be
- // representabile in either.
- static constexpr auto commonClampHighest =
- std::min(static_cast<CommonHighestType>(fromHighest),
- static_cast<CommonHighestType>(toHighest));
- static constexpr auto commonClampLowest =
- std::max(static_cast<CommonLowestType>(fromLowest),
- static_cast<CommonLowestType>(toLowest));
+ // representable in either.
+ constexpr auto commonClampHighest = std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ constexpr auto commonClampLowest = std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
- static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
- static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+ constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
// A clamp is needed only if the range we are clamping to is not the
// same as the range of the input.
- static constexpr bool isClampNeeded =
+ constexpr bool isClampNeeded =
(fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
- if (!isClampNeeded) {
+ if constexpr (!isClampNeeded) {
return static_cast<BareToType>(v);
}
@@ -170,34 +135,46 @@
// Otherwise clamping is done by using the already computed endpoints
// for each type.
- return (v <= fromClampLowest)
- ? toClampLowest
- : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
+ if (v <= fromClampLowest) {
+ return toClampLowest;
+ }
+
+ return v >= fromClampHighest ? toClampHighest : static_cast<BareToType>(v);
}
};
-// ------------------------------------------------------------------------
-// Comparisons
-// ------------------------------------------------------------------------
+constexpr Size kInvalidSize;
+constexpr Size kEmptySize{0, 0};
-inline bool operator==(const Size& lhs, const Size& rhs) {
+inline void Size::makeInvalid() {
+ set(kInvalidSize);
+}
+
+inline void Size::clear() {
+ set(kEmptySize);
+}
+
+inline bool operator==(Size lhs, Size rhs) {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
-inline bool operator!=(const Size& lhs, const Size& rhs) {
- return !operator==(lhs, rhs);
+inline bool Size::isEmpty() const {
+ return *this == kEmptySize;
}
-inline bool operator<(const Size& lhs, const Size& rhs) {
+inline bool operator!=(Size lhs, Size rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(Size lhs, Size rhs) {
// Orders by increasing width, then height.
if (lhs.width != rhs.width) return lhs.width < rhs.width;
return lhs.height < rhs.height;
}
// Defining PrintTo helps with Google Tests.
-static inline void PrintTo(const Size& size, ::std::ostream* os) {
- *os << "Size(" << size.width << ", " << size.height << ")";
+inline void PrintTo(Size size, std::ostream* stream) {
+ *stream << "Size(" << size.width << ", " << size.height << ')';
}
-} // namespace ui
-} // namespace android
+} // namespace android::ui
diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h
new file mode 100644
index 0000000..71adee4
--- /dev/null
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 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 <gmock/gmock.h>
+#include <ui/Fence.h>
+
+namespace android::mock {
+
+class MockFence : public android::Fence {
+public:
+ MockFence() = default;
+ virtual ~MockFence() = default;
+
+ MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
+ MOCK_METHOD(Status, getStatus, (), (override));
+};
+
+}; // namespace android::mock
diff --git a/libs/ui/Size.cpp b/libs/ui/include_types/ui/DataspaceUtils.h
similarity index 61%
copy from libs/ui/Size.cpp
copy to libs/ui/include_types/ui/DataspaceUtils.h
index d2996d1..a461cb4 100644
--- a/libs/ui/Size.cpp
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,16 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+#pragma once
-namespace android::ui {
+#include <ui/GraphicTypes.h>
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+namespace android {
-} // namespace android::ui
+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/GraphicTypes.h b/libs/ui/include_types/ui/GraphicTypes.h
new file mode 120000
index 0000000..b1859e0
--- /dev/null
+++ b/libs/ui/include_types/ui/GraphicTypes.h
@@ -0,0 +1 @@
+../../include/ui/GraphicTypes.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 516aad8..22fbf45 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,28 +27,40 @@
name: "Region_test",
shared_libs: ["libui"],
srcs: ["Region_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "colorspace_test",
shared_libs: ["libui"],
srcs: ["colorspace_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "DisplayId_test",
shared_libs: ["libui"],
srcs: ["DisplayId_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
name: "FlattenableHelpers_test",
shared_libs: ["libui"],
srcs: ["FlattenableHelpers_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -68,7 +80,10 @@
"GraphicBufferAllocator_test.cpp",
"mock/MockGrallocAllocator.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -83,14 +98,20 @@
"libutils",
],
srcs: ["GraphicBuffer_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
// This test has a main method, and requires a separate binary to be built.
cc_test {
name: "GraphicBufferOverBinder_test",
srcs: ["GraphicBufferOverBinder_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbinder",
"libgui",
@@ -105,7 +126,10 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Rect_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
cc_test {
@@ -113,5 +137,39 @@
test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "MockFence_test",
+ shared_libs: ["libui"],
+ static_libs: ["libgmock"],
+ srcs: ["MockFence_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "Transform_test",
+ shared_libs: ["libui"],
+ srcs: ["Transform_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+cc_test {
+ name: "DataspaceUtils_test",
+ shared_libs: ["libui"],
+ srcs: ["DataspaceUtils_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
new file mode 100644
index 0000000..3e09671
--- /dev/null
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 1d908b8..8ddee7e 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -32,6 +32,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createPhysicalIdFromPort) {
@@ -43,6 +46,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_TRUE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<PhysicalDisplayId>(id.value));
}
TEST(DisplayIdTest, createGpuVirtualId) {
@@ -52,6 +58,9 @@
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
}
TEST(DisplayIdTest, createHalVirtualId) {
@@ -61,6 +70,9 @@
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
}
} // namespace android::ui
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
new file mode 100644
index 0000000..40dddc3
--- /dev/null
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include <ui/MockFence.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+using testing::Return;
+
+class MockFenceTest : public testing::Test {
+public:
+ sp<Fence> getFenceForTesting() const { return mMockFence; }
+
+ const mock::MockFence& getMockFence() const { return *mMockFence; }
+
+private:
+ sp<mock::MockFence> mMockFence = sp<mock::MockFence>::make();
+};
+
+TEST_F(MockFenceTest, getSignalTime) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(Fence::SIGNAL_TIME_PENDING));
+ EXPECT_EQ(Fence::SIGNAL_TIME_PENDING, fence->getSignalTime());
+
+ EXPECT_CALL(getMockFence(), getSignalTime).WillOnce(Return(1234));
+ EXPECT_EQ(1234, fence->getSignalTime());
+}
+
+TEST_F(MockFenceTest, getStatus) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled));
+ EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled));
+ EXPECT_EQ(Fence::Status::Signaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid));
+ EXPECT_EQ(Fence::Status::Invalid, fence->getStatus());
+}
+} // namespace android::ui
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 5f75aea..acef47f 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -93,9 +93,8 @@
}
{
- const auto& s = Size::INVALID;
- EXPECT_FALSE(s.isValid());
- EXPECT_FALSE(s.isEmpty());
+ EXPECT_FALSE(kInvalidSize.isValid());
+ EXPECT_FALSE(kInvalidSize.isEmpty());
}
{
@@ -112,9 +111,8 @@
}
{
- const auto& s = Size::EMPTY;
- EXPECT_TRUE(s.isValid());
- EXPECT_TRUE(s.isEmpty());
+ EXPECT_TRUE(kEmptySize.isValid());
+ EXPECT_TRUE(kEmptySize.isEmpty());
}
{
diff --git a/libs/ui/tests/Transform_test.cpp b/libs/ui/tests/Transform_test.cpp
new file mode 100644
index 0000000..6964284
--- /dev/null
+++ b/libs/ui/tests/Transform_test.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+namespace android::ui {
+
+TEST(TransformTest, inverseRotation_hasCorrectType) {
+ const auto testRotationFlagsForInverse = [](Transform::RotationFlags rotation,
+ Transform::RotationFlags expectedInverse,
+ bool isRotation) {
+ const Transform t(rotation, 0, 0);
+ EXPECT_EQ(t.getOrientation(), rotation);
+ const Transform inverse = t.inverse();
+ EXPECT_EQ(inverse.getOrientation(), expectedInverse);
+
+ if (isRotation) {
+ EXPECT_TRUE(t.getType() & Transform::ROTATE);
+ EXPECT_TRUE(inverse.getType() & Transform::ROTATE);
+ } else {
+ EXPECT_FALSE(t.getType() & Transform::ROTATE);
+ EXPECT_FALSE(inverse.getType() & Transform::ROTATE);
+ }
+ };
+
+ testRotationFlagsForInverse(Transform::ROT_0, Transform::ROT_0, false);
+ testRotationFlagsForInverse(Transform::ROT_90, Transform::ROT_270, true);
+ testRotationFlagsForInverse(Transform::ROT_180, Transform::ROT_180, true);
+ testRotationFlagsForInverse(Transform::ROT_270, Transform::ROT_90, true);
+ testRotationFlagsForInverse(Transform::FLIP_H, Transform::FLIP_H, false);
+ testRotationFlagsForInverse(Transform::FLIP_V, Transform::FLIP_V, false);
+}
+
+} // namespace android::ui
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
deleted file mode 100644
index bf848af..0000000
--- a/libs/vr/libvrflinger/Android.bp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2008 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sourceFiles = [
- "acquired_buffer.cpp",
- "epoll_event_dispatcher.cpp",
- "display_manager_service.cpp",
- "display_service.cpp",
- "display_surface.cpp",
- "hardware_composer.cpp",
- "vr_flinger.cpp",
-]
-
-includeFiles = [ "include" ]
-
-staticLibraries = [
- "libdisplay",
- "libdvrcommon",
- "libperformance",
- "libvrsensor",
- "libbroadcastring",
- "libvr_manager",
- "libbroadcastring",
-]
-
-sharedLibraries = [
- "android.frameworks.vr.composer@2.0",
- "android.hardware.graphics.allocator@2.0",
- "android.hardware.graphics.composer@2.1",
- "android.hardware.graphics.composer@2.2",
- "android.hardware.graphics.composer@2.3",
- "android.hardware.graphics.composer@2.4",
- "libbinder",
- "libbase",
- "libbufferhubqueue",
- "libcutils",
- "liblog",
- "libhardware",
- "libnativewindow",
- "libprocessgroup",
- "libutils",
- "libEGL",
- "libGLESv1_CM",
- "libGLESv2",
- "libvulkan",
- "libui",
- "libgui",
- "libsync",
- "libhidlbase",
- "libfmq",
- "libpdx_default_transport",
-]
-
-headerLibraries = [
- "android.hardware.graphics.composer@2.1-command-buffer",
- "android.hardware.graphics.composer@2.2-command-buffer",
- "android.hardware.graphics.composer@2.3-command-buffer",
- "android.hardware.graphics.composer@2.4-command-buffer",
- "libdvr_headers",
- "libsurfaceflinger_headers",
-]
-
-cc_library_static {
- srcs: sourceFiles,
- export_include_dirs: includeFiles,
-
- clang: true,
- cflags: [
- "-DLOG_TAG=\"vr_flinger\"",
- "-DTRACE=0",
- "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
- "-DGL_GLEXT_PROTOTYPES",
- "-DEGL_EGLEXT_PROTOTYPES",
- "-Wall",
- "-Werror",
- "-Wno-error=sign-compare", // to fix later
- "-Wno-unused-variable",
- ],
- shared_libs: sharedLibraries,
- whole_static_libs: staticLibraries,
- header_libs: headerLibraries,
- name: "libvrflinger",
-}
-
-subdirs = [
- "tests",
-]
diff --git a/libs/vr/libvrflinger/acquired_buffer.cpp b/libs/vr/libvrflinger/acquired_buffer.cpp
deleted file mode 100644
index c360dee..0000000
--- a/libs/vr/libvrflinger/acquired_buffer.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "acquired_buffer.h"
-
-#include <log/log.h>
-#include <sync/sync.h>
-
-using android::pdx::LocalHandle;
-
-namespace android {
-namespace dvr {
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- LocalHandle acquire_fence, std::size_t slot)
- : buffer_(buffer), acquire_fence_(std::move(acquire_fence)), slot_(slot) {}
-
-AcquiredBuffer::AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- int* error) {
- LocalHandle fence;
- const int ret = buffer->Acquire(&fence);
-
- if (error)
- *error = ret;
-
- if (ret < 0) {
- ALOGW("AcquiredBuffer::AcquiredBuffer: Failed to acquire buffer: %s",
- strerror(-ret));
- buffer_ = nullptr;
- // Default construct sets acquire_fence_ to empty.
- } else {
- buffer_ = buffer;
- acquire_fence_ = std::move(fence);
- }
-}
-
-AcquiredBuffer::AcquiredBuffer(AcquiredBuffer&& other) noexcept {
- *this = std::move(other);
-}
-
-AcquiredBuffer::~AcquiredBuffer() { Release(LocalHandle(kEmptyFence)); }
-
-AcquiredBuffer& AcquiredBuffer::operator=(AcquiredBuffer&& other) noexcept {
- if (this != &other) {
- Release();
-
- using std::swap;
- swap(buffer_, other.buffer_);
- swap(acquire_fence_, other.acquire_fence_);
- swap(slot_, other.slot_);
- }
- return *this;
-}
-
-bool AcquiredBuffer::IsAvailable() const {
- if (IsEmpty())
- return false;
-
- // Only check the fence if the acquire fence is not empty.
- if (acquire_fence_) {
- const int ret = sync_wait(acquire_fence_.Get(), 0);
- ALOGD_IF(TRACE || (ret < 0 && errno != ETIME),
- "AcquiredBuffer::IsAvailable: buffer_id=%d acquire_fence=%d "
- "sync_wait()=%d errno=%d.",
- buffer_->id(), acquire_fence_.Get(), ret, ret < 0 ? errno : 0);
- if (ret == 0) {
- // The fence is completed, so to avoid further calls to sync_wait we close
- // it here.
- acquire_fence_.Close();
- }
- return ret == 0;
- } else {
- return true;
- }
-}
-
-LocalHandle AcquiredBuffer::ClaimAcquireFence() {
- return std::move(acquire_fence_);
-}
-
-std::shared_ptr<ConsumerBuffer> AcquiredBuffer::ClaimBuffer() {
- return std::move(buffer_);
-}
-
-int AcquiredBuffer::Release(LocalHandle release_fence) {
- ALOGD_IF(TRACE, "AcquiredBuffer::Release: buffer_id=%d release_fence=%d",
- buffer_ ? buffer_->id() : -1, release_fence.Get());
- if (buffer_) {
- const int ret = buffer_->ReleaseAsync();
- if (ret < 0) {
- ALOGE("AcquiredBuffer::Release: Failed to release buffer %d: %s",
- buffer_->id(), strerror(-ret));
- if (ret != -ESHUTDOWN)
- return ret;
- }
-
- buffer_ = nullptr;
- }
-
- acquire_fence_.Close();
- slot_ = 0;
- return 0;
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h
deleted file mode 100644
index 7643e75..0000000
--- a/libs/vr/libvrflinger/acquired_buffer.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
-
-#include <pdx/file_handle.h>
-#include <private/dvr/consumer_buffer.h>
-
-#include <memory>
-
-namespace android {
-namespace dvr {
-
-// Manages the ACQUIRE/RELEASE ownership cycle of a ConsumerBuffer.
-class AcquiredBuffer {
- public:
- static constexpr int kEmptyFence = pdx::LocalHandle::kEmptyFileHandle;
-
- AcquiredBuffer() : buffer_(nullptr), acquire_fence_(kEmptyFence) {}
-
- // Constructs an AcquiredBuffer from a ConsumerBuffer pointer and an acquire
- // fence. The ConsumerBuffer MUST be in the ACQUIRED state prior to calling
- // this constructor; the constructor does not attempt to ACQUIRE the buffer
- // itself.
- AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer,
- pdx::LocalHandle acquire_fence, std::size_t slot = 0);
-
- // Constructs an AcquiredBuffer from a ConsumerBuffer. The ConsumerBuffer MUST
- // be in the POSTED state prior to calling this constructor, as this
- // constructor attempts to ACQUIRE the buffer. If ACQUIRING the buffer fails
- // this instance is left in the empty state. An optional error code is
- // returned in |error|, which may be nullptr if not needed.
- AcquiredBuffer(const std::shared_ptr<ConsumerBuffer>& buffer, int* error);
-
- // Move constructor. Behaves similarly to the move assignment operator below.
- AcquiredBuffer(AcquiredBuffer&& other) noexcept;
-
- ~AcquiredBuffer();
-
- // Move assignment operator. Moves the ConsumerBuffer and acquire fence from
- // |other| into this instance after RELEASING the current ConsumerBuffer and
- // closing the acquire fence. After the move |other| is left in the empty
- // state.
- AcquiredBuffer& operator=(AcquiredBuffer&& other) noexcept;
-
- // Accessors for the underlying ConsumerBuffer, the acquire fence, and the
- // use-case specific sequence value from the acquisition (see
- // private/dvr/consumer_buffer.h).
- std::shared_ptr<ConsumerBuffer> buffer() const { return buffer_; }
- int acquire_fence() const { return acquire_fence_.Get(); }
-
- // When non-empty, returns true if the acquired fence was signaled (or if the
- // fence is empty). Returns false when empty or if the fence is not signaled.
- bool IsAvailable() const;
-
- bool IsEmpty() const { return buffer_ == nullptr; }
-
- // Returns the acquire fence, passing ownership to the caller.
- pdx::LocalHandle ClaimAcquireFence();
-
- // Returns the buffer, passing ownership to the caller. Caller is responsible
- // for calling Release on the returned buffer.
- std::shared_ptr<ConsumerBuffer> ClaimBuffer();
-
- // Releases the ConsumerBuffer, passing the release fence in |release_fence|
- // to the producer. On success, the ConsumerBuffer and acquire fence are set
- // to empty state; if release fails, the ConsumerBuffer and acquire fence are
- // left in place and a negative error code is returned.
- int Release(pdx::LocalHandle release_fence = {});
-
- // Returns the slot in the queue this buffer belongs to. Buffers that are not
- // part of a queue return 0.
- std::size_t slot() const { return slot_; }
-
- private:
- std::shared_ptr<ConsumerBuffer> buffer_;
- // Mutable so that the fence can be closed when it is determined to be
- // signaled during IsAvailable().
- mutable pdx::LocalHandle acquire_fence_;
- std::size_t slot_{0};
-
- AcquiredBuffer(const AcquiredBuffer&) = delete;
- void operator=(const AcquiredBuffer&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_ACQUIRED_BUFFER_H_
diff --git a/libs/vr/libvrflinger/display_manager_service.cpp b/libs/vr/libvrflinger/display_manager_service.cpp
deleted file mode 100644
index 34b3b0a..0000000
--- a/libs/vr/libvrflinger/display_manager_service.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "display_manager_service.h"
-
-#include <pdx/channel_handle.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/trusted_uids.h>
-#include <sys/poll.h>
-
-#include <array>
-
-using android::dvr::display::DisplayManagerProtocol;
-using android::pdx::Channel;
-using android::pdx::LocalChannelHandle;
-using android::pdx::Message;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::ErrorStatus;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-using android::pdx::rpc::RemoteMethodError;
-
-namespace android {
-namespace dvr {
-
-void DisplayManager::SetNotificationsPending(bool pending) {
- auto status = service_->ModifyChannelEvents(channel_id_, pending ? 0 : POLLIN,
- pending ? POLLIN : 0);
- ALOGE_IF(!status,
- "DisplayManager::SetNotificationPending: Failed to modify channel "
- "events: %s",
- status.GetErrorMessage().c_str());
-}
-
-DisplayManagerService::DisplayManagerService(
- const std::shared_ptr<DisplayService>& display_service)
- : BASE("DisplayManagerService",
- Endpoint::Create(DisplayManagerProtocol::kClientPath)),
- display_service_(display_service) {
- display_service_->SetDisplayConfigurationUpdateNotifier(
- std::bind(&DisplayManagerService::OnDisplaySurfaceChange, this));
-}
-
-std::shared_ptr<pdx::Channel> DisplayManagerService::OnChannelOpen(
- pdx::Message& message) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
- // Check if the display_manager_ has a defunct channel.
- if (display_manager_ && !HasChannelId(display_manager_->channel_id())) {
- ALOGE("DisplayManagerService::OnChannelOpen: Found defunct channel %d with "
- "no OnChannelClose, clearing prior display manager.",
- display_manager_->channel_id());
- display_manager_ = nullptr;
- }
-
- // Prevent more than one display manager from registering at a time or
- // untrusted UIDs from connecting.
- if (display_manager_ || !trusted) {
- RemoteMethodError(message, EPERM);
- return nullptr;
- }
-
- display_manager_ =
- std::make_shared<DisplayManager>(this, message.GetChannelId());
- return display_manager_;
-}
-
-void DisplayManagerService::OnChannelClose(
- pdx::Message& /*message*/, const std::shared_ptr<pdx::Channel>& channel) {
- // Unregister the display manager when the channel closes.
- if (display_manager_ == channel)
- display_manager_ = nullptr;
-}
-
-pdx::Status<void> DisplayManagerService::HandleMessage(pdx::Message& message) {
- ATRACE_NAME("DisplayManagerService::HandleMessage");
- auto channel = std::static_pointer_cast<DisplayManager>(message.GetChannel());
-
- switch (message.GetOp()) {
- case DisplayManagerProtocol::GetSurfaceState::Opcode:
- DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceState>(
- *this, &DisplayManagerService::OnGetSurfaceState, message);
- return {};
-
- case DisplayManagerProtocol::GetSurfaceQueue::Opcode:
- DispatchRemoteMethod<DisplayManagerProtocol::GetSurfaceQueue>(
- *this, &DisplayManagerService::OnGetSurfaceQueue, message);
- return {};
-
- default:
- return Service::DefaultHandleMessage(message);
- }
-}
-
-pdx::Status<std::vector<display::SurfaceState>>
-DisplayManagerService::OnGetSurfaceState(pdx::Message& /*message*/) {
- std::vector<display::SurfaceState> items;
-
- display_service_->ForEachDisplaySurface(
- SurfaceType::Application,
- [&items](const std::shared_ptr<DisplaySurface>& surface) mutable {
- items.push_back({surface->surface_id(), surface->process_id(),
- surface->user_id(), surface->attributes(),
- surface->update_flags(), surface->GetQueueIds()});
- surface->ClearUpdate();
- });
-
- // The fact that we're in the message handler implies that display_manager_ is
- // not nullptr. No check required, unless this service becomes multi-threaded.
- display_manager_->SetNotificationsPending(false);
- return items;
-}
-
-pdx::Status<pdx::LocalChannelHandle> DisplayManagerService::OnGetSurfaceQueue(
- pdx::Message& /*message*/, int surface_id, int queue_id) {
- auto surface = display_service_->GetDisplaySurface(surface_id);
- if (!surface || surface->surface_type() != SurfaceType::Application)
- return ErrorStatus(EINVAL);
-
- auto queue =
- std::static_pointer_cast<ApplicationDisplaySurface>(surface)->GetQueue(
- queue_id);
- if (!queue)
- return ErrorStatus(EINVAL);
-
- auto status = queue->CreateConsumerQueueHandle();
- ALOGE_IF(
- !status,
- "DisplayManagerService::OnGetSurfaceQueue: Failed to create consumer "
- "queue for queue_id=%d: %s",
- queue->id(), status.GetErrorMessage().c_str());
-
- return status;
-}
-
-void DisplayManagerService::OnDisplaySurfaceChange() {
- if (display_manager_)
- display_manager_->SetNotificationsPending(true);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_manager_service.h b/libs/vr/libvrflinger/display_manager_service.h
deleted file mode 100644
index 3133fe1..0000000
--- a/libs/vr/libvrflinger/display_manager_service.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-#define ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
-
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/display_protocol.h>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayManagerService;
-
-// The display manager is a client of the display manager service. This class
-// represents the connected client that the display manager service sends
-// notifications to.
-class DisplayManager : public pdx::Channel {
- public:
- DisplayManager(DisplayManagerService* service, int channel_id)
- : service_(service), channel_id_(channel_id) {}
-
- int channel_id() const { return channel_id_; }
-
- // Sets or clears the channel event mask to indicate pending events that the
- // display manager on the other end of the channel should read and handle.
- // When |pending| is true the POLLIN bit is set in the event mask; when
- // |pending| is false the POLLIN bit is cleared in the event mask.
- void SetNotificationsPending(bool pending);
-
- private:
- DisplayManager(const DisplayManager&) = delete;
- void operator=(const DisplayManager&) = delete;
-
- DisplayManagerService* service_;
- int channel_id_;
-};
-
-// The display manager service marshalls state and events from the display
-// service to the display manager.
-class DisplayManagerService : public pdx::ServiceBase<DisplayManagerService> {
- public:
- std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
- void OnChannelClose(pdx::Message& message,
- const std::shared_ptr<pdx::Channel>& channel) override;
- pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- private:
- friend BASE;
-
- explicit DisplayManagerService(
- const std::shared_ptr<DisplayService>& display_service);
-
- pdx::Status<std::vector<display::SurfaceState>> OnGetSurfaceState(
- pdx::Message& message);
- pdx::Status<pdx::LocalChannelHandle> OnGetSurfaceQueue(pdx::Message& message,
- int surface_id,
- int queue_id);
-
- // Called by the display service to indicate changes to display surfaces that
- // the display manager should evaluate.
- void OnDisplaySurfaceChange();
-
- DisplayManagerService(const DisplayManagerService&) = delete;
- void operator=(const DisplayManagerService&) = delete;
-
- std::shared_ptr<DisplayService> display_service_;
- std::shared_ptr<DisplayManager> display_manager_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_VRFLINGER_DISPLAY_MANAGER_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
deleted file mode 100644
index 582fed3..0000000
--- a/libs/vr/libvrflinger/display_service.cpp
+++ /dev/null
@@ -1,437 +0,0 @@
-#include "display_service.h"
-
-#include <unistd.h>
-
-#include <algorithm>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <pdx/rpc/remote_method.h>
-#include <private/android_filesystem_config.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/numeric.h>
-#include <private/dvr/trusted_uids.h>
-#include <private/dvr/types.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::Channel;
-using android::pdx::ErrorStatus;
-using android::pdx::Message;
-using android::pdx::Status;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace {
-
-const char kDvrLensMetricsProperty[] = "ro.dvr.lens_metrics";
-const char kDvrDeviceMetricsProperty[] = "ro.dvr.device_metrics";
-const char kDvrDeviceConfigProperty[] = "ro.dvr.device_configuration";
-
-} // namespace
-
-namespace android {
-namespace dvr {
-
-DisplayService::DisplayService(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback)
- : BASE("DisplayService",
- Endpoint::Create(display::DisplayProtocol::kClientPath)) {
- hardware_composer_.Initialize(
- hidl, primary_display_id, request_display_callback);
-}
-
-bool DisplayService::IsInitialized() const {
- return BASE::IsInitialized() && hardware_composer_.IsInitialized();
-}
-
-std::string DisplayService::DumpState(size_t /*max_length*/) {
- std::ostringstream stream;
-
- auto surfaces = GetDisplaySurfaces();
- std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
- return a->surface_id() < b->surface_id();
- });
-
- stream << "Application Surfaces:" << std::endl;
-
- size_t count = 0;
- for (const auto& surface : surfaces) {
- if (surface->surface_type() == SurfaceType::Application) {
- stream << "Surface " << count++ << ":";
- stream << " surface_id=" << surface->surface_id()
- << " process_id=" << surface->process_id()
- << " user_id=" << surface->user_id()
- << " visible=" << surface->visible()
- << " z_order=" << surface->z_order();
-
- stream << " queue_ids=";
- auto queue_ids = surface->GetQueueIds();
- std::sort(queue_ids.begin(), queue_ids.end());
- for (int32_t id : queue_ids) {
- if (id != queue_ids[0])
- stream << ",";
- stream << id;
- }
- stream << std::endl;
- }
- }
- stream << std::endl;
-
- stream << "Direct Surfaces:" << std::endl;
-
- count = 0;
- for (const auto& surface : surfaces) {
- if (surface->surface_type() == SurfaceType::Direct) {
- stream << "Surface " << count++ << ":";
- stream << " surface_id=" << surface->surface_id()
- << " process_id=" << surface->process_id()
- << " user_id=" << surface->user_id()
- << " visible=" << surface->visible()
- << " z_order=" << surface->z_order();
-
- stream << " queue_ids=";
- auto queue_ids = surface->GetQueueIds();
- std::sort(queue_ids.begin(), queue_ids.end());
- for (int32_t id : queue_ids) {
- if (id != queue_ids[0])
- stream << ",";
- stream << id;
- }
- stream << std::endl;
- }
- }
- stream << std::endl;
-
- stream << hardware_composer_.Dump();
- return stream.str();
-}
-
-void DisplayService::OnChannelClose(pdx::Message& message,
- const std::shared_ptr<Channel>& channel) {
- if (auto surface = std::static_pointer_cast<DisplaySurface>(channel)) {
- surface->OnSetAttributes(message,
- {{display::SurfaceAttribute::Visible,
- display::SurfaceAttributeValue{false}}});
- }
-}
-
-// First-level dispatch for display service messages. Directly handles messages
-// that are independent of the display surface (metrics, creation) and routes
-// surface-specific messages to the per-instance handlers.
-Status<void> DisplayService::HandleMessage(pdx::Message& message) {
- ALOGD_IF(TRACE, "DisplayService::HandleMessage: opcode=%d", message.GetOp());
- ATRACE_NAME("DisplayService::HandleMessage");
-
- switch (message.GetOp()) {
- case DisplayProtocol::GetMetrics::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetMetrics>(
- *this, &DisplayService::OnGetMetrics, message);
- return {};
-
- case DisplayProtocol::GetConfigurationData::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetConfigurationData>(
- *this, &DisplayService::OnGetConfigurationData, message);
- return {};
-
- case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
- *this, &DisplayService::OnGetDisplayIdentificationPort, message);
- return {};
-
- case DisplayProtocol::CreateSurface::Opcode:
- DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
- *this, &DisplayService::OnCreateSurface, message);
- return {};
-
- case DisplayProtocol::SetupGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::SetupGlobalBuffer>(
- *this, &DisplayService::OnSetupGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::DeleteGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::DeleteGlobalBuffer>(
- *this, &DisplayService::OnDeleteGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::GetGlobalBuffer::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetGlobalBuffer>(
- *this, &DisplayService::OnGetGlobalBuffer, message);
- return {};
-
- case DisplayProtocol::IsVrAppRunning::Opcode:
- DispatchRemoteMethod<DisplayProtocol::IsVrAppRunning>(
- *this, &DisplayService::IsVrAppRunning, message);
- return {};
-
- // Direct the surface specific messages to the surface instance.
- case DisplayProtocol::SetAttributes::Opcode:
- case DisplayProtocol::CreateQueue::Opcode:
- case DisplayProtocol::GetSurfaceInfo::Opcode:
- return HandleSurfaceMessage(message);
-
- default:
- return Service::HandleMessage(message);
- }
-}
-
-Status<display::Metrics> DisplayService::OnGetMetrics(
- pdx::Message& /*message*/) {
- const auto& params = hardware_composer_.GetPrimaryDisplayParams();
- return {{static_cast<uint32_t>(params.width),
- static_cast<uint32_t>(params.height),
- static_cast<uint32_t>(params.dpi.x),
- static_cast<uint32_t>(params.dpi.y),
- static_cast<uint32_t>(params.vsync_period_ns),
- 0,
- 0,
- 0,
- 0.0,
- {},
- {}}};
-}
-
-pdx::Status<std::string> DisplayService::OnGetConfigurationData(
- pdx::Message& /*message*/, display::ConfigFileType config_type) {
- std::string property_name;
- DisplayIdentificationData display_identification_data;
- switch (config_type) {
- case display::ConfigFileType::kLensMetrics:
- property_name = kDvrLensMetricsProperty;
- break;
- case display::ConfigFileType::kDeviceMetrics:
- property_name = kDvrDeviceMetricsProperty;
- break;
- case display::ConfigFileType::kDeviceConfiguration:
- property_name = kDvrDeviceConfigProperty;
- break;
- case display::ConfigFileType::kDeviceEdid:
- display_identification_data =
- hardware_composer_.GetCurrentDisplayIdentificationData();
- if (display_identification_data.size() == 0) {
- return ErrorStatus(ENOENT);
- }
- return std::string(display_identification_data.begin(),
- display_identification_data.end());
- default:
- return ErrorStatus(EINVAL);
- }
- std::string file_path = base::GetProperty(property_name, "");
- if (file_path.empty()) {
- return ErrorStatus(ENOENT);
- }
-
- std::string data;
- if (!base::ReadFileToString(file_path, &data)) {
- return ErrorStatus(errno);
- }
-
- return std::move(data);
-}
-
-pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
- pdx::Message& /*message*/) {
- return hardware_composer_.GetCurrentDisplayPort();
-}
-
-// Creates a new DisplaySurface and associates it with this channel. This may
-// only be done once per channel.
-Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
- pdx::Message& message, const display::SurfaceAttributes& attributes) {
- // A surface may only be created once per channel.
- if (message.GetChannel())
- return ErrorStatus(EINVAL);
-
- ALOGI_IF(TRACE, "DisplayService::OnCreateSurface: cid=%d",
- message.GetChannelId());
-
- // Use the channel id as the unique surface id.
- const int surface_id = message.GetChannelId();
- const int process_id = message.GetProcessId();
- const int user_id = message.GetEffectiveUserId();
-
- ALOGI_IF(TRACE,
- "DisplayService::OnCreateSurface: surface_id=%d process_id=%d",
- surface_id, process_id);
-
- auto surface_status =
- DisplaySurface::Create(this, surface_id, process_id, user_id, attributes);
- if (!surface_status) {
- ALOGE("DisplayService::OnCreateSurface: Failed to create surface: %s",
- surface_status.GetErrorMessage().c_str());
- return ErrorStatus(surface_status.error());
- }
- auto surface = surface_status.take();
- message.SetChannel(surface);
-
- // Update the surface with the attributes supplied with the create call. For
- // application surfaces this has the side effect of notifying the display
- // manager of the new surface. For direct surfaces, this may trigger a mode
- // change, depending on the value of the visible attribute.
- surface->OnSetAttributes(message, attributes);
-
- return {{surface->surface_id(), surface->visible(), surface->z_order()}};
-}
-
-void DisplayService::SurfaceUpdated(SurfaceType surface_type,
- display::SurfaceUpdateFlags update_flags) {
- ALOGD_IF(TRACE, "DisplayService::SurfaceUpdated: update_flags=%x",
- update_flags.value());
- if (update_flags.value() != 0) {
- if (surface_type == SurfaceType::Application)
- NotifyDisplayConfigurationUpdate();
- else
- UpdateActiveDisplaySurfaces();
- }
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnSetupGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key, size_t size,
- uint64_t usage) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
-
- if (!trusted) {
- ALOGE(
- "DisplayService::OnSetupGlobalBuffer: Permission denied for user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- return SetupGlobalBuffer(key, size, usage);
-}
-
-pdx::Status<void> DisplayService::OnDeleteGlobalBuffer(pdx::Message& message,
- DvrGlobalBufferKey key) {
- const int user_id = message.GetEffectiveUserId();
- const bool trusted = (user_id == AID_ROOT) || IsTrustedUid(user_id);
-
- if (!trusted) {
- ALOGE(
- "DisplayService::OnDeleteGlobalBuffer: Permission denied for "
- "user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- return DeleteGlobalBuffer(key);
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::OnGetGlobalBuffer(
- pdx::Message& /* message */, DvrGlobalBufferKey key) {
- ALOGD_IF(TRACE, "DisplayService::OnGetGlobalBuffer: key=%d", key);
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer != global_buffers_.end())
- return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
- else
- return pdx::ErrorStatus(EINVAL);
-}
-
-// Calls the message handler for the DisplaySurface associated with this
-// channel.
-Status<void> DisplayService::HandleSurfaceMessage(pdx::Message& message) {
- auto surface = std::static_pointer_cast<DisplaySurface>(message.GetChannel());
- ALOGW_IF(!surface,
- "DisplayService::HandleSurfaceMessage: surface is nullptr!");
-
- if (surface)
- return surface->HandleMessage(message);
- else
- return ErrorStatus(EINVAL);
-}
-
-std::shared_ptr<DisplaySurface> DisplayService::GetDisplaySurface(
- int surface_id) const {
- return std::static_pointer_cast<DisplaySurface>(GetChannel(surface_id));
-}
-
-std::vector<std::shared_ptr<DisplaySurface>>
-DisplayService::GetDisplaySurfaces() const {
- return GetChannels<DisplaySurface>();
-}
-
-std::vector<std::shared_ptr<DirectDisplaySurface>>
-DisplayService::GetVisibleDisplaySurfaces() const {
- std::vector<std::shared_ptr<DirectDisplaySurface>> visible_surfaces;
-
- ForEachDisplaySurface(
- SurfaceType::Direct,
- [&](const std::shared_ptr<DisplaySurface>& surface) mutable {
- if (surface->visible()) {
- visible_surfaces.push_back(
- std::static_pointer_cast<DirectDisplaySurface>(surface));
- surface->ClearUpdate();
- }
- });
-
- return visible_surfaces;
-}
-
-void DisplayService::UpdateActiveDisplaySurfaces() {
- auto visible_surfaces = GetVisibleDisplaySurfaces();
- ALOGD_IF(TRACE,
- "DisplayService::UpdateActiveDisplaySurfaces: %zd visible surfaces",
- visible_surfaces.size());
- hardware_composer_.SetDisplaySurfaces(std::move(visible_surfaces));
-}
-
-pdx::Status<BorrowedNativeBufferHandle> DisplayService::SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage) {
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer == global_buffers_.end()) {
- auto ion_buffer = std::make_unique<IonBuffer>(static_cast<int>(size), 1,
- HAL_PIXEL_FORMAT_BLOB, usage);
-
- // Some buffers are used internally. If they were configured with an
- // invalid size or format, this will fail.
- int result = hardware_composer_.OnNewGlobalBuffer(key, *ion_buffer.get());
- if (result < 0)
- return ErrorStatus(result);
- global_buffer =
- global_buffers_.insert(std::make_pair(key, std::move(ion_buffer)))
- .first;
- }
-
- return {BorrowedNativeBufferHandle(*global_buffer->second, 0)};
-}
-
-pdx::Status<void> DisplayService::DeleteGlobalBuffer(DvrGlobalBufferKey key) {
- auto global_buffer = global_buffers_.find(key);
- if (global_buffer != global_buffers_.end()) {
- // Some buffers are used internally.
- hardware_composer_.OnDeletedGlobalBuffer(key);
- global_buffers_.erase(global_buffer);
- }
-
- return {0};
-}
-
-void DisplayService::SetDisplayConfigurationUpdateNotifier(
- DisplayConfigurationUpdateNotifier update_notifier) {
- update_notifier_ = update_notifier;
-}
-
-void DisplayService::NotifyDisplayConfigurationUpdate() {
- if (update_notifier_)
- update_notifier_();
-}
-
-Status<bool> DisplayService::IsVrAppRunning(pdx::Message& /*message*/) {
- bool visible = false;
- ForEachDisplaySurface(
- SurfaceType::Application,
- [&visible](const std::shared_ptr<DisplaySurface>& surface) {
- if (surface->visible())
- visible = true;
- });
-
- return {visible};
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
deleted file mode 100644
index 89f1eae..0000000
--- a/libs/vr/libvrflinger/display_service.h
+++ /dev/null
@@ -1,126 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
-
-#include <dvr/dvr_api.h>
-#include <pdx/service.h>
-#include <pdx/status.h>
-#include <private/dvr/bufferhub_rpc.h>
-#include <private/dvr/display_protocol.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-#include "display_surface.h"
-#include "epoll_event_dispatcher.h"
-#include "hardware_composer.h"
-
-namespace android {
-namespace dvr {
-
-// DisplayService implements the display service component of VrFlinger.
-class DisplayService : public pdx::ServiceBase<DisplayService> {
- public:
- bool IsInitialized() const override;
- std::string DumpState(size_t max_length) override;
-
- void OnChannelClose(pdx::Message& message,
- const std::shared_ptr<pdx::Channel>& channel) override;
- pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
- std::shared_ptr<DisplaySurface> GetDisplaySurface(int surface_id) const;
- std::vector<std::shared_ptr<DisplaySurface>> GetDisplaySurfaces() const;
- std::vector<std::shared_ptr<DirectDisplaySurface>> GetVisibleDisplaySurfaces()
- const;
-
- // Updates the list of actively displayed surfaces. This must be called after
- // any change to client/manager attributes that affect visibility or z order.
- void UpdateActiveDisplaySurfaces();
-
- pdx::Status<BorrowedNativeBufferHandle> SetupGlobalBuffer(
- DvrGlobalBufferKey key, size_t size, uint64_t usage);
-
- pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
-
- template <class A>
- void ForEachDisplaySurface(SurfaceType surface_type, A action) const {
- ForEachChannel([surface_type,
- action](const ChannelIterator::value_type& pair) mutable {
- auto surface = std::static_pointer_cast<DisplaySurface>(pair.second);
- if (surface->surface_type() == surface_type)
- action(surface);
- });
- }
-
- using DisplayConfigurationUpdateNotifier = std::function<void(void)>;
- void SetDisplayConfigurationUpdateNotifier(
- DisplayConfigurationUpdateNotifier notifier);
-
- void GrantDisplayOwnership() { hardware_composer_.Enable(); }
- void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
- void OnBootFinished() { hardware_composer_.OnBootFinished(); }
-
- private:
- friend BASE;
- friend DisplaySurface;
-
- friend class VrDisplayStateService;
-
- using RequestDisplayCallback = std::function<void(bool)>;
-
- DisplayService(android::Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- pdx::Status<BorrowedNativeBufferHandle> OnGetGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key);
- pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
- pdx::Status<std::string> OnGetConfigurationData(
- pdx::Message& message, display::ConfigFileType config_type);
- pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message);
- pdx::Status<display::SurfaceInfo> OnCreateSurface(
- pdx::Message& message, const display::SurfaceAttributes& attributes);
- pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
- pdx::Message& message, DvrGlobalBufferKey key, size_t size,
- uint64_t usage);
- pdx::Status<void> OnDeleteGlobalBuffer(pdx::Message& message,
- DvrGlobalBufferKey key);
-
- // Temporary query for current VR status. Will be removed later.
- pdx::Status<bool> IsVrAppRunning(pdx::Message& message);
-
- pdx::Status<void> AddEventHandler(int fd, int events,
- EpollEventDispatcher::Handler handler) {
- return dispatcher_.AddEventHandler(fd, events, handler);
- }
- pdx::Status<void> RemoveEventHandler(int fd) {
- return dispatcher_.RemoveEventHandler(fd);
- }
-
- void SurfaceUpdated(SurfaceType surface_type,
- display::SurfaceUpdateFlags update_flags);
-
- // Called by DisplaySurface to signal that a surface property has changed and
- // the display manager should be notified.
- void NotifyDisplayConfigurationUpdate();
-
- pdx::Status<void> HandleSurfaceMessage(pdx::Message& message);
-
- HardwareComposer hardware_composer_;
- EpollEventDispatcher dispatcher_;
- DisplayConfigurationUpdateNotifier update_notifier_;
-
- std::unordered_map<DvrGlobalBufferKey, std::unique_ptr<IonBuffer>>
- global_buffers_;
-
- DisplayService(const DisplayService&) = delete;
- void operator=(const DisplayService&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SERVICE_H_
diff --git a/libs/vr/libvrflinger/display_surface.cpp b/libs/vr/libvrflinger/display_surface.cpp
deleted file mode 100644
index 87c823e..0000000
--- a/libs/vr/libvrflinger/display_surface.cpp
+++ /dev/null
@@ -1,488 +0,0 @@
-#include "display_surface.h"
-
-#include <private/android_filesystem_config.h>
-#include <utils/Trace.h>
-
-#include <private/dvr/trusted_uids.h>
-
-#include "display_service.h"
-#include "hardware_composer.h"
-
-#define LOCAL_TRACE 1
-
-using android::dvr::display::DisplayProtocol;
-using android::pdx::BorrowedChannelHandle;
-using android::pdx::ErrorStatus;
-using android::pdx::LocalChannelHandle;
-using android::pdx::LocalHandle;
-using android::pdx::Message;
-using android::pdx::RemoteChannelHandle;
-using android::pdx::Status;
-using android::pdx::rpc::DispatchRemoteMethod;
-using android::pdx::rpc::IfAnyOf;
-
-namespace android {
-namespace dvr {
-
-DisplaySurface::DisplaySurface(DisplayService* service,
- SurfaceType surface_type, int surface_id,
- int process_id, int user_id)
- : service_(service),
- surface_type_(surface_type),
- surface_id_(surface_id),
- process_id_(process_id),
- user_id_(user_id),
- update_flags_(display::SurfaceUpdateFlags::NewSurface) {}
-
-DisplaySurface::~DisplaySurface() {
- ALOGD_IF(LOCAL_TRACE,
- "DisplaySurface::~DisplaySurface: surface_id=%d process_id=%d",
- surface_id(), process_id());
-}
-
-Status<void> DisplaySurface::HandleMessage(pdx::Message& message) {
- switch (message.GetOp()) {
- case DisplayProtocol::SetAttributes::Opcode:
- DispatchRemoteMethod<DisplayProtocol::SetAttributes>(
- *this, &DisplaySurface::OnSetAttributes, message);
- break;
-
- case DisplayProtocol::GetSurfaceInfo::Opcode:
- DispatchRemoteMethod<DisplayProtocol::GetSurfaceInfo>(
- *this, &DisplaySurface::OnGetSurfaceInfo, message);
- break;
-
- case DisplayProtocol::CreateQueue::Opcode:
- DispatchRemoteMethod<DisplayProtocol::CreateQueue>(
- *this, &DisplaySurface::OnCreateQueue, message);
- break;
- }
-
- return {};
-}
-
-Status<void> DisplaySurface::OnSetAttributes(
- pdx::Message& /*message*/, const display::SurfaceAttributes& attributes) {
- display::SurfaceUpdateFlags update_flags;
-
- for (const auto& attribute : attributes) {
- const auto key = attribute.first;
- const auto* variant = &attribute.second;
- bool invalid_value = false;
- bool visibility_changed = false;
-
- // Catch attributes that have significance to the display service.
- switch (key) {
- case display::SurfaceAttribute::ZOrder:
- invalid_value = !IfAnyOf<int32_t, int64_t, float>::Call(
- variant, [&](const auto& value) {
- if (z_order_ != value) {
- visibility_changed = true;
- z_order_ = value;
- }
- });
- break;
- case display::SurfaceAttribute::Visible:
- invalid_value = !IfAnyOf<int32_t, int64_t, bool>::Call(
- variant, [&](const auto& value) {
- if (visible_ != value) {
- visibility_changed = true;
- visible_ = value;
- }
- });
- break;
- }
-
- // Only update the attribute map with valid values. This check also has the
- // effect of preventing special attributes handled above from being deleted
- // by an empty value.
- if (invalid_value) {
- ALOGW(
- "DisplaySurface::OnClientSetAttributes: Failed to set display "
- "surface attribute '%d' because of incompatible type: %d",
- key, variant->index());
- } else {
- // An empty value indicates the attribute should be deleted.
- if (variant->empty()) {
- auto search = attributes_.find(key);
- if (search != attributes_.end())
- attributes_.erase(search);
- } else {
- attributes_[key] = *variant;
- }
-
- // All attribute changes generate a notification, even if the value
- // doesn't change. Visibility attributes set a flag only if the value
- // changes.
- update_flags.Set(display::SurfaceUpdateFlags::AttributesChanged);
- if (visibility_changed)
- update_flags.Set(display::SurfaceUpdateFlags::VisibilityChanged);
- }
- }
-
- SurfaceUpdated(update_flags);
- return {};
-}
-
-void DisplaySurface::SurfaceUpdated(display::SurfaceUpdateFlags update_flags) {
- ALOGD_IF(TRACE,
- "DisplaySurface::SurfaceUpdated: surface_id=%d update_flags=0x%x",
- surface_id(), update_flags.value());
-
- update_flags_.Set(update_flags);
- service()->SurfaceUpdated(surface_type(), update_flags_);
-}
-
-void DisplaySurface::ClearUpdate() {
- ALOGD_IF(TRACE > 1, "DisplaySurface::ClearUpdate: surface_id=%d",
- surface_id());
- update_flags_ = display::SurfaceUpdateFlags::None;
-}
-
-Status<display::SurfaceInfo> DisplaySurface::OnGetSurfaceInfo(
- Message& /*message*/) {
- ALOGD_IF(
- TRACE,
- "DisplaySurface::OnGetSurfaceInfo: surface_id=%d visible=%d z_order=%d",
- surface_id(), visible(), z_order());
- return {{surface_id(), visible(), z_order()}};
-}
-
-Status<void> DisplaySurface::RegisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue) {
- ALOGD_IF(TRACE, "DisplaySurface::RegisterQueue: surface_id=%d queue_id=%d",
- surface_id(), consumer_queue->id());
- // Capture references for the lambda to work around apparent clang bug.
- // TODO(eieio): Figure out if there is a clang bug or C++11 ambiguity when
- // capturing self and consumer_queue by copy in the following case:
- // auto self = Self();
- // [self, consumer_queue](int events) {
- // self->OnQueueEvent(consuemr_queue, events); }
- //
- struct State {
- std::shared_ptr<DisplaySurface> surface;
- std::shared_ptr<ConsumerQueue> queue;
- };
- State state{Self(), consumer_queue};
-
- return service()->AddEventHandler(
- consumer_queue->queue_fd(), EPOLLIN | EPOLLHUP | EPOLLET,
- [state](int events) {
- state.surface->OnQueueEvent(state.queue, events);
- });
-}
-
-Status<void> DisplaySurface::UnregisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue) {
- ALOGD_IF(TRACE, "DisplaySurface::UnregisterQueue: surface_id=%d queue_id=%d",
- surface_id(), consumer_queue->id());
- return service()->RemoveEventHandler(consumer_queue->queue_fd());
-}
-
-void DisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& /*consumer_queue*/, int /*events*/) {
- ALOGE(
- "DisplaySurface::OnQueueEvent: ERROR base virtual method should not be "
- "called!!!");
-}
-
-std::shared_ptr<ConsumerQueue> ApplicationDisplaySurface::GetQueue(
- int32_t queue_id) {
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::GetQueue: surface_id=%d queue_id=%d",
- surface_id(), queue_id);
-
- std::lock_guard<std::mutex> autolock(lock_);
- auto search = consumer_queues_.find(queue_id);
- if (search != consumer_queues_.end())
- return search->second;
- else
- return nullptr;
-}
-
-std::vector<int32_t> ApplicationDisplaySurface::GetQueueIds() const {
- std::lock_guard<std::mutex> autolock(lock_);
- std::vector<int32_t> queue_ids;
- for (const auto& entry : consumer_queues_)
- queue_ids.push_back(entry.first);
- return queue_ids;
-}
-
-Status<LocalChannelHandle> ApplicationDisplaySurface::OnCreateQueue(
- Message& /*message*/, const ProducerQueueConfig& config) {
- ATRACE_NAME("ApplicationDisplaySurface::OnCreateQueue");
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::OnCreateQueue: surface_id=%d, "
- "user_metadata_size=%zu",
- surface_id(), config.user_metadata_size);
-
- std::lock_guard<std::mutex> autolock(lock_);
- auto producer = ProducerQueue::Create(config, UsagePolicy{});
- if (!producer) {
- ALOGE(
- "ApplicationDisplaySurface::OnCreateQueue: Failed to create producer "
- "queue!");
- return ErrorStatus(ENOMEM);
- }
-
- std::shared_ptr<ConsumerQueue> consumer =
- producer->CreateSilentConsumerQueue();
- auto status = RegisterQueue(consumer);
- if (!status) {
- ALOGE(
- "ApplicationDisplaySurface::OnCreateQueue: Failed to register consumer "
- "queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- consumer_queues_[consumer->id()] = std::move(consumer);
-
- SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
- return std::move(producer->GetChannelHandle());
-}
-
-void ApplicationDisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
- ALOGD_IF(TRACE,
- "ApplicationDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
- consumer_queue->id(), events);
-
- std::lock_guard<std::mutex> autolock(lock_);
-
- // Always give the queue a chance to handle its internal bookkeeping.
- consumer_queue->HandleQueueEvents();
-
- // Check for hangup and remove a queue that is no longer needed.
- if (consumer_queue->hung_up()) {
- ALOGD_IF(TRACE, "ApplicationDisplaySurface::OnQueueEvent: Removing queue.");
- UnregisterQueue(consumer_queue);
- auto search = consumer_queues_.find(consumer_queue->id());
- if (search != consumer_queues_.end()) {
- consumer_queues_.erase(search);
- } else {
- ALOGE(
- "ApplicationDisplaySurface::OnQueueEvent: Failed to find queue_id=%d",
- consumer_queue->id());
- }
- SurfaceUpdated(display::SurfaceUpdateFlags::BuffersChanged);
- }
-}
-
-std::vector<int32_t> DirectDisplaySurface::GetQueueIds() const {
- std::lock_guard<std::mutex> autolock(lock_);
- std::vector<int32_t> queue_ids;
- if (direct_queue_)
- queue_ids.push_back(direct_queue_->id());
- return queue_ids;
-}
-
-Status<LocalChannelHandle> DirectDisplaySurface::OnCreateQueue(
- Message& /*message*/, const ProducerQueueConfig& config) {
- ATRACE_NAME("DirectDisplaySurface::OnCreateQueue");
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::OnCreateQueue: surface_id=%d "
- "user_metadata_size=%zu",
- surface_id(), config.user_metadata_size);
-
- std::lock_guard<std::mutex> autolock(lock_);
- if (!direct_queue_) {
- // Inject the hw composer usage flag to enable the display to read the
- // buffers.
- auto producer = ProducerQueue::Create(
- config, UsagePolicy{GraphicBuffer::USAGE_HW_COMPOSER, 0, 0, 0});
- if (!producer) {
- ALOGE(
- "DirectDisplaySurface::OnCreateQueue: Failed to create producer "
- "queue!");
- return ErrorStatus(ENOMEM);
- }
-
- direct_queue_ = producer->CreateConsumerQueue();
- if (direct_queue_->metadata_size() > 0) {
- metadata_.reset(new uint8_t[direct_queue_->metadata_size()]);
- }
- auto status = RegisterQueue(direct_queue_);
- if (!status) {
- ALOGE(
- "DirectDisplaySurface::OnCreateQueue: Failed to register consumer "
- "queue: %s",
- status.GetErrorMessage().c_str());
- return status.error_status();
- }
-
- return std::move(producer->GetChannelHandle());
- } else {
- return ErrorStatus(EALREADY);
- }
-}
-
-void DirectDisplaySurface::OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events) {
- ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: queue_id=%d events=%x",
- consumer_queue->id(), events);
-
- std::lock_guard<std::mutex> autolock(lock_);
-
- // Always give the queue a chance to handle its internal bookkeeping.
- consumer_queue->HandleQueueEvents();
-
- // Check for hangup and remove a queue that is no longer needed.
- if (consumer_queue->hung_up()) {
- ALOGD_IF(TRACE, "DirectDisplaySurface::OnQueueEvent: Removing queue.");
- UnregisterQueue(consumer_queue);
- direct_queue_ = nullptr;
- }
-}
-
-void DirectDisplaySurface::DequeueBuffersLocked() {
- if (direct_queue_ == nullptr) {
- ALOGE(
- "DirectDisplaySurface::DequeueBuffersLocked: Consumer queue is not "
- "initialized.");
- return;
- }
-
- while (true) {
- LocalHandle acquire_fence;
- size_t slot;
- auto buffer_status = direct_queue_->Dequeue(
- 0, &slot, metadata_.get(),
- direct_queue_->metadata_size(), &acquire_fence);
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::DequeueBuffersLocked: Dequeue with metadata_size: %zu",
- direct_queue_->metadata_size());
- if (!buffer_status) {
- ALOGD_IF(
- TRACE > 1 && buffer_status.error() == ETIMEDOUT,
- "DirectDisplaySurface::DequeueBuffersLocked: All buffers dequeued.");
- ALOGE_IF(buffer_status.error() != ETIMEDOUT,
- "DirectDisplaySurface::DequeueBuffersLocked: Failed to dequeue "
- "buffer: %s",
- buffer_status.GetErrorMessage().c_str());
- return;
- }
- auto buffer_consumer = buffer_status.take();
-
- if (!visible()) {
- ATRACE_NAME("DropFrameOnInvisibleSurface");
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::DequeueBuffersLocked: Discarding "
- "buffer_id=%d on invisible surface.",
- buffer_consumer->id());
- buffer_consumer->Discard();
- continue;
- }
-
- if (acquired_buffers_.IsFull()) {
- ALOGE(
- "DirectDisplaySurface::DequeueBuffersLocked: Posted buffers full, "
- "overwriting.");
- acquired_buffers_.PopBack();
- }
-
- acquired_buffers_.Append(
- AcquiredBuffer(buffer_consumer, std::move(acquire_fence), slot));
- }
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireCurrentBuffer() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- if (acquired_buffers_.IsEmpty()) {
- ALOGE(
- "DirectDisplaySurface::AcquireCurrentBuffer: attempt to acquire buffer "
- "when none are posted.");
- return AcquiredBuffer();
- }
- AcquiredBuffer buffer = std::move(acquired_buffers_.Front());
- acquired_buffers_.PopFront();
- ALOGD_IF(TRACE, "DirectDisplaySurface::AcquireCurrentBuffer: buffer_id=%d",
- buffer.buffer()->id());
- return buffer;
-}
-
-AcquiredBuffer DirectDisplaySurface::AcquireNewestAvailableBuffer(
- AcquiredBuffer* skipped_buffer) {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- AcquiredBuffer buffer;
- int frames = 0;
- // Basic latency stopgap for when the application misses a frame:
- // If the application recovers on the 2nd or 3rd (etc) frame after
- // missing, this code will skip frames to catch up by checking if
- // the next frame is also available.
- while (!acquired_buffers_.IsEmpty() &&
- acquired_buffers_.Front().IsAvailable()) {
- // Capture the skipped buffer into the result parameter.
- // Note that this API only supports skipping one buffer per vsync.
- if (frames > 0 && skipped_buffer)
- *skipped_buffer = std::move(buffer);
- ++frames;
- buffer = std::move(acquired_buffers_.Front());
- acquired_buffers_.PopFront();
- if (frames == 2)
- break;
- }
- ALOGD_IF(TRACE,
- "DirectDisplaySurface::AcquireNewestAvailableBuffer: buffer_id=%d",
- buffer.buffer()->id());
- return buffer;
-}
-
-bool DirectDisplaySurface::IsBufferAvailable() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- return !acquired_buffers_.IsEmpty() &&
- acquired_buffers_.Front().IsAvailable();
-}
-
-bool DirectDisplaySurface::IsBufferPosted() {
- std::lock_guard<std::mutex> autolock(lock_);
- DequeueBuffersLocked();
-
- return !acquired_buffers_.IsEmpty();
-}
-
-Status<std::shared_ptr<DisplaySurface>> DisplaySurface::Create(
- DisplayService* service, int surface_id, int process_id, int user_id,
- const display::SurfaceAttributes& attributes) {
- bool direct = false;
- auto search = attributes.find(display::SurfaceAttribute::Direct);
- if (search != attributes.end()) {
- if (!IfAnyOf<int32_t, int64_t, bool, float>::Get(&search->second,
- &direct)) {
- ALOGE(
- "DisplaySurface::Create: Invalid type for SurfaceAttribute::Direct!");
- return ErrorStatus(EINVAL);
- }
- }
-
- ALOGD_IF(TRACE,
- "DisplaySurface::Create: surface_id=%d process_id=%d user_id=%d "
- "direct=%d",
- surface_id, process_id, user_id, direct);
-
- if (direct) {
- const bool trusted = user_id == AID_ROOT || IsTrustedUid(user_id);
- if (trusted) {
- return {std::shared_ptr<DisplaySurface>{
- new DirectDisplaySurface(service, surface_id, process_id, user_id)}};
- } else {
- ALOGE(
- "DisplaySurface::Create: Direct surfaces may only be created by "
- "trusted UIDs: user_id=%d",
- user_id);
- return ErrorStatus(EPERM);
- }
- } else {
- return {std::shared_ptr<DisplaySurface>{new ApplicationDisplaySurface(
- service, surface_id, process_id, user_id)}};
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/display_surface.h b/libs/vr/libvrflinger/display_surface.h
deleted file mode 100644
index c8b1a07..0000000
--- a/libs/vr/libvrflinger/display_surface.h
+++ /dev/null
@@ -1,188 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
-
-#include <pdx/file_handle.h>
-#include <pdx/service.h>
-#include <private/dvr/buffer_hub_queue_client.h>
-#include <private/dvr/display_protocol.h>
-#include <private/dvr/ring_buffer.h>
-
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "acquired_buffer.h"
-
-namespace android {
-namespace dvr {
-
-class DisplayService;
-
-enum class SurfaceType {
- Direct,
- Application,
-};
-
-class DisplaySurface : public pdx::Channel {
- public:
- static pdx::Status<std::shared_ptr<DisplaySurface>> Create(
- DisplayService* service, int surface_id, int process_id, int user_id,
- const display::SurfaceAttributes& attributes);
-
- ~DisplaySurface() override;
-
- DisplayService* service() const { return service_; }
- SurfaceType surface_type() const { return surface_type_; }
- int surface_id() const { return surface_id_; }
- int process_id() const { return process_id_; }
- int user_id() const { return user_id_; }
-
- bool visible() const { return visible_; }
- int z_order() const { return z_order_; }
-
- const display::SurfaceAttributes& attributes() const { return attributes_; }
- display::SurfaceUpdateFlags update_flags() const { return update_flags_; }
-
- virtual std::vector<int32_t> GetQueueIds() const { return {}; }
-
- bool IsUpdatePending() const {
- return update_flags_.value() != display::SurfaceUpdateFlags::None;
- }
-
- protected:
- DisplaySurface(DisplayService* service, SurfaceType surface_type,
- int surface_id, int process_id, int user_id);
-
- // Utility to retrieve a shared pointer to this channel as the desired derived
- // type.
- template <
- typename T = DisplaySurface,
- typename = std::enable_if_t<std::is_base_of<DisplaySurface, T>::value>>
- std::shared_ptr<T> Self() {
- return std::static_pointer_cast<T>(shared_from_this());
- }
-
- virtual pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) = 0;
-
- // Registers a consumer queue with the event dispatcher in DisplayService. The
- // OnQueueEvent callback below is called to handle queue events.
- pdx::Status<void> RegisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
- pdx::Status<void> UnregisterQueue(
- const std::shared_ptr<ConsumerQueue>& consumer_queue);
-
- // Called by the event dispatcher in DisplayService when a registered queue
- // event triggers. Executes on the event dispatcher thread.
- virtual void OnQueueEvent(
- const std::shared_ptr<ConsumerQueue>& consumer_queue, int events);
-
- void SurfaceUpdated(display::SurfaceUpdateFlags update_flags);
- void ClearUpdate();
-
- // Synchronizes access to mutable state below between message dispatch thread
- // and frame post thread.
- mutable std::mutex lock_;
-
- private:
- friend class DisplayService;
- friend class DisplayManagerService;
-
- // Dispatches display surface messages to the appropriate handlers. This
- // handler runs on the VrFlinger message dispatch thread.
- pdx::Status<void> HandleMessage(pdx::Message& message);
-
- pdx::Status<void> OnSetAttributes(
- pdx::Message& message, const display::SurfaceAttributes& attributes);
- pdx::Status<display::SurfaceInfo> OnGetSurfaceInfo(pdx::Message& message);
-
- DisplayService* service_;
- SurfaceType surface_type_;
- int surface_id_;
- int process_id_;
- int user_id_;
-
- display::SurfaceAttributes attributes_;
- display::SurfaceUpdateFlags update_flags_ = display::SurfaceUpdateFlags::None;
-
- // Subset of attributes that may be interpreted by the display service.
- bool visible_ = false;
- int z_order_ = 0;
-
- DisplaySurface(const DisplaySurface&) = delete;
- void operator=(const DisplaySurface&) = delete;
-};
-
-class ApplicationDisplaySurface : public DisplaySurface {
- public:
- ApplicationDisplaySurface(DisplayService* service, int surface_id,
- int process_id, int user_id)
- : DisplaySurface(service, SurfaceType::Application, surface_id,
- process_id, user_id) {}
-
- std::shared_ptr<ConsumerQueue> GetQueue(int32_t queue_id);
- std::vector<int32_t> GetQueueIds() const override;
-
- private:
- pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) override;
- void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
- int events) override;
-
- // Accessed by both message dispatch thread and epoll event thread.
- std::unordered_map<int32_t, std::shared_ptr<ConsumerQueue>> consumer_queues_;
-};
-
-class DirectDisplaySurface : public DisplaySurface {
- public:
- DirectDisplaySurface(DisplayService* service, int surface_id, int process_id,
- int user_id)
- : DisplaySurface(service, SurfaceType::Direct, surface_id, process_id,
- user_id),
- acquired_buffers_(kMaxPostedBuffers),
- metadata_(nullptr) {}
- std::vector<int32_t> GetQueueIds() const override;
- bool IsBufferAvailable();
- bool IsBufferPosted();
- AcquiredBuffer AcquireCurrentBuffer();
-
- // Get the newest buffer. Up to one buffer will be skipped. If a buffer is
- // skipped, it will be stored in skipped_buffer if non null.
- AcquiredBuffer AcquireNewestAvailableBuffer(AcquiredBuffer* skipped_buffer);
-
- private:
- pdx::Status<pdx::LocalChannelHandle> OnCreateQueue(
- pdx::Message& message, const ProducerQueueConfig& config) override;
- void OnQueueEvent(const std::shared_ptr<ConsumerQueue>& consumer_queue,
- int events) override;
-
- // The capacity of the pending buffer queue. Should be enough to hold all the
- // buffers of this DisplaySurface, although in practice only 1 or 2 frames
- // will be pending at a time.
- static constexpr int kSurfaceBufferMaxCount = 4;
- static constexpr int kSurfaceViewMaxCount = 4;
- static constexpr int kMaxPostedBuffers =
- kSurfaceBufferMaxCount * kSurfaceViewMaxCount;
-
- // Returns whether a frame is available without locking the mutex.
- bool IsFrameAvailableNoLock() const;
-
- // Dequeue all available buffers from the consumer queue.
- void DequeueBuffersLocked();
-
- // In a triple-buffered surface, up to kMaxPostedBuffers buffers may be
- // posted and pending.
- RingBuffer<AcquiredBuffer> acquired_buffers_;
-
- std::shared_ptr<ConsumerQueue> direct_queue_;
-
- // Stores metadata when it dequeue buffers from consumer queue.
- std::unique_ptr<uint8_t[]> metadata_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_DISPLAY_SURFACE_H_
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
deleted file mode 100644
index 0d5eb80..0000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "epoll_event_dispatcher.h"
-
-#include <log/log.h>
-#include <sys/epoll.h>
-#include <sys/eventfd.h>
-#include <sys/prctl.h>
-
-#include <dvr/performance_client_api.h>
-
-namespace android {
-namespace dvr {
-
-EpollEventDispatcher::EpollEventDispatcher() {
- epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC));
- if (!epoll_fd_) {
- ALOGE("Failed to create epoll fd: %s", strerror(errno));
- return;
- }
-
- event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
- if (!event_fd_) {
- ALOGE("Failed to create event for epolling: %s", strerror(errno));
- return;
- }
-
- // Add watch for eventfd. This should only watch for EPOLLIN, which gets set
- // when eventfd_write occurs. Use "this" as a unique sentinal value to
- // identify events from the event fd.
- epoll_event event = {.events = EPOLLIN, .data = {.ptr = this}};
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, event_fd_.Get(), &event) < 0) {
- ALOGE("Failed to add eventfd to epoll set because: %s", strerror(errno));
- return;
- }
-
- thread_ = std::thread(&EpollEventDispatcher::EventThread, this);
-}
-
-EpollEventDispatcher::~EpollEventDispatcher() { Stop(); }
-
-void EpollEventDispatcher::Stop() {
- exit_thread_.store(true);
- eventfd_write(event_fd_.Get(), 1);
-}
-
-pdx::Status<void> EpollEventDispatcher::AddEventHandler(int fd, int event_mask,
- Handler handler) {
- std::lock_guard<std::mutex> lock(lock_);
-
- epoll_event event;
- event.events = event_mask;
- event.data.ptr = &(handlers_[fd] = handler);
-
- ALOGD_IF(
- TRACE,
- "EpollEventDispatcher::AddEventHandler: fd=%d event_mask=0x%x handler=%p",
- fd, event_mask, event.data.ptr);
-
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_ADD, fd, &event) < 0) {
- const int error = errno;
- ALOGE("Failed to add fd to epoll set because: %s", strerror(error));
- return pdx::ErrorStatus(error);
- } else {
- return {};
- }
-}
-
-pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) {
- ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd);
- std::lock_guard<std::mutex> lock(lock_);
-
- epoll_event ee; // See BUGS in man 2 epoll_ctl.
- if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) {
- const int error = errno;
- ALOGE("Failed to remove fd from epoll set because: %s", strerror(error));
- return pdx::ErrorStatus(error);
- }
-
- // If the fd was valid above, add it to the list of ids to remove.
- removed_handlers_.push_back(fd);
-
- // Wake up the event thread to clean up.
- eventfd_write(event_fd_.Get(), 1);
-
- return {};
-}
-
-void EpollEventDispatcher::EventThread() {
- prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrEvent"), 0, 0, 0);
-
- const int error = dvrSetSchedulerClass(0, "graphics");
- LOG_ALWAYS_FATAL_IF(
- error < 0,
- "EpollEventDispatcher::EventThread: Failed to set scheduler class: %s",
- strerror(-error));
-
- const size_t kMaxNumEvents = 128;
- epoll_event events[kMaxNumEvents];
-
- while (!exit_thread_.load()) {
- const int num_events = epoll_wait(epoll_fd_.Get(), events, kMaxNumEvents, -1);
- if (num_events < 0 && errno != EINTR)
- break;
-
- ALOGD_IF(TRACE > 1, "EpollEventDispatcher::EventThread: num_events=%d",
- num_events);
-
- for (int i = 0; i < num_events; i++) {
- ALOGD_IF(
- TRACE > 1,
- "EpollEventDispatcher::EventThread: event %d: handler=%p events=0x%x",
- i, events[i].data.ptr, events[i].events);
-
- if (events[i].data.ptr == this) {
- // Clear pending event on event_fd_. Serialize the read with respect to
- // writes from other threads.
- std::lock_guard<std::mutex> lock(lock_);
- eventfd_t value;
- eventfd_read(event_fd_.Get(), &value);
- } else {
- auto handler = reinterpret_cast<Handler*>(events[i].data.ptr);
- if (handler)
- (*handler)(events[i].events);
- }
- }
-
- // Remove any handlers that have been posted for removal. This is done here
- // instead of in RemoveEventHandler() to prevent races between the dispatch
- // thread and the code requesting the removal. Handlers are guaranteed to
- // stay alive between exiting epoll_wait() and the dispatch loop above.
- std::lock_guard<std::mutex> lock(lock_);
- for (auto handler_fd : removed_handlers_) {
- ALOGD_IF(TRACE,
- "EpollEventDispatcher::EventThread: removing handler: fd=%d",
- handler_fd);
- handlers_.erase(handler_fd);
- }
- removed_handlers_.clear();
- }
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.h b/libs/vr/libvrflinger/epoll_event_dispatcher.h
deleted file mode 100644
index eb687f4..0000000
--- a/libs/vr/libvrflinger/epoll_event_dispatcher.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
-
-#include <sys/epoll.h>
-
-#include <atomic>
-#include <functional>
-#include <mutex>
-#include <thread>
-#include <unordered_map>
-#include <vector>
-
-#include <pdx/file_handle.h>
-#include <pdx/status.h>
-
-namespace android {
-namespace dvr {
-
-class EpollEventDispatcher {
- public:
- // Function type for event handlers. The handler receives a bitmask of the
- // epoll events that occurred on the file descriptor associated with the
- // handler.
- using Handler = std::function<void(int)>;
-
- EpollEventDispatcher();
- ~EpollEventDispatcher();
-
- // |handler| is called on the internal dispatch thread when |fd| is signaled
- // by events in |event_mask|.
- pdx::Status<void> AddEventHandler(int fd, int event_mask, Handler handler);
- pdx::Status<void> RemoveEventHandler(int fd);
-
- void Stop();
-
- private:
- void EventThread();
-
- std::thread thread_;
- std::atomic<bool> exit_thread_{false};
-
- // Protects handlers_ and removed_handlers_ and serializes operations on
- // epoll_fd_ and event_fd_.
- std::mutex lock_;
-
- // Maintains a map of fds to event handlers. This is primarily to keep any
- // references alive that may be bound in the std::function instances. It is
- // not used at dispatch time to avoid performance problems with different
- // versions of std::unordered_map.
- std::unordered_map<int, Handler> handlers_;
-
- // List of fds to be removed from the map. The actual removal is performed
- // by the event dispatch thread to avoid races.
- std::vector<int> removed_handlers_;
-
- pdx::LocalHandle epoll_fd_;
- pdx::LocalHandle event_fd_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_EPOLL_EVENT_DISPATCHER_H_
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
deleted file mode 100644
index 70f303b..0000000
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ /dev/null
@@ -1,1541 +0,0 @@
-#include "hardware_composer.h"
-
-#include <binder/IServiceManager.h>
-#include <cutils/properties.h>
-#include <cutils/sched_policy.h>
-#include <fcntl.h>
-#include <log/log.h>
-#include <poll.h>
-#include <stdint.h>
-#include <sync/sync.h>
-#include <sys/eventfd.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-#include <sys/system_properties.h>
-#include <sys/timerfd.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <chrono>
-#include <functional>
-#include <map>
-#include <sstream>
-#include <string>
-#include <tuple>
-
-#include <dvr/dvr_display_types.h>
-#include <dvr/performance_client_api.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/ion_buffer.h>
-
-using android::hardware::Return;
-using android::hardware::Void;
-using android::pdx::ErrorStatus;
-using android::pdx::LocalHandle;
-using android::pdx::Status;
-using android::pdx::rpc::EmptyVariant;
-using android::pdx::rpc::IfAnyOf;
-
-using namespace std::chrono_literals;
-
-namespace android {
-namespace dvr {
-
-namespace {
-
-const char kDvrPerformanceProperty[] = "sys.dvr.performance";
-
-const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
-
-// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
-// events. Name ours similarly.
-const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
-
-// How long to wait after boot finishes before we turn the display off.
-constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
-
-constexpr int kDefaultDisplayWidth = 1920;
-constexpr int kDefaultDisplayHeight = 1080;
-constexpr int64_t kDefaultVsyncPeriodNs = 16666667;
-// Hardware composer reports dpi as dots per thousand inches (dpi * 1000).
-constexpr int kDefaultDpi = 400000;
-
-// Get time offset from a vsync to when the pose for that vsync should be
-// predicted out to. For example, if scanout gets halfway through the frame
-// at the halfway point between vsyncs, then this could be half the period.
-// With global shutter displays, this should be changed to the offset to when
-// illumination begins. Low persistence adds a frame of latency, so we predict
-// to the center of the next frame.
-inline int64_t GetPosePredictionTimeOffset(int64_t vsync_period_ns) {
- return (vsync_period_ns * 150) / 100;
-}
-
-// Attempts to set the scheduler class and partiton for the current thread.
-// Returns true on success or false on failure.
-bool SetThreadPolicy(const std::string& scheduler_class,
- const std::string& partition) {
- int error = dvrSetSchedulerClass(0, scheduler_class.c_str());
- if (error < 0) {
- ALOGE(
- "SetThreadPolicy: Failed to set scheduler class \"%s\" for "
- "thread_id=%d: %s",
- scheduler_class.c_str(), gettid(), strerror(-error));
- return false;
- }
- error = dvrSetCpuPartition(0, partition.c_str());
- if (error < 0) {
- ALOGE(
- "SetThreadPolicy: Failed to set cpu partiton \"%s\" for thread_id=%d: "
- "%s",
- partition.c_str(), gettid(), strerror(-error));
- return false;
- }
- return true;
-}
-
-// Utility to generate scoped tracers with arguments.
-// TODO(eieio): Move/merge this into utils/Trace.h?
-class TraceArgs {
- public:
- template <typename... Args>
- explicit TraceArgs(const char* format, Args&&... args) {
- std::array<char, 1024> buffer;
- snprintf(buffer.data(), buffer.size(), format, std::forward<Args>(args)...);
- atrace_begin(ATRACE_TAG, buffer.data());
- }
-
- ~TraceArgs() { atrace_end(ATRACE_TAG); }
-
- private:
- TraceArgs(const TraceArgs&) = delete;
- void operator=(const TraceArgs&) = delete;
-};
-
-// Macro to define a scoped tracer with arguments. Uses PASTE(x, y) macro
-// defined in utils/Trace.h.
-#define TRACE_FORMAT(format, ...) \
- TraceArgs PASTE(__tracer, __LINE__) { format, ##__VA_ARGS__ }
-
-// Returns "primary" or "external". Useful for writing more readable logs.
-const char* GetDisplayName(bool is_primary) {
- return is_primary ? "primary" : "external";
-}
-
-} // anonymous namespace
-
-HardwareComposer::HardwareComposer()
- : initialized_(false), request_display_callback_(nullptr) {}
-
-HardwareComposer::~HardwareComposer(void) {
- UpdatePostThreadState(PostThreadState::Quit, true);
- if (post_thread_.joinable())
- post_thread_.join();
- composer_callback_->SetVsyncService(nullptr);
-}
-
-void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
- hwc2_display_t hw_id) {
- const auto error = composer->getDisplayIdentificationData(
- hw_id, &display_port_, &display_identification_data_);
- if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
- if (error !=
- android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
- ALOGI("hardware_composer: identification data error\n");
- } else {
- ALOGI("hardware_composer: identification data unsupported\n");
- }
- }
-}
-
-bool HardwareComposer::Initialize(
- Hwc2::Composer* composer, hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- if (initialized_) {
- ALOGE("HardwareComposer::Initialize: already initialized.");
- return false;
- }
-
- request_display_callback_ = request_display_callback;
-
- primary_display_ = GetDisplayParams(composer, primary_display_id, true);
-
- vsync_service_ = new VsyncService;
- sp<IServiceManager> sm(defaultServiceManager());
- auto result = sm->addService(String16(VsyncService::GetServiceName()),
- vsync_service_, false);
- LOG_ALWAYS_FATAL_IF(result != android::OK,
- "addService(%s) failed", VsyncService::GetServiceName());
-
- post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
- LOG_ALWAYS_FATAL_IF(
- !post_thread_event_fd_,
- "HardwareComposer: Failed to create interrupt event fd : %s",
- strerror(errno));
-
- UpdateEdidData(composer, primary_display_id);
-
- post_thread_ = std::thread(&HardwareComposer::PostThread, this);
-
- initialized_ = true;
-
- return initialized_;
-}
-
-void HardwareComposer::Enable() {
- UpdatePostThreadState(PostThreadState::Suspended, false);
-}
-
-void HardwareComposer::Disable() {
- UpdatePostThreadState(PostThreadState::Suspended, true);
-
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- post_thread_ready_.wait(lock, [this] {
- return !post_thread_resumed_;
- });
-}
-
-void HardwareComposer::OnBootFinished() {
- std::lock_guard<std::mutex> lock(post_thread_mutex_);
- if (boot_finished_)
- return;
- boot_finished_ = true;
- post_thread_wait_.notify_one();
-}
-
-// Update the post thread quiescent state based on idle and suspended inputs.
-void HardwareComposer::UpdatePostThreadState(PostThreadStateType state,
- bool suspend) {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
- // Update the votes in the state variable before evaluating the effective
- // quiescent state. Any bits set in post_thread_state_ indicate that the post
- // thread should be suspended.
- if (suspend) {
- post_thread_state_ |= state;
- } else {
- post_thread_state_ &= ~state;
- }
-
- const bool quit = post_thread_state_ & PostThreadState::Quit;
- const bool effective_suspend = post_thread_state_ != PostThreadState::Active;
- if (quit) {
- post_thread_quiescent_ = true;
- eventfd_write(post_thread_event_fd_.Get(), 1);
- post_thread_wait_.notify_one();
- } else if (effective_suspend && !post_thread_quiescent_) {
- post_thread_quiescent_ = true;
- eventfd_write(post_thread_event_fd_.Get(), 1);
- } else if (!effective_suspend && post_thread_quiescent_) {
- post_thread_quiescent_ = false;
- eventfd_t value;
- eventfd_read(post_thread_event_fd_.Get(), &value);
- post_thread_wait_.notify_one();
- }
-}
-
-void HardwareComposer::CreateComposer() {
- if (composer_)
- return;
- composer_.reset(new Hwc2::impl::Composer("default"));
- composer_callback_ = new ComposerCallback;
- composer_->registerCallback(composer_callback_);
- LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
- "Registered composer callback but didn't get hotplug for primary"
- " display");
- composer_callback_->SetVsyncService(vsync_service_);
-}
-
-void HardwareComposer::OnPostThreadResumed() {
- ALOGI("OnPostThreadResumed");
- EnableDisplay(*target_display_, true);
-
- // Trigger target-specific performance mode change.
- property_set(kDvrPerformanceProperty, "performance");
-}
-
-void HardwareComposer::OnPostThreadPaused() {
- ALOGI("OnPostThreadPaused");
- retire_fence_fds_.clear();
- layers_.clear();
-
- // Phones create a new composer client on resume and destroy it on pause.
- if (composer_callback_ != nullptr) {
- composer_callback_->SetVsyncService(nullptr);
- composer_callback_ = nullptr;
- }
- composer_.reset(nullptr);
-
- // Trigger target-specific performance mode change.
- property_set(kDvrPerformanceProperty, "idle");
-}
-
-bool HardwareComposer::PostThreadCondWait(std::unique_lock<std::mutex>& lock,
- int timeout_sec,
- const std::function<bool()>& pred) {
- auto pred_with_quit = [&] {
- return pred() || (post_thread_state_ & PostThreadState::Quit);
- };
- if (timeout_sec >= 0) {
- post_thread_wait_.wait_for(lock, std::chrono::seconds(timeout_sec),
- pred_with_quit);
- } else {
- post_thread_wait_.wait(lock, pred_with_quit);
- }
- if (post_thread_state_ & PostThreadState::Quit) {
- ALOGI("HardwareComposer::PostThread: Quitting.");
- return true;
- }
- return false;
-}
-
-HWC::Error HardwareComposer::Validate(hwc2_display_t display) {
- uint32_t num_types;
- uint32_t num_requests;
- HWC::Error error =
- composer_->validateDisplay(display, &num_types, &num_requests);
-
- if (error == HWC2_ERROR_HAS_CHANGES) {
- ALOGE("Hardware composer has requested composition changes, "
- "which we don't support.");
- // Accept the changes anyway and see if we can get something on the screen.
- error = composer_->acceptDisplayChanges(display);
- }
-
- return error;
-}
-
-bool HardwareComposer::EnableVsync(const DisplayParams& display, bool enabled) {
- HWC::Error error = composer_->setVsyncEnabled(display.id,
- (Hwc2::IComposerClient::Vsync)(enabled ? HWC2_VSYNC_ENABLE
- : HWC2_VSYNC_DISABLE));
- if (error != HWC::Error::None) {
- ALOGE("Error attempting to %s vsync on %s display: %s",
- enabled ? "enable" : "disable", GetDisplayName(display.is_primary),
- error.to_string().c_str());
- }
- return error == HWC::Error::None;
-}
-
-bool HardwareComposer::SetPowerMode(const DisplayParams& display, bool active) {
- ALOGI("Turning %s display %s", GetDisplayName(display.is_primary),
- active ? "on" : "off");
- HWC::PowerMode power_mode = active ? HWC::PowerMode::On : HWC::PowerMode::Off;
- HWC::Error error = composer_->setPowerMode(display.id,
- power_mode.cast<Hwc2::IComposerClient::PowerMode>());
- if (error != HWC::Error::None) {
- ALOGE("Error attempting to turn %s display %s: %s",
- GetDisplayName(display.is_primary), active ? "on" : "off",
- error.to_string().c_str());
- }
- return error == HWC::Error::None;
-}
-
-bool HardwareComposer::EnableDisplay(const DisplayParams& display,
- bool enabled) {
- bool power_result;
- bool vsync_result;
- // When turning a display on, we set the power state then set vsync. When
- // turning a display off we do it in the opposite order.
- if (enabled) {
- power_result = SetPowerMode(display, enabled);
- vsync_result = EnableVsync(display, enabled);
- } else {
- vsync_result = EnableVsync(display, enabled);
- power_result = SetPowerMode(display, enabled);
- }
- return power_result && vsync_result;
-}
-
-HWC::Error HardwareComposer::Present(hwc2_display_t display) {
- int32_t present_fence;
- HWC::Error error = composer_->presentDisplay(display, &present_fence);
-
- // According to the documentation, this fence is signaled at the time of
- // vsync/DMA for physical displays.
- if (error == HWC::Error::None) {
- retire_fence_fds_.emplace_back(present_fence);
- } else {
- ATRACE_INT("HardwareComposer: PresentResult", error);
- }
-
- return error;
-}
-
-DisplayParams HardwareComposer::GetDisplayParams(
- Hwc2::Composer* composer, hwc2_display_t display, bool is_primary) {
- DisplayParams params;
- params.id = display;
- params.is_primary = is_primary;
-
- Hwc2::Config config;
- HWC::Error error = composer->getActiveConfig(display, &config);
-
- if (error == HWC::Error::None) {
- auto get_attr = [&](hwc2_attribute_t attr, const char* attr_name)
- -> std::optional<int32_t> {
- int32_t val;
- HWC::Error error = composer->getDisplayAttribute(
- display, config, (Hwc2::IComposerClient::Attribute)attr, &val);
- if (error != HWC::Error::None) {
- ALOGE("Failed to get %s display attr %s: %s",
- GetDisplayName(is_primary), attr_name,
- error.to_string().c_str());
- return std::nullopt;
- }
- return val;
- };
-
- auto width = get_attr(HWC2_ATTRIBUTE_WIDTH, "width");
- auto height = get_attr(HWC2_ATTRIBUTE_HEIGHT, "height");
-
- if (width && height) {
- params.width = *width;
- params.height = *height;
- } else {
- ALOGI("Failed to get width and/or height for %s display. Using default"
- " size %dx%d.", GetDisplayName(is_primary), kDefaultDisplayWidth,
- kDefaultDisplayHeight);
- params.width = kDefaultDisplayWidth;
- params.height = kDefaultDisplayHeight;
- }
-
- auto vsync_period = get_attr(HWC2_ATTRIBUTE_VSYNC_PERIOD, "vsync period");
- if (vsync_period) {
- params.vsync_period_ns = *vsync_period;
- } else {
- ALOGI("Failed to get vsync period for %s display. Using default vsync"
- " period %.2fms", GetDisplayName(is_primary),
- static_cast<float>(kDefaultVsyncPeriodNs) / 1000000);
- params.vsync_period_ns = kDefaultVsyncPeriodNs;
- }
-
- auto dpi_x = get_attr(HWC2_ATTRIBUTE_DPI_X, "DPI X");
- auto dpi_y = get_attr(HWC2_ATTRIBUTE_DPI_Y, "DPI Y");
- if (dpi_x && dpi_y) {
- params.dpi.x = *dpi_x;
- params.dpi.y = *dpi_y;
- } else {
- ALOGI("Failed to get dpi_x and/or dpi_y for %s display. Using default"
- " dpi %d.", GetDisplayName(is_primary), kDefaultDpi);
- params.dpi.x = kDefaultDpi;
- params.dpi.y = kDefaultDpi;
- }
- } else {
- ALOGE("HardwareComposer: Failed to get current %s display config: %d."
- " Using default display values.",
- GetDisplayName(is_primary), error.value);
- params.width = kDefaultDisplayWidth;
- params.height = kDefaultDisplayHeight;
- params.dpi.x = kDefaultDpi;
- params.dpi.y = kDefaultDpi;
- params.vsync_period_ns = kDefaultVsyncPeriodNs;
- }
-
- ALOGI(
- "HardwareComposer: %s display attributes: width=%d height=%d "
- "vsync_period_ns=%d DPI=%dx%d",
- GetDisplayName(is_primary),
- params.width,
- params.height,
- params.vsync_period_ns,
- params.dpi.x,
- params.dpi.y);
-
- return params;
-}
-
-std::string HardwareComposer::Dump() {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- std::ostringstream stream;
-
- auto print_display_metrics = [&](const DisplayParams& params) {
- stream << GetDisplayName(params.is_primary)
- << " display metrics: " << params.width << "x"
- << params.height << " " << (params.dpi.x / 1000.0)
- << "x" << (params.dpi.y / 1000.0) << " dpi @ "
- << (1000000000.0 / params.vsync_period_ns) << " Hz"
- << std::endl;
- };
-
- print_display_metrics(primary_display_);
- if (external_display_)
- print_display_metrics(*external_display_);
-
- stream << "Post thread resumed: " << post_thread_resumed_ << std::endl;
- stream << "Active layers: " << layers_.size() << std::endl;
- stream << std::endl;
-
- for (size_t i = 0; i < layers_.size(); i++) {
- stream << "Layer " << i << ":";
- stream << " type=" << layers_[i].GetCompositionType().to_string();
- stream << " surface_id=" << layers_[i].GetSurfaceId();
- stream << " buffer_id=" << layers_[i].GetBufferId();
- stream << std::endl;
- }
- stream << std::endl;
-
- if (post_thread_resumed_) {
- stream << "Hardware Composer Debug Info:" << std::endl;
- stream << composer_->dumpDebugInfo();
- }
-
- return stream.str();
-}
-
-void HardwareComposer::PostLayers(hwc2_display_t display) {
- ATRACE_NAME("HardwareComposer::PostLayers");
-
- // Setup the hardware composer layers with current buffers.
- for (auto& layer : layers_) {
- layer.Prepare();
- }
-
- // Now that we have taken in a frame from the application, we have a chance
- // to drop the frame before passing the frame along to HWC.
- // If the display driver has become backed up, we detect it here and then
- // react by skipping this frame to catch up latency.
- while (!retire_fence_fds_.empty() &&
- (!retire_fence_fds_.front() ||
- sync_wait(retire_fence_fds_.front().Get(), 0) == 0)) {
- // There are only 2 fences in here, no performance problem to shift the
- // array of ints.
- retire_fence_fds_.erase(retire_fence_fds_.begin());
- }
-
- const bool is_fence_pending = static_cast<int32_t>(retire_fence_fds_.size()) >
- post_thread_config_.allowed_pending_fence_count;
-
- if (is_fence_pending) {
- ATRACE_INT("frame_skip_count", ++frame_skip_count_);
-
- ALOGW_IF(is_fence_pending,
- "Warning: dropping a frame to catch up with HWC (pending = %zd)",
- retire_fence_fds_.size());
-
- for (auto& layer : layers_) {
- layer.Drop();
- }
- return;
- } else {
- // Make the transition more obvious in systrace when the frame skip happens
- // above.
- ATRACE_INT("frame_skip_count", 0);
- }
-
-#if TRACE > 1
- for (size_t i = 0; i < layers_.size(); i++) {
- ALOGI("HardwareComposer::PostLayers: layer=%zu buffer_id=%d composition=%s",
- i, layers_[i].GetBufferId(),
- layers_[i].GetCompositionType().to_string().c_str());
- }
-#endif
-
- HWC::Error error = Validate(display);
- if (error != HWC::Error::None) {
- ALOGE("HardwareComposer::PostLayers: Validate failed: %s display=%" PRIu64,
- error.to_string().c_str(), display);
- return;
- }
-
- error = Present(display);
- if (error != HWC::Error::None) {
- ALOGE("HardwareComposer::PostLayers: Present failed: %s",
- error.to_string().c_str());
- return;
- }
-
- std::vector<Hwc2::Layer> out_layers;
- std::vector<int> out_fences;
- error = composer_->getReleaseFences(display,
- &out_layers, &out_fences);
- ALOGE_IF(error != HWC::Error::None,
- "HardwareComposer::PostLayers: Failed to get release fences: %s",
- error.to_string().c_str());
-
- // Perform post-frame bookkeeping.
- uint32_t num_elements = out_layers.size();
- for (size_t i = 0; i < num_elements; ++i) {
- for (auto& layer : layers_) {
- if (layer.GetLayerHandle() == out_layers[i]) {
- layer.Finish(out_fences[i]);
- }
- }
- }
-}
-
-void HardwareComposer::SetDisplaySurfaces(
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces) {
- ALOGI("HardwareComposer::SetDisplaySurfaces: surface count=%zd",
- surfaces.size());
- const bool display_idle = surfaces.size() == 0;
- {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- surfaces_ = std::move(surfaces);
- surfaces_changed_ = true;
- }
-
- if (request_display_callback_)
- request_display_callback_(!display_idle);
-
- // Set idle state based on whether there are any surfaces to handle.
- UpdatePostThreadState(PostThreadState::Idle, display_idle);
-}
-
-int HardwareComposer::OnNewGlobalBuffer(DvrGlobalBufferKey key,
- IonBuffer& ion_buffer) {
- if (key == DvrGlobalBuffers::kVsyncBuffer) {
- vsync_ring_ = std::make_unique<CPUMappedBroadcastRing<DvrVsyncRing>>(
- &ion_buffer, CPUUsageMode::WRITE_OFTEN);
-
- if (vsync_ring_->IsMapped() == false) {
- return -EPERM;
- }
- }
-
- if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
- return MapConfigBuffer(ion_buffer);
- }
-
- return 0;
-}
-
-void HardwareComposer::OnDeletedGlobalBuffer(DvrGlobalBufferKey key) {
- if (key == DvrGlobalBuffers::kVrFlingerConfigBufferKey) {
- ConfigBufferDeleted();
- }
-}
-
-int HardwareComposer::MapConfigBuffer(IonBuffer& ion_buffer) {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- shared_config_ring_ = DvrConfigRing();
-
- if (ion_buffer.width() < DvrConfigRing::MemorySize()) {
- ALOGE("HardwareComposer::MapConfigBuffer: invalid buffer size.");
- return -EINVAL;
- }
-
- void* buffer_base = 0;
- int result = ion_buffer.Lock(ion_buffer.usage(), 0, 0, ion_buffer.width(),
- ion_buffer.height(), &buffer_base);
- if (result != 0) {
- ALOGE(
- "HardwareComposer::MapConfigBuffer: Failed to map vrflinger config "
- "buffer.");
- return -EPERM;
- }
-
- shared_config_ring_ = DvrConfigRing::Create(buffer_base, ion_buffer.width());
- ion_buffer.Unlock();
-
- return 0;
-}
-
-void HardwareComposer::ConfigBufferDeleted() {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- shared_config_ring_ = DvrConfigRing();
-}
-
-void HardwareComposer::UpdateConfigBuffer() {
- std::lock_guard<std::mutex> lock(shared_config_mutex_);
- if (!shared_config_ring_.is_valid())
- return;
- // Copy from latest record in shared_config_ring_ to local copy.
- DvrConfig record;
- if (shared_config_ring_.GetNewest(&shared_config_ring_sequence_, &record)) {
- ALOGI("DvrConfig updated: sequence %u, post offset %d",
- shared_config_ring_sequence_, record.frame_post_offset_ns);
- ++shared_config_ring_sequence_;
- post_thread_config_ = record;
- }
-}
-
-int HardwareComposer::PostThreadPollInterruptible(
- const pdx::LocalHandle& event_fd, int requested_events, int timeout_ms) {
- pollfd pfd[2] = {
- {
- .fd = event_fd.Get(),
- .events = static_cast<short>(requested_events),
- .revents = 0,
- },
- {
- .fd = post_thread_event_fd_.Get(),
- .events = POLLPRI | POLLIN,
- .revents = 0,
- },
- };
- int ret, error;
- do {
- ret = poll(pfd, 2, timeout_ms);
- error = errno;
- ALOGW_IF(ret < 0,
- "HardwareComposer::PostThreadPollInterruptible: Error during "
- "poll(): %s (%d)",
- strerror(error), error);
- } while (ret < 0 && error == EINTR);
-
- if (ret < 0) {
- return -error;
- } else if (ret == 0) {
- return -ETIMEDOUT;
- } else if (pfd[0].revents != 0) {
- return 0;
- } else if (pfd[1].revents != 0) {
- ALOGI("VrHwcPost thread interrupted: revents=%x", pfd[1].revents);
- return kPostThreadInterrupted;
- } else {
- return 0;
- }
-}
-
-// Sleep until the next predicted vsync, returning the predicted vsync
-// timestamp.
-Status<int64_t> HardwareComposer::WaitForPredictedVSync() {
- const int64_t predicted_vsync_time = last_vsync_timestamp_ +
- (target_display_->vsync_period_ns * vsync_prediction_interval_);
- const int error = SleepUntil(predicted_vsync_time);
- if (error < 0) {
- ALOGE("HardwareComposer::WaifForVSync:: Failed to sleep: %s",
- strerror(-error));
- return error;
- }
- return {predicted_vsync_time};
-}
-
-int HardwareComposer::SleepUntil(int64_t wakeup_timestamp) {
- const int timer_fd = vsync_sleep_timer_fd_.Get();
- const itimerspec wakeup_itimerspec = {
- .it_interval = {.tv_sec = 0, .tv_nsec = 0},
- .it_value = NsToTimespec(wakeup_timestamp),
- };
- int ret =
- timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &wakeup_itimerspec, nullptr);
- int error = errno;
- if (ret < 0) {
- ALOGE("HardwareComposer::SleepUntil: Failed to set timerfd: %s",
- strerror(error));
- return -error;
- }
-
- return PostThreadPollInterruptible(vsync_sleep_timer_fd_, POLLIN,
- /*timeout_ms*/ -1);
-}
-
-void HardwareComposer::PostThread() {
- // NOLINTNEXTLINE(runtime/int)
- prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrHwcPost"), 0, 0, 0);
-
- // Set the scheduler to SCHED_FIFO with high priority. If this fails here
- // there may have been a startup timing issue between this thread and
- // performanced. Try again later when this thread becomes active.
- bool thread_policy_setup =
- SetThreadPolicy("graphics:high", "/system/performance");
-
- // Create a timerfd based on CLOCK_MONOTINIC.
- vsync_sleep_timer_fd_.Reset(timerfd_create(CLOCK_MONOTONIC, 0));
- LOG_ALWAYS_FATAL_IF(
- !vsync_sleep_timer_fd_,
- "HardwareComposer: Failed to create vsync sleep timerfd: %s",
- strerror(errno));
-
- struct VsyncEyeOffsets { int64_t left_ns, right_ns; };
- bool was_running = false;
-
- auto get_vsync_eye_offsets = [this]() -> VsyncEyeOffsets {
- VsyncEyeOffsets offsets;
- offsets.left_ns =
- GetPosePredictionTimeOffset(target_display_->vsync_period_ns);
-
- // TODO(jbates) Query vblank time from device, when such an API is
- // available. This value (6.3%) was measured on A00 in low persistence mode.
- int64_t vblank_ns = target_display_->vsync_period_ns * 63 / 1000;
- offsets.right_ns = (target_display_->vsync_period_ns - vblank_ns) / 2;
-
- // Check property for overriding right eye offset value.
- offsets.right_ns =
- property_get_int64(kRightEyeOffsetProperty, offsets.right_ns);
-
- return offsets;
- };
-
- VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets();
-
- while (1) {
- ATRACE_NAME("HardwareComposer::PostThread");
-
- // Check for updated config once per vsync.
- UpdateConfigBuffer();
-
- while (post_thread_quiescent_) {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
- ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
-
- if (was_running) {
- vsync_trace_parity_ = false;
- ATRACE_INT(kVsyncTraceEventName, 0);
- }
-
- // Tear down resources.
- OnPostThreadPaused();
- was_running = false;
- post_thread_resumed_ = false;
- post_thread_ready_.notify_all();
-
- if (PostThreadCondWait(lock, -1,
- [this] { return !post_thread_quiescent_; })) {
- // A true return value means we've been asked to quit.
- return;
- }
-
- post_thread_resumed_ = true;
- post_thread_ready_.notify_all();
-
- ALOGI("HardwareComposer::PostThread: Exiting quiescent state.");
- }
-
- if (!composer_)
- CreateComposer();
-
- bool target_display_changed = UpdateTargetDisplay();
- bool just_resumed_running = !was_running;
- was_running = true;
-
- if (target_display_changed)
- vsync_eye_offsets = get_vsync_eye_offsets();
-
- if (just_resumed_running) {
- OnPostThreadResumed();
-
- // Try to setup the scheduler policy if it failed during startup. Only
- // attempt to do this on transitions from inactive to active to avoid
- // spamming the system with RPCs and log messages.
- if (!thread_policy_setup) {
- thread_policy_setup =
- SetThreadPolicy("graphics:high", "/system/performance");
- }
- }
-
- if (target_display_changed || just_resumed_running) {
- // Initialize the last vsync timestamp with the current time. The
- // predictor below uses this time + the vsync interval in absolute time
- // units for the initial delay. Once the driver starts reporting vsync the
- // predictor will sync up with the real vsync.
- last_vsync_timestamp_ = GetSystemClockNs();
- vsync_prediction_interval_ = 1;
- retire_fence_fds_.clear();
- }
-
- int64_t vsync_timestamp = 0;
- {
- TRACE_FORMAT("wait_vsync|vsync=%u;last_timestamp=%" PRId64
- ";prediction_interval=%d|",
- vsync_count_ + 1, last_vsync_timestamp_,
- vsync_prediction_interval_);
-
- auto status = WaitForPredictedVSync();
- ALOGE_IF(
- !status,
- "HardwareComposer::PostThread: Failed to wait for vsync event: %s",
- status.GetErrorMessage().c_str());
-
- // If there was an error either sleeping was interrupted due to pausing or
- // there was an error getting the latest timestamp.
- if (!status)
- continue;
-
- // Predicted vsync timestamp for this interval. This is stable because we
- // use absolute time for the wakeup timer.
- vsync_timestamp = status.get();
- }
-
- vsync_trace_parity_ = !vsync_trace_parity_;
- ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
-
- // Advance the vsync counter only if the system is keeping up with hardware
- // vsync to give clients an indication of the delays.
- if (vsync_prediction_interval_ == 1)
- ++vsync_count_;
-
- UpdateLayerConfig();
-
- // Publish the vsync event.
- if (vsync_ring_) {
- DvrVsync vsync;
- vsync.vsync_count = vsync_count_;
- vsync.vsync_timestamp_ns = vsync_timestamp;
- vsync.vsync_left_eye_offset_ns = vsync_eye_offsets.left_ns;
- vsync.vsync_right_eye_offset_ns = vsync_eye_offsets.right_ns;
- vsync.vsync_period_ns = target_display_->vsync_period_ns;
-
- vsync_ring_->Publish(vsync);
- }
-
- {
- // Sleep until shortly before vsync.
- ATRACE_NAME("sleep");
-
- const int64_t display_time_est_ns =
- vsync_timestamp + target_display_->vsync_period_ns;
- const int64_t now_ns = GetSystemClockNs();
- const int64_t sleep_time_ns = display_time_est_ns - now_ns -
- post_thread_config_.frame_post_offset_ns;
- const int64_t wakeup_time_ns =
- display_time_est_ns - post_thread_config_.frame_post_offset_ns;
-
- ATRACE_INT64("sleep_time_ns", sleep_time_ns);
- if (sleep_time_ns > 0) {
- int error = SleepUntil(wakeup_time_ns);
- ALOGE_IF(error < 0 && error != kPostThreadInterrupted,
- "HardwareComposer::PostThread: Failed to sleep: %s",
- strerror(-error));
- // If the sleep was interrupted (error == kPostThreadInterrupted),
- // we still go through and present this frame because we may have set
- // layers earlier and we want to flush the Composer's internal command
- // buffer by continuing through to validate and present.
- }
- }
-
- {
- auto status = composer_callback_->GetVsyncTime(target_display_->id);
-
- // If we failed to read vsync there might be a problem with the driver.
- // Since there's nothing we can do just behave as though we didn't get an
- // updated vsync time and let the prediction continue.
- const int64_t current_vsync_timestamp =
- status ? status.get() : last_vsync_timestamp_;
-
- const bool vsync_delayed =
- last_vsync_timestamp_ == current_vsync_timestamp;
- ATRACE_INT("vsync_delayed", vsync_delayed);
-
- // If vsync was delayed advance the prediction interval and allow the
- // fence logic in PostLayers() to skip the frame.
- if (vsync_delayed) {
- ALOGW(
- "HardwareComposer::PostThread: VSYNC timestamp did not advance "
- "since last frame: timestamp=%" PRId64 " prediction_interval=%d",
- current_vsync_timestamp, vsync_prediction_interval_);
- vsync_prediction_interval_++;
- } else {
- // We have an updated vsync timestamp, reset the prediction interval.
- last_vsync_timestamp_ = current_vsync_timestamp;
- vsync_prediction_interval_ = 1;
- }
- }
-
- PostLayers(target_display_->id);
- }
-}
-
-bool HardwareComposer::UpdateTargetDisplay() {
- bool target_display_changed = false;
- auto displays = composer_callback_->GetDisplays();
- if (displays.external_display_was_hotplugged) {
- bool was_using_external_display = !target_display_->is_primary;
- if (was_using_external_display) {
- // The external display was hotplugged, so make sure to ignore any bad
- // display errors as we destroy the layers.
- for (auto& layer: layers_)
- layer.IgnoreBadDisplayErrorsOnDestroy(true);
- }
-
- if (displays.external_display) {
- // External display was connected
- external_display_ = GetDisplayParams(composer_.get(),
- *displays.external_display, /*is_primary*/ false);
-
- ALOGI("External display connected. Switching to external display.");
- target_display_ = &(*external_display_);
- target_display_changed = true;
- } else {
- // External display was disconnected
- external_display_ = std::nullopt;
- if (was_using_external_display) {
- ALOGI("External display disconnected. Switching to primary display.");
- target_display_ = &primary_display_;
- target_display_changed = true;
- }
- }
- }
-
- if (target_display_changed) {
- // If we're switching to the external display, turn the primary display off.
- if (!target_display_->is_primary) {
- EnableDisplay(primary_display_, false);
- }
- // If we're switching to the primary display, and the external display is
- // still connected, turn the external display off.
- else if (target_display_->is_primary && external_display_) {
- EnableDisplay(*external_display_, false);
- }
-
- // Update the cached edid data for the current display.
- UpdateEdidData(composer_.get(), target_display_->id);
-
- // Turn the new target display on.
- EnableDisplay(*target_display_, true);
-
- // When we switch displays we need to recreate all the layers, so clear the
- // current list, which will trigger layer recreation.
- layers_.clear();
- }
-
- return target_display_changed;
-}
-
-// Checks for changes in the surface stack and updates the layer config to
-// accomodate the new stack.
-void HardwareComposer::UpdateLayerConfig() {
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces;
- {
- std::unique_lock<std::mutex> lock(post_thread_mutex_);
-
- if (!surfaces_changed_ && (!layers_.empty() || surfaces_.empty()))
- return;
-
- surfaces = surfaces_;
- surfaces_changed_ = false;
- }
-
- ATRACE_NAME("UpdateLayerConfig_HwLayers");
-
- // Sort the new direct surface list by z-order to determine the relative order
- // of the surfaces. This relative order is used for the HWC z-order value to
- // insulate VrFlinger and HWC z-order semantics from each other.
- std::sort(surfaces.begin(), surfaces.end(), [](const auto& a, const auto& b) {
- return a->z_order() < b->z_order();
- });
-
- // Prepare a new layer stack, pulling in layers from the previous
- // layer stack that are still active and updating their attributes.
- std::vector<Layer> layers;
- size_t layer_index = 0;
- for (const auto& surface : surfaces) {
- // The bottom layer is opaque, other layers blend.
- HWC::BlendMode blending =
- layer_index == 0 ? HWC::BlendMode::None : HWC::BlendMode::Coverage;
-
- // Try to find a layer for this surface in the set of active layers.
- auto search =
- std::lower_bound(layers_.begin(), layers_.end(), surface->surface_id());
- const bool found = search != layers_.end() &&
- search->GetSurfaceId() == surface->surface_id();
- if (found) {
- // Update the attributes of the layer that may have changed.
- search->SetBlending(blending);
- search->SetZOrder(layer_index); // Relative z-order.
-
- // Move the existing layer to the new layer set and remove the empty layer
- // object from the current set.
- layers.push_back(std::move(*search));
- layers_.erase(search);
- } else {
- // Insert a layer for the new surface.
- layers.emplace_back(composer_.get(), *target_display_, surface, blending,
- HWC::Composition::Device, layer_index);
- }
-
- ALOGI_IF(
- TRACE,
- "HardwareComposer::UpdateLayerConfig: layer_index=%zu surface_id=%d",
- layer_index, layers[layer_index].GetSurfaceId());
-
- layer_index++;
- }
-
- // Sort the new layer stack by ascending surface id.
- std::sort(layers.begin(), layers.end());
-
- // Replace the previous layer set with the new layer set. The destructor of
- // the previous set will clean up the remaining Layers that are not moved to
- // the new layer set.
- layers_ = std::move(layers);
-
- ALOGD_IF(TRACE, "HardwareComposer::UpdateLayerConfig: %zd active layers",
- layers_.size());
-}
-
-std::vector<sp<IVsyncCallback>>::const_iterator
-HardwareComposer::VsyncService::FindCallback(
- const sp<IVsyncCallback>& callback) const {
- sp<IBinder> binder = IInterface::asBinder(callback);
- return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
- [&](const sp<IVsyncCallback>& callback) {
- return IInterface::asBinder(callback) == binder;
- });
-}
-
-status_t HardwareComposer::VsyncService::registerCallback(
- const sp<IVsyncCallback> callback) {
- std::lock_guard<std::mutex> autolock(mutex_);
- if (FindCallback(callback) == callbacks_.cend()) {
- callbacks_.push_back(callback);
- }
- return OK;
-}
-
-status_t HardwareComposer::VsyncService::unregisterCallback(
- const sp<IVsyncCallback> callback) {
- std::lock_guard<std::mutex> autolock(mutex_);
- auto iter = FindCallback(callback);
- if (iter != callbacks_.cend()) {
- callbacks_.erase(iter);
- }
- return OK;
-}
-
-void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
- ATRACE_NAME("VsyncService::OnVsync");
- std::lock_guard<std::mutex> autolock(mutex_);
- for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
- if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
- iter = callbacks_.erase(iter);
- } else {
- ++iter;
- }
- }
-}
-
-Return<void> HardwareComposer::ComposerCallback::onHotplug(
- Hwc2::Display display, IComposerCallback::Connection conn) {
- std::lock_guard<std::mutex> lock(mutex_);
- ALOGI("onHotplug display=%" PRIu64 " conn=%d", display, conn);
-
- bool is_primary = !got_first_hotplug_ || display == primary_display_.id;
-
- // Our first onHotplug callback is always for the primary display.
- if (!got_first_hotplug_) {
- LOG_ALWAYS_FATAL_IF(conn != IComposerCallback::Connection::CONNECTED,
- "Initial onHotplug callback should be primary display connected");
- got_first_hotplug_ = true;
- } else if (is_primary) {
- ALOGE("Ignoring unexpected onHotplug() call for primary display");
- return Void();
- }
-
- if (conn == IComposerCallback::Connection::CONNECTED) {
- if (!is_primary)
- external_display_ = DisplayInfo();
- DisplayInfo& display_info = is_primary ?
- primary_display_ : *external_display_;
- display_info.id = display;
-
- std::array<char, 1024> buffer;
- snprintf(buffer.data(), buffer.size(),
- "/sys/class/graphics/fb%" PRIu64 "/vsync_event", display);
- if (LocalHandle handle{buffer.data(), O_RDONLY}) {
- ALOGI(
- "HardwareComposer::ComposerCallback::onHotplug: Driver supports "
- "vsync_event node for display %" PRIu64,
- display);
- display_info.driver_vsync_event_fd = std::move(handle);
- } else {
- ALOGI(
- "HardwareComposer::ComposerCallback::onHotplug: Driver does not "
- "support vsync_event node for display %" PRIu64,
- display);
- }
- } else if (conn == IComposerCallback::Connection::DISCONNECTED) {
- external_display_ = std::nullopt;
- }
-
- if (!is_primary)
- external_display_was_hotplugged_ = true;
-
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onRefresh(
- Hwc2::Display /*display*/) {
- return hardware::Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
- int64_t timestamp) {
- TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
- display, timestamp);
- std::lock_guard<std::mutex> lock(mutex_);
- DisplayInfo* display_info = GetDisplayInfo(display);
- if (display_info) {
- display_info->callback_vsync_timestamp = timestamp;
- }
- if (primary_display_.id == display && vsync_service_ != nullptr) {
- vsync_service_->OnVsync(timestamp);
- }
-
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
- Hwc2::Display /*display*/, int64_t /*timestamp*/,
- Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
- LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
- Hwc2::Display /*display*/,
- const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
- LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
- return Void();
-}
-
-Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
- Hwc2::Display /*display*/) {
- LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
- return Void();
-}
-
-void HardwareComposer::ComposerCallback::SetVsyncService(
- const sp<VsyncService>& vsync_service) {
- std::lock_guard<std::mutex> lock(mutex_);
- vsync_service_ = vsync_service;
-}
-
-HardwareComposer::ComposerCallback::Displays
-HardwareComposer::ComposerCallback::GetDisplays() {
- std::lock_guard<std::mutex> lock(mutex_);
- Displays displays;
- displays.primary_display = primary_display_.id;
- if (external_display_)
- displays.external_display = external_display_->id;
- if (external_display_was_hotplugged_) {
- external_display_was_hotplugged_ = false;
- displays.external_display_was_hotplugged = true;
- }
- return displays;
-}
-
-Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
- hwc2_display_t display) {
- std::lock_guard<std::mutex> autolock(mutex_);
- DisplayInfo* display_info = GetDisplayInfo(display);
- if (!display_info) {
- ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
- return ErrorStatus(EINVAL);
- }
-
- // See if the driver supports direct vsync events.
- LocalHandle& event_fd = display_info->driver_vsync_event_fd;
- if (!event_fd) {
- // Fall back to returning the last timestamp returned by the vsync
- // callback.
- return display_info->callback_vsync_timestamp;
- }
-
- // When the driver supports the vsync_event sysfs node we can use it to
- // determine the latest vsync timestamp, even if the HWC callback has been
- // delayed.
-
- // The driver returns data in the form "VSYNC=<timestamp ns>".
- std::array<char, 32> data;
- data.fill('\0');
-
- // Seek back to the beginning of the event file.
- int ret = lseek(event_fd.Get(), 0, SEEK_SET);
- if (ret < 0) {
- const int error = errno;
- ALOGE(
- "HardwareComposer::ComposerCallback::GetVsyncTime: Failed to seek "
- "vsync event fd: %s",
- strerror(error));
- return ErrorStatus(error);
- }
-
- // Read the vsync event timestamp.
- ret = read(event_fd.Get(), data.data(), data.size());
- if (ret < 0) {
- const int error = errno;
- ALOGE_IF(error != EAGAIN,
- "HardwareComposer::ComposerCallback::GetVsyncTime: Error "
- "while reading timestamp: %s",
- strerror(error));
- return ErrorStatus(error);
- }
-
- int64_t timestamp;
- ret = sscanf(data.data(), "VSYNC=%" PRIu64,
- reinterpret_cast<uint64_t*>(×tamp));
- if (ret < 0) {
- const int error = errno;
- ALOGE(
- "HardwareComposer::ComposerCallback::GetVsyncTime: Error while "
- "parsing timestamp: %s",
- strerror(error));
- return ErrorStatus(error);
- }
-
- return {timestamp};
-}
-
-HardwareComposer::ComposerCallback::DisplayInfo*
-HardwareComposer::ComposerCallback::GetDisplayInfo(hwc2_display_t display) {
- if (display == primary_display_.id) {
- return &primary_display_;
- } else if (external_display_ && display == external_display_->id) {
- return &(*external_display_);
- }
- return nullptr;
-}
-
-void Layer::Reset() {
- if (hardware_composer_layer_) {
- HWC::Error error =
- composer_->destroyLayer(display_params_.id, hardware_composer_layer_);
- if (error != HWC::Error::None &&
- (!ignore_bad_display_errors_on_destroy_ ||
- error != HWC::Error::BadDisplay)) {
- ALOGE("destroyLayer() failed for display %" PRIu64 ", layer %" PRIu64
- ". error: %s", display_params_.id, hardware_composer_layer_,
- error.to_string().c_str());
- }
- hardware_composer_layer_ = 0;
- }
-
- z_order_ = 0;
- blending_ = HWC::BlendMode::None;
- composition_type_ = HWC::Composition::Invalid;
- target_composition_type_ = composition_type_;
- source_ = EmptyVariant{};
- acquire_fence_.Close();
- surface_rect_functions_applied_ = false;
- pending_visibility_settings_ = true;
- cached_buffer_map_.clear();
- ignore_bad_display_errors_on_destroy_ = false;
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Composition composition_type,
- size_t z_order)
- : composer_(composer),
- display_params_(display_params),
- z_order_{z_order},
- blending_{blending},
- target_composition_type_{composition_type},
- source_{SourceSurface{surface}} {
- CommonLayerSetup();
-}
-
-Layer::Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Composition composition_type, size_t z_order)
- : composer_(composer),
- display_params_(display_params),
- z_order_{z_order},
- blending_{blending},
- target_composition_type_{composition_type},
- source_{SourceBuffer{buffer}} {
- CommonLayerSetup();
-}
-
-Layer::~Layer() { Reset(); }
-
-Layer::Layer(Layer&& other) noexcept { *this = std::move(other); }
-
-Layer& Layer::operator=(Layer&& other) noexcept {
- if (this != &other) {
- Reset();
- using std::swap;
- swap(composer_, other.composer_);
- swap(display_params_, other.display_params_);
- swap(hardware_composer_layer_, other.hardware_composer_layer_);
- swap(z_order_, other.z_order_);
- swap(blending_, other.blending_);
- swap(composition_type_, other.composition_type_);
- swap(target_composition_type_, other.target_composition_type_);
- swap(source_, other.source_);
- swap(acquire_fence_, other.acquire_fence_);
- swap(surface_rect_functions_applied_,
- other.surface_rect_functions_applied_);
- swap(pending_visibility_settings_, other.pending_visibility_settings_);
- swap(cached_buffer_map_, other.cached_buffer_map_);
- swap(ignore_bad_display_errors_on_destroy_,
- other.ignore_bad_display_errors_on_destroy_);
- }
- return *this;
-}
-
-void Layer::UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer) {
- if (source_.is<SourceBuffer>())
- std::get<SourceBuffer>(source_) = {buffer};
-}
-
-void Layer::SetBlending(HWC::BlendMode blending) {
- if (blending_ != blending) {
- blending_ = blending;
- pending_visibility_settings_ = true;
- }
-}
-
-void Layer::SetZOrder(size_t z_order) {
- if (z_order_ != z_order) {
- z_order_ = z_order;
- pending_visibility_settings_ = true;
- }
-}
-
-IonBuffer* Layer::GetBuffer() {
- struct Visitor {
- IonBuffer* operator()(SourceSurface& source) { return source.GetBuffer(); }
- IonBuffer* operator()(SourceBuffer& source) { return source.GetBuffer(); }
- IonBuffer* operator()(EmptyVariant) { return nullptr; }
- };
- return source_.Visit(Visitor{});
-}
-
-void Layer::UpdateVisibilitySettings() {
- if (pending_visibility_settings_) {
- pending_visibility_settings_ = false;
-
- HWC::Error error;
-
- error = composer_->setLayerBlendMode(
- display_params_.id, hardware_composer_layer_,
- blending_.cast<Hwc2::IComposerClient::BlendMode>());
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer blend mode: %s",
- error.to_string().c_str());
-
- error = composer_->setLayerZOrder(display_params_.id,
- hardware_composer_layer_, z_order_);
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting z_ order: %s",
- error.to_string().c_str());
- }
-}
-
-void Layer::UpdateLayerSettings() {
- HWC::Error error;
-
- UpdateVisibilitySettings();
-
- // TODO(eieio): Use surface attributes or some other mechanism to control
- // the layer display frame.
- error = composer_->setLayerDisplayFrame(
- display_params_.id, hardware_composer_layer_,
- {0, 0, display_params_.width, display_params_.height});
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer display frame: %s",
- error.to_string().c_str());
-
- error = composer_->setLayerVisibleRegion(
- display_params_.id, hardware_composer_layer_,
- {{0, 0, display_params_.width, display_params_.height}});
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer visible region: %s",
- error.to_string().c_str());
-
- error = composer_->setLayerPlaneAlpha(display_params_.id,
- hardware_composer_layer_, 1.0f);
- ALOGE_IF(error != HWC::Error::None,
- "Layer::UpdateLayerSettings: Error setting layer plane alpha: %s",
- error.to_string().c_str());
-}
-
-void Layer::CommonLayerSetup() {
- HWC::Error error = composer_->createLayer(display_params_.id,
- &hardware_composer_layer_);
- ALOGE_IF(error != HWC::Error::None,
- "Layer::CommonLayerSetup: Failed to create layer on primary "
- "display: %s",
- error.to_string().c_str());
- UpdateLayerSettings();
-}
-
-bool Layer::CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id) {
- auto search = cached_buffer_map_.find(slot);
- if (search != cached_buffer_map_.end() && search->second == buffer_id)
- return true;
-
- // Assign or update the buffer slot.
- if (buffer_id >= 0)
- cached_buffer_map_[slot] = buffer_id;
- return false;
-}
-
-void Layer::Prepare() {
- int right, bottom, id;
- sp<GraphicBuffer> handle;
- std::size_t slot;
-
- // Acquire the next buffer according to the type of source.
- IfAnyOf<SourceSurface, SourceBuffer>::Call(&source_, [&](auto& source) {
- std::tie(right, bottom, id, handle, acquire_fence_, slot) =
- source.Acquire();
- });
-
- TRACE_FORMAT("Layer::Prepare|buffer_id=%d;slot=%zu|", id, slot);
-
- // Update any visibility (blending, z-order) changes that occurred since
- // last prepare.
- UpdateVisibilitySettings();
-
- // When a layer is first setup there may be some time before the first
- // buffer arrives. Setup the HWC layer as a solid color to stall for time
- // until the first buffer arrives. Once the first buffer arrives there will
- // always be a buffer for the frame even if it is old.
- if (!handle.get()) {
- if (composition_type_ == HWC::Composition::Invalid) {
- composition_type_ = HWC::Composition::SolidColor;
- composer_->setLayerCompositionType(
- display_params_.id, hardware_composer_layer_,
- composition_type_.cast<Hwc2::IComposerClient::Composition>());
- Hwc2::IComposerClient::Color layer_color = {0, 0, 0, 0};
- composer_->setLayerColor(display_params_.id, hardware_composer_layer_,
- layer_color);
- } else {
- // The composition type is already set. Nothing else to do until a
- // buffer arrives.
- }
- } else {
- if (composition_type_ != target_composition_type_) {
- composition_type_ = target_composition_type_;
- composer_->setLayerCompositionType(
- display_params_.id, hardware_composer_layer_,
- composition_type_.cast<Hwc2::IComposerClient::Composition>());
- }
-
- // See if the HWC cache already has this buffer.
- const bool cached = CheckAndUpdateCachedBuffer(slot, id);
- if (cached)
- handle = nullptr;
-
- HWC::Error error{HWC::Error::None};
- error =
- composer_->setLayerBuffer(display_params_.id, hardware_composer_layer_,
- slot, handle, acquire_fence_.Get());
-
- ALOGE_IF(error != HWC::Error::None,
- "Layer::Prepare: Error setting layer buffer: %s",
- error.to_string().c_str());
-
- if (!surface_rect_functions_applied_) {
- const float float_right = right;
- const float float_bottom = bottom;
- error = composer_->setLayerSourceCrop(display_params_.id,
- hardware_composer_layer_,
- {0, 0, float_right, float_bottom});
-
- ALOGE_IF(error != HWC::Error::None,
- "Layer::Prepare: Error setting layer source crop: %s",
- error.to_string().c_str());
-
- surface_rect_functions_applied_ = true;
- }
- }
-}
-
-void Layer::Finish(int release_fence_fd) {
- IfAnyOf<SourceSurface, SourceBuffer>::Call(
- &source_, [release_fence_fd](auto& source) {
- source.Finish(LocalHandle(release_fence_fd));
- });
-}
-
-void Layer::Drop() { acquire_fence_.Close(); }
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
deleted file mode 100644
index bfce10b..0000000
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ /dev/null
@@ -1,577 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
-
-#include <ui/GraphicBuffer.h>
-#include "DisplayHardware/ComposerHal.h"
-#include "hwc_types.h"
-
-#include <dvr/dvr_shared_buffers.h>
-#include <hardware/gralloc.h>
-#include <log/log.h>
-
-#include <array>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-#include <tuple>
-#include <vector>
-
-#include <dvr/dvr_config.h>
-#include <dvr/dvr_vsync.h>
-#include <pdx/file_handle.h>
-#include <pdx/rpc/variant.h>
-#include <private/dvr/shared_buffer_helpers.h>
-#include <private/dvr/vsync_service.h>
-
-#include "DisplayHardware/DisplayIdentification.h"
-#include "acquired_buffer.h"
-#include "display_surface.h"
-
-// Hardware composer HAL doesn't define HWC_TRANSFORM_NONE as of this writing.
-#ifndef HWC_TRANSFORM_NONE
-#define HWC_TRANSFORM_NONE static_cast<hwc_transform_t>(0)
-#endif
-
-namespace android {
-namespace dvr {
-
-// Basic display metrics for physical displays.
-struct DisplayParams {
- hwc2_display_t id;
- bool is_primary;
-
- int width;
- int height;
-
- struct {
- int x;
- int y;
- } dpi;
-
- int vsync_period_ns;
-};
-
-// Layer represents the connection between a hardware composer layer and the
-// source supplying buffers for the layer's contents.
-class Layer {
- public:
- Layer() = default;
-
- // Sets up the layer to use a display surface as its content source. The Layer
- // automatically handles ACQUIRE/RELEASE phases for the surface's buffer train
- // every frame.
- //
- // |composer| The composer instance.
- // |display_params| Info about the display to use.
- // |blending| receives HWC_BLENDING_* values.
- // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
- // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
- // |index| is the index of this surface in the DirectDisplaySurface array.
- Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<DirectDisplaySurface>& surface,
- HWC::BlendMode blending, HWC::Composition composition_type,
- size_t z_order);
-
- // Sets up the layer to use a direct buffer as its content source. No special
- // handling of the buffer is performed; responsibility for updating or
- // changing the buffer each frame is on the caller.
- //
- // |composer| The composer instance.
- // |display_params| Info about the display to use.
- // |blending| receives HWC_BLENDING_* values.
- // |composition_type| receives either HWC_FRAMEBUFFER for most layers or
- // HWC_FRAMEBUFFER_TARGET (unless you know what you are doing).
- Layer(Hwc2::Composer* composer, const DisplayParams& display_params,
- const std::shared_ptr<IonBuffer>& buffer, HWC::BlendMode blending,
- HWC::Composition composition_type, size_t z_order);
-
- Layer(Layer&&) noexcept;
- Layer& operator=(Layer&&) noexcept;
-
- ~Layer();
-
- // Releases any shared pointers and fence handles held by this instance.
- void Reset();
-
- // Layers that use a direct IonBuffer should call this each frame to update
- // which buffer will be used for the next PostLayers.
- void UpdateBuffer(const std::shared_ptr<IonBuffer>& buffer);
-
- // Sets up the hardware composer layer for the next frame. When the layer is
- // associated with a display surface, this method automatically ACQUIRES a new
- // buffer if one is available.
- void Prepare();
-
- // After calling prepare, if this frame is to be dropped instead of passing
- // along to the HWC, call Drop to close the contained fence(s).
- void Drop();
-
- // Performs fence bookkeeping after the frame has been posted to hardware
- // composer.
- void Finish(int release_fence_fd);
-
- // Sets the blending for the layer. |blending| receives HWC_BLENDING_* values.
- void SetBlending(HWC::BlendMode blending);
-
- // Sets the z-order of this layer
- void SetZOrder(size_t z_order);
-
- // Gets the current IonBuffer associated with this layer. Ownership of the
- // buffer DOES NOT pass to the caller and the pointer is not guaranteed to
- // remain valid across calls to Layer::Setup(), Layer::Prepare(), or
- // Layer::Reset(). YOU HAVE BEEN WARNED.
- IonBuffer* GetBuffer();
-
- HWC::Composition GetCompositionType() const { return composition_type_; }
- HWC::Layer GetLayerHandle() const { return hardware_composer_layer_; }
- bool IsLayerSetup() const { return !source_.empty(); }
-
- int GetSurfaceId() const {
- int surface_id = -1;
- pdx::rpc::IfAnyOf<SourceSurface>::Call(
- &source_, [&surface_id](const SourceSurface& surface_source) {
- surface_id = surface_source.GetSurfaceId();
- });
- return surface_id;
- }
-
- int GetBufferId() const {
- int buffer_id = -1;
- pdx::rpc::IfAnyOf<SourceSurface>::Call(
- &source_, [&buffer_id](const SourceSurface& surface_source) {
- buffer_id = surface_source.GetBufferId();
- });
- return buffer_id;
- }
-
- // Compares Layers by surface id.
- bool operator<(const Layer& other) const {
- return GetSurfaceId() < other.GetSurfaceId();
- }
- bool operator<(int surface_id) const { return GetSurfaceId() < surface_id; }
-
- void IgnoreBadDisplayErrorsOnDestroy(bool ignore) {
- ignore_bad_display_errors_on_destroy_ = ignore;
- }
-
- private:
- void CommonLayerSetup();
-
- // Applies all of the settings to this layer using the hwc functions
- void UpdateLayerSettings();
-
- // Applies visibility settings that may have changed.
- void UpdateVisibilitySettings();
-
- // Checks whether the buffer, given by id, is associated with the given slot
- // in the HWC buffer cache. If the slot is not associated with the given
- // buffer the cache is updated to establish the association and the buffer
- // should be sent to HWC using setLayerBuffer. Returns true if the association
- // was already established, false if not. A buffer_id of -1 is never
- // associated and always returns false.
- bool CheckAndUpdateCachedBuffer(std::size_t slot, int buffer_id);
-
- // Composer instance.
- Hwc2::Composer* composer_ = nullptr;
-
- // Parameters of the display to use for this layer.
- DisplayParams display_params_;
-
- // The hardware composer layer and metrics to use during the prepare cycle.
- hwc2_layer_t hardware_composer_layer_ = 0;
-
- // Layer properties used to setup the hardware composer layer during the
- // Prepare phase.
- size_t z_order_ = 0;
- HWC::BlendMode blending_ = HWC::BlendMode::None;
- HWC::Composition composition_type_ = HWC::Composition::Invalid;
- HWC::Composition target_composition_type_ = HWC::Composition::Device;
-
- // State when the layer is connected to a surface. Provides the same interface
- // as SourceBuffer to simplify internal use by Layer.
- struct SourceSurface {
- std::shared_ptr<DirectDisplaySurface> surface;
- AcquiredBuffer acquired_buffer;
- pdx::LocalHandle release_fence;
-
- explicit SourceSurface(const std::shared_ptr<DirectDisplaySurface>& surface)
- : surface(surface) {}
-
- // Attempts to acquire a new buffer from the surface and return a tuple with
- // width, height, buffer handle, and fence. If a new buffer is not available
- // the previous buffer is returned or an empty value if no buffer has ever
- // been posted. When a new buffer is acquired the previous buffer's release
- // fence is passed out automatically.
- std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
- Acquire() {
- if (surface->IsBufferAvailable()) {
- acquired_buffer.Release(std::move(release_fence));
- acquired_buffer = surface->AcquireCurrentBuffer();
- ATRACE_ASYNC_END("BufferPost", acquired_buffer.buffer()->id());
- }
- if (!acquired_buffer.IsEmpty()) {
- return std::make_tuple(
- acquired_buffer.buffer()->width(),
- acquired_buffer.buffer()->height(), acquired_buffer.buffer()->id(),
- acquired_buffer.buffer()->buffer()->buffer(),
- acquired_buffer.ClaimAcquireFence(), acquired_buffer.slot());
- } else {
- return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
- }
- }
-
- void Finish(pdx::LocalHandle fence) { release_fence = std::move(fence); }
-
- // Gets a pointer to the current acquired buffer or returns nullptr if there
- // isn't one.
- IonBuffer* GetBuffer() {
- if (acquired_buffer.IsAvailable())
- return acquired_buffer.buffer()->buffer();
- else
- return nullptr;
- }
-
- // Returns the surface id of the surface.
- int GetSurfaceId() const { return surface->surface_id(); }
-
- // Returns the buffer id for the current buffer.
- int GetBufferId() const {
- if (acquired_buffer.IsAvailable())
- return acquired_buffer.buffer()->id();
- else
- return -1;
- }
- };
-
- // State when the layer is connected to a buffer. Provides the same interface
- // as SourceSurface to simplify internal use by Layer.
- struct SourceBuffer {
- std::shared_ptr<IonBuffer> buffer;
-
- std::tuple<int, int, int, sp<GraphicBuffer>, pdx::LocalHandle, std::size_t>
- Acquire() {
- if (buffer)
- return std::make_tuple(buffer->width(), buffer->height(), -1,
- buffer->buffer(), pdx::LocalHandle{}, 0);
- else
- return std::make_tuple(0, 0, -1, nullptr, pdx::LocalHandle{}, 0);
- }
-
- void Finish(pdx::LocalHandle /*fence*/) {}
-
- IonBuffer* GetBuffer() { return buffer.get(); }
-
- int GetSurfaceId() const { return -1; }
- int GetBufferId() const { return -1; }
- };
-
- // The underlying hardware composer layer is supplied buffers either from a
- // surface buffer train or from a buffer directly.
- pdx::rpc::Variant<SourceSurface, SourceBuffer> source_;
-
- pdx::LocalHandle acquire_fence_;
- bool surface_rect_functions_applied_ = false;
- bool pending_visibility_settings_ = true;
-
- // Map of buffer slot assignments that have already been established with HWC:
- // slot -> buffer_id. When this map contains a matching slot and buffer_id the
- // buffer argument to setLayerBuffer may be nullptr to avoid the cost of
- // importing a buffer HWC already knows about.
- std::map<std::size_t, int> cached_buffer_map_;
-
- // When calling destroyLayer() on an external display that's been removed we
- // typically get HWC2_ERROR_BAD_DISPLAY errors. If
- // ignore_bad_display_errors_on_destroy_ is true, don't log the bad display
- // errors, since they're expected.
- bool ignore_bad_display_errors_on_destroy_ = false;
-
- Layer(const Layer&) = delete;
- void operator=(const Layer&) = delete;
-};
-
-// HardwareComposer encapsulates the hardware composer HAL, exposing a
-// simplified API to post buffers to the display.
-//
-// HardwareComposer is accessed by both the vr flinger dispatcher thread and the
-// surface flinger main thread, in addition to internally running a separate
-// thread for compositing/EDS and posting layers to the HAL. When changing how
-// variables are used or adding new state think carefully about which threads
-// will access the state and whether it needs to be synchronized.
-class HardwareComposer {
- public:
- using RequestDisplayCallback = std::function<void(bool)>;
-
- HardwareComposer();
- ~HardwareComposer();
-
- bool Initialize(Hwc2::Composer* composer,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- bool IsInitialized() const { return initialized_; }
-
- // Start the post thread if there's work to do (i.e. visible layers). This
- // should only be called from surface flinger's main thread.
- void Enable();
- // Pause the post thread, blocking until the post thread has signaled that
- // it's paused. This should only be called from surface flinger's main thread.
- void Disable();
-
- // Called on a binder thread.
- void OnBootFinished();
-
- std::string Dump();
-
- const DisplayParams& GetPrimaryDisplayParams() const {
- return primary_display_;
- }
-
- // Sets the display surfaces to compose the hardware layer stack.
- void SetDisplaySurfaces(
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces);
-
- int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
- void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
-
- // Gets the edid data for the current active display (internal or external)
- DisplayIdentificationData GetCurrentDisplayIdentificationData() {
- return display_identification_data_;
- }
-
- // Gets the edid port for the current active display (internal or external)
- uint8_t GetCurrentDisplayPort() { return display_port_; }
-
- private:
- DisplayParams GetDisplayParams(Hwc2::Composer* composer,
- hwc2_display_t display, bool is_primary);
-
- // Turn display vsync on/off. Returns true on success, false on failure.
- bool EnableVsync(const DisplayParams& display, bool enabled);
- // Turn display power on/off. Returns true on success, false on failure.
- bool SetPowerMode(const DisplayParams& display, bool active);
- // Convenience function to turn a display on/off. Turns both power and vsync
- // on/off. Returns true on success, false on failure.
- bool EnableDisplay(const DisplayParams& display, bool enabled);
-
- class VsyncService : public BnVsyncService {
- public:
- status_t registerCallback(const sp<IVsyncCallback> callback) override;
- status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
- void OnVsync(int64_t vsync_timestamp);
- private:
- std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
- const sp<IVsyncCallback>& callback) const;
- std::mutex mutex_;
- std::vector<sp<IVsyncCallback>> callbacks_;
- };
-
- class ComposerCallback : public Hwc2::IComposerCallback {
- public:
- ComposerCallback() = default;
- hardware::Return<void> onHotplug(Hwc2::Display display,
- Connection conn) override;
- hardware::Return<void> onRefresh(Hwc2::Display display) override;
- hardware::Return<void> onVsync(Hwc2::Display display,
- int64_t timestamp) override;
- hardware::Return<void> onVsync_2_4(
- Hwc2::Display display, int64_t timestamp,
- Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
- hardware::Return<void> onVsyncPeriodTimingChanged(
- Hwc2::Display display,
- const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
- hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
-
- bool GotFirstHotplug() { return got_first_hotplug_; }
- void SetVsyncService(const sp<VsyncService>& vsync_service);
-
- struct Displays {
- hwc2_display_t primary_display = 0;
- std::optional<hwc2_display_t> external_display;
- bool external_display_was_hotplugged = false;
- };
-
- Displays GetDisplays();
- pdx::Status<int64_t> GetVsyncTime(hwc2_display_t display);
-
- private:
- struct DisplayInfo {
- hwc2_display_t id = 0;
- pdx::LocalHandle driver_vsync_event_fd;
- int64_t callback_vsync_timestamp{0};
- };
-
- DisplayInfo* GetDisplayInfo(hwc2_display_t display);
-
- std::mutex mutex_;
-
- bool got_first_hotplug_ = false;
- DisplayInfo primary_display_;
- std::optional<DisplayInfo> external_display_;
- bool external_display_was_hotplugged_ = false;
- sp<VsyncService> vsync_service_;
- };
-
- HWC::Error Validate(hwc2_display_t display);
- HWC::Error Present(hwc2_display_t display);
-
- void PostLayers(hwc2_display_t display);
- void PostThread();
-
- // The post thread has two controlling states:
- // 1. Idle: no work to do (no visible surfaces).
- // 2. Suspended: explicitly halted (system is not in VR mode).
- // When either #1 or #2 is true then the post thread is quiescent, otherwise
- // it is active.
- using PostThreadStateType = uint32_t;
- struct PostThreadState {
- enum : PostThreadStateType {
- Active = 0,
- Idle = (1 << 0),
- Suspended = (1 << 1),
- Quit = (1 << 2),
- };
- };
-
- void UpdatePostThreadState(uint32_t state, bool suspend);
-
- // Blocks until either event_fd becomes readable, or we're interrupted by a
- // control thread, or timeout_ms is reached before any events occur. Any
- // errors are returned as negative errno values, with -ETIMEDOUT returned in
- // the case of a timeout. If we're interrupted, kPostThreadInterrupted will be
- // returned.
- int PostThreadPollInterruptible(const pdx::LocalHandle& event_fd,
- int requested_events, int timeout_ms);
-
- // WaitForPredictedVSync and SleepUntil are blocking calls made on the post
- // thread that can be interrupted by a control thread. If interrupted, these
- // calls return kPostThreadInterrupted.
- int ReadWaitPPState();
- pdx::Status<int64_t> WaitForPredictedVSync();
- int SleepUntil(int64_t wakeup_timestamp);
-
- // Initialize any newly connected displays, and set target_display_ to the
- // display we should render to. Returns true if target_display_
- // changed. Called only from the post thread.
- bool UpdateTargetDisplay();
-
- // Reconfigures the layer stack if the display surfaces changed since the last
- // frame. Called only from the post thread.
- void UpdateLayerConfig();
-
- // Called on the post thread to create the Composer instance.
- void CreateComposer();
-
- // Called on the post thread when the post thread is resumed.
- void OnPostThreadResumed();
- // Called on the post thread when the post thread is paused or quits.
- void OnPostThreadPaused();
-
- // Use post_thread_wait_ to wait for a specific condition, specified by pred.
- // timeout_sec < 0 means wait indefinitely, otherwise it specifies the timeout
- // in seconds.
- // The lock must be held when this function is called.
- // Returns true if the wait was interrupted because the post thread was asked
- // to quit.
- bool PostThreadCondWait(std::unique_lock<std::mutex>& lock,
- int timeout_sec,
- const std::function<bool()>& pred);
-
- // Map the given shared memory buffer to our broadcast ring to track updates
- // to the config parameters.
- int MapConfigBuffer(IonBuffer& ion_buffer);
- void ConfigBufferDeleted();
- // Poll for config udpates.
- void UpdateConfigBuffer();
-
- bool initialized_;
- bool is_standalone_device_;
-
- std::unique_ptr<Hwc2::Composer> composer_;
- sp<ComposerCallback> composer_callback_;
- RequestDisplayCallback request_display_callback_;
-
- DisplayParams primary_display_;
- std::optional<DisplayParams> external_display_;
- DisplayParams* target_display_ = &primary_display_;
-
- // The list of surfaces we should draw. Set by the display service when
- // DirectSurfaces are added, removed, or change visibility. Written by the
- // message dispatch thread and read by the post thread.
- std::vector<std::shared_ptr<DirectDisplaySurface>> surfaces_;
- // Set to true by the dispatch thread whenever surfaces_ changes. Set to false
- // by the post thread when the new list of surfaces is processed.
- bool surfaces_changed_ = false;
-
- std::vector<std::shared_ptr<DirectDisplaySurface>> current_surfaces_;
-
- // Layer set for handling buffer flow into hardware composer layers. This
- // vector must be sorted by surface_id in ascending order.
- std::vector<Layer> layers_;
-
- // The layer posting thread. This thread wakes up a short time before vsync to
- // hand buffers to hardware composer.
- std::thread post_thread_;
-
- // Post thread state machine and synchronization primitives.
- PostThreadStateType post_thread_state_{PostThreadState::Idle |
- PostThreadState::Suspended};
- std::atomic<bool> post_thread_quiescent_{true};
- bool post_thread_resumed_{false};
- pdx::LocalHandle post_thread_event_fd_;
- std::mutex post_thread_mutex_;
- std::condition_variable post_thread_wait_;
- std::condition_variable post_thread_ready_;
-
- // When boot is finished this will be set to true and the post thread will be
- // notified via post_thread_wait_.
- bool boot_finished_ = false;
-
- // VSync sleep timerfd.
- pdx::LocalHandle vsync_sleep_timer_fd_;
-
- // The timestamp of the last vsync.
- int64_t last_vsync_timestamp_ = 0;
-
- // The number of vsync intervals to predict since the last vsync.
- int vsync_prediction_interval_ = 1;
-
- // Vsync count since display on.
- uint32_t vsync_count_ = 0;
-
- // Counter tracking the number of skipped frames.
- int frame_skip_count_ = 0;
-
- // Fd array for tracking retire fences that are returned by hwc. This allows
- // us to detect when the display driver begins queuing frames.
- std::vector<pdx::LocalHandle> retire_fence_fds_;
-
- // If we are publishing vsync data, we will put it here.
- std::unique_ptr<CPUMappedBroadcastRing<DvrVsyncRing>> vsync_ring_;
-
- // Broadcast ring for receiving config data from the DisplayManager.
- DvrConfigRing shared_config_ring_;
- uint32_t shared_config_ring_sequence_{0};
- // Config buffer for reading from the post thread.
- DvrConfig post_thread_config_;
- std::mutex shared_config_mutex_;
-
- bool vsync_trace_parity_ = false;
- sp<VsyncService> vsync_service_;
-
- // Edid section.
- void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
- DisplayIdentificationData display_identification_data_;
- uint8_t display_port_;
-
- static constexpr int kPostThreadInterrupted = 1;
-
- HardwareComposer(const HardwareComposer&) = delete;
- void operator=(const HardwareComposer&) = delete;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_SERVICES_DISPLAYD_HARDWARE_COMPOSER_H_
diff --git a/libs/vr/libvrflinger/hwc_types.h b/libs/vr/libvrflinger/hwc_types.h
deleted file mode 100644
index 8b5c3b3..0000000
--- a/libs/vr/libvrflinger/hwc_types.h
+++ /dev/null
@@ -1,307 +0,0 @@
-#ifndef ANDROID_LIBVRFLINGER_HWCTYPES_H
-#define ANDROID_LIBVRFLINGER_HWCTYPES_H
-
-// General HWC type support. Hardware composer type support is a bit of a mess
-// between HWC1, HWC2 C/C++11, and HIDL types. Particularly bothersome is the
-// use of enum classes, which make analogous types between versions much
-// harder to deal with in a uniform way.
-//
-// These utilities help address some of these pains by providing a type-safe,
-// flexible interface to translate between different type spaces.
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <string>
-#include <type_traits>
-
-namespace HWC {
-
-// Value types derived from HWC HAL types. Some of these are stand-alone,
-// while others are also wrapped in translator classes below.
-using ColorMode = int32_t; // android_color_mode_t;
-using Config = hwc2_config_t;
-using ColorTransform =
- std::underlying_type<android_color_transform_t>::type; // int32_t;
-using Dataspace = std::underlying_type<android_dataspace_t>::type; // int32_t;
-using DisplayId = hwc2_display_t;
-using DisplayRequest = std::underlying_type<HWC2::DisplayRequest>::type;
-using Hdr = std::underlying_type<android_hdr_t>::type; // int32_t;
-using Layer = hwc2_layer_t;
-using PixelFormat =
- std::underlying_type<android_pixel_format_t>::type; // int32_t;
-
-// Type traits and casting utilities.
-
-// SFINAE utility to evaluate type expressions.
-template <typename...>
-using TestTypeExpression = void;
-
-// Traits type to determine the underlying type of an enum, integer,
-// or wrapper class.
-template <typename T, typename = typename std::is_enum<T>::type,
- typename = typename std::is_integral<T>::type, typename = void>
-struct UnderlyingType {
- using Type = T;
-};
-// Partial specialization that matches enum types. Captures the underlying type
-// of the enum in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::true_type, std::false_type> {
- using Type = typename std::underlying_type<T>::type;
-};
-// Partial specialization that matches integral types. Captures the type of the
-// integer in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::true_type> {
- using Type = T;
-};
-// Partial specialization that matches the wrapper types below. Captures
-// wrapper member type ValueType in member type Type.
-template <typename T>
-struct UnderlyingType<T, std::false_type, std::false_type,
- TestTypeExpression<typename T::ValueType>> {
- using Type = typename T::ValueType;
-};
-
-// Enable if T is an enum with underlying type U.
-template <typename T, typename U, typename ReturnType = void>
-using EnableIfMatchingEnum = typename std::enable_if<
- std::is_enum<T>::value &&
- std::is_same<U, typename UnderlyingType<T>::Type>::value,
- ReturnType>::type;
-
-// Enable if T and U are the same size/alignment and have the same underlying
-// type. Handles enum, integral, and wrapper classes below.
-template <typename T, typename U, typename Return = void>
-using EnableIfSafeCast = typename std::enable_if<
- sizeof(T) == sizeof(U) && alignof(T) == alignof(U) &&
- std::is_same<typename UnderlyingType<T>::Type,
- typename UnderlyingType<U>::Type>::value,
- Return>::type;
-
-// Safely cast between std::vectors of matching enum/integer/wraper types.
-// Normally this is not possible with pendantic compiler type checks. However,
-// given the same size, alignment, and underlying type this is safe due to
-// allocator requirements and array-like element access guarantees.
-template <typename T, typename U>
-EnableIfSafeCast<T, U, std::vector<T>*> VectorCast(std::vector<U>* in) {
- return reinterpret_cast<std::vector<T>*>(in);
-}
-
-// Translator classes that wrap specific HWC types to make translating
-// between different types (especially enum class) in code cleaner.
-
-// Base type for the enum wrappers below. This type provides type definitions
-// and implicit conversion logic common to each wrapper type.
-template <typename EnumType>
-struct Wrapper {
- // Alias type of this instantiantion of Wrapper. Useful for inheriting
- // constructors in subclasses via "using Base::Base;" statements.
- using Base = Wrapper<EnumType>;
-
- // The enum type wrapped by this instantiation of Wrapper.
- using BaseType = EnumType;
-
- // The underlying type of the base enum type.
- using ValueType = typename UnderlyingType<BaseType>::Type;
-
- // A default constructor is not defined here. Subclasses should define one
- // as appropriate to define the correct inital value for the enum type.
-
- // Default copy constructor.
- Wrapper(const Wrapper&) = default;
-
- // Implicit conversion from ValueType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(ValueType value) : value(value) {}
-
- // Implicit conversion from BaseType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(BaseType value) : value(static_cast<ValueType>(value)) {}
-
- // Implicit conversion from an enum type of the same underlying type.
- template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
- // NOLINTNEXTLINE(google-explicit-constructor)
- Wrapper(const T& value) : value(static_cast<ValueType>(value)) {}
-
- // Implicit conversion to BaseType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator BaseType() const { return static_cast<BaseType>(value); }
-
- // Implicit conversion to ValueType.
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator ValueType() const { return value; }
-
- template <typename T, typename = EnableIfMatchingEnum<T, ValueType>>
- T cast() const {
- return static_cast<T>(value);
- }
-
- // Converts to string using HWC2 stringification of BaseType.
- std::string to_string() const {
- return HWC2::to_string(static_cast<BaseType>(value));
- }
-
- bool operator!=(const Wrapper& other) const { return value != other.value; }
- bool operator!=(ValueType other_value) const { return value != other_value; }
- bool operator!=(BaseType other_value) const {
- return static_cast<BaseType>(value) != other_value;
- }
- bool operator==(const Wrapper& other) const { return value == other.value; }
- bool operator==(ValueType other_value) const { return value == other_value; }
- bool operator==(BaseType other_value) const {
- return static_cast<BaseType>(value) == other_value;
- }
-
- ValueType value;
-};
-
-struct Attribute final : public Wrapper<HWC2::Attribute> {
- enum : ValueType {
- Invalid = HWC2_ATTRIBUTE_INVALID,
- Width = HWC2_ATTRIBUTE_WIDTH,
- Height = HWC2_ATTRIBUTE_HEIGHT,
- VsyncPeriod = HWC2_ATTRIBUTE_VSYNC_PERIOD,
- DpiX = HWC2_ATTRIBUTE_DPI_X,
- DpiY = HWC2_ATTRIBUTE_DPI_Y,
- };
-
- Attribute() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct BlendMode final : public Wrapper<HWC2::BlendMode> {
- enum : ValueType {
- Invalid = HWC2_BLEND_MODE_INVALID,
- None = HWC2_BLEND_MODE_NONE,
- Premultiplied = HWC2_BLEND_MODE_PREMULTIPLIED,
- Coverage = HWC2_BLEND_MODE_COVERAGE,
- };
-
- BlendMode() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct Composition final : public Wrapper<HWC2::Composition> {
- enum : ValueType {
- Invalid = HWC2_COMPOSITION_INVALID,
- Client = HWC2_COMPOSITION_CLIENT,
- Device = HWC2_COMPOSITION_DEVICE,
- SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
- Cursor = HWC2_COMPOSITION_CURSOR,
- Sideband = HWC2_COMPOSITION_SIDEBAND,
- };
-
- Composition() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct DisplayType final : public Wrapper<HWC2::DisplayType> {
- enum : ValueType {
- Invalid = HWC2_DISPLAY_TYPE_INVALID,
- Physical = HWC2_DISPLAY_TYPE_PHYSICAL,
- Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
- };
-
- DisplayType() : Base(Invalid) {}
- using Base::Base;
-};
-
-struct Error final : public Wrapper<HWC2::Error> {
- enum : ValueType {
- None = HWC2_ERROR_NONE,
- BadConfig = HWC2_ERROR_BAD_CONFIG,
- BadDisplay = HWC2_ERROR_BAD_DISPLAY,
- BadLayer = HWC2_ERROR_BAD_LAYER,
- BadParameter = HWC2_ERROR_BAD_PARAMETER,
- HasChanges = HWC2_ERROR_HAS_CHANGES,
- NoResources = HWC2_ERROR_NO_RESOURCES,
- NotValidated = HWC2_ERROR_NOT_VALIDATED,
- Unsupported = HWC2_ERROR_UNSUPPORTED,
- };
-
- Error() : Base(None) {}
- using Base::Base;
-};
-
-struct LayerRequest final : public Wrapper<HWC2::LayerRequest> {
- enum : ValueType {
- ClearClientTarget = HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET,
- };
-
- LayerRequest() : Base(0) {}
- using Base::Base;
-};
-
-struct PowerMode final : public Wrapper<HWC2::PowerMode> {
- enum : ValueType {
- Off = HWC2_POWER_MODE_OFF,
- DozeSuspend = HWC2_POWER_MODE_DOZE_SUSPEND,
- Doze = HWC2_POWER_MODE_DOZE,
- On = HWC2_POWER_MODE_ON,
- };
-
- PowerMode() : Base(Off) {}
- using Base::Base;
-};
-
-struct Transform final : public Wrapper<HWC2::Transform> {
- enum : ValueType {
- None = 0,
- FlipH = HWC_TRANSFORM_FLIP_H,
- FlipV = HWC_TRANSFORM_FLIP_V,
- Rotate90 = HWC_TRANSFORM_ROT_90,
- Rotate180 = HWC_TRANSFORM_ROT_180,
- Rotate270 = HWC_TRANSFORM_ROT_270,
- FlipHRotate90 = HWC_TRANSFORM_FLIP_H_ROT_90,
- FlipVRotate90 = HWC_TRANSFORM_FLIP_V_ROT_90,
- };
-
- Transform() : Base(None) {}
- using Base::Base;
-};
-
-struct Vsync final : public Wrapper<HWC2::Vsync> {
- enum : ValueType {
- Invalid = HWC2_VSYNC_INVALID,
- Enable = HWC2_VSYNC_ENABLE,
- Disable = HWC2_VSYNC_DISABLE,
- };
-
- Vsync() : Base(Invalid) {}
- using Base::Base;
-};
-
-// Utility color type.
-struct Color final {
- Color(const Color&) = default;
- Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- Color(hwc_color_t color) : r(color.r), g(color.g), b(color.b), a(color.a) {}
-
- // NOLINTNEXTLINE(google-explicit-constructor)
- operator hwc_color_t() const { return {r, g, b, a}; }
-
- uint8_t r __attribute__((aligned(1)));
- uint8_t g __attribute__((aligned(1)));
- uint8_t b __attribute__((aligned(1)));
- uint8_t a __attribute__((aligned(1)));
-};
-
-// Utility rectangle type.
-struct Rect final {
- // TODO(eieio): Implicit conversion to/from Android rect types.
-
- int32_t left __attribute__((aligned(4)));
- int32_t top __attribute__((aligned(4)));
- int32_t right __attribute__((aligned(4)));
- int32_t bottom __attribute__((aligned(4)));
-};
-
-} // namespace HWC
-
-#endif // ANDROID_LIBVRFLINGER_HWCTYPES_H
diff --git a/libs/vr/libvrflinger/include/dvr/vr_flinger.h b/libs/vr/libvrflinger/include/dvr/vr_flinger.h
deleted file mode 100644
index ae52076..0000000
--- a/libs/vr/libvrflinger/include/dvr/vr_flinger.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef ANDROID_DVR_VR_FLINGER_H_
-#define ANDROID_DVR_VR_FLINGER_H_
-
-#include <thread>
-#include <memory>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include <pdx/service_dispatcher.h>
-#include <vr/vr_manager/vr_manager.h>
-
-namespace android {
-
-namespace Hwc2 {
-class Composer;
-} // namespace Hwc2
-
-namespace dvr {
-
-class DisplayService;
-
-class VrFlinger {
- public:
- using RequestDisplayCallback = std::function<void(bool)>;
- static std::unique_ptr<VrFlinger> Create(
- Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
- ~VrFlinger();
-
- // These functions are all called on surface flinger's main thread.
- void OnBootFinished();
- void GrantDisplayOwnership();
- void SeizeDisplayOwnership();
-
- // dump all vr flinger state.
- std::string Dump();
-
- private:
- VrFlinger();
- bool Init(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback);
-
- // Needs to be a separate class for binder's ref counting
- class PersistentVrStateCallback : public BnPersistentVrStateCallbacks {
- public:
- explicit PersistentVrStateCallback(
- RequestDisplayCallback request_display_callback)
- : request_display_callback_(request_display_callback) {}
- void onPersistentVrStateChanged(bool enabled) override;
- private:
- RequestDisplayCallback request_display_callback_;
- };
-
- std::thread dispatcher_thread_;
- std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher_;
- std::shared_ptr<android::dvr::DisplayService> display_service_;
- sp<PersistentVrStateCallback> persistent_vr_state_callback_;
- RequestDisplayCallback request_display_callback_;
-};
-
-} // namespace dvr
-} // namespace android
-
-#endif // ANDROID_DVR_VR_FLINGER_H_
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
deleted file mode 100644
index 095f556..0000000
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ /dev/null
@@ -1,47 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-shared_libs = [
- "android.hardware.configstore-utils",
- "android.hardware.configstore@1.0",
- "libbinder",
- "libbufferhubqueue",
- "libcutils",
- "libgui",
- "libhidlbase",
- "liblog",
- "libui",
- "libutils",
- "libnativewindow",
- "libpdx_default_transport",
- "libSurfaceFlingerProp",
-]
-
-static_libs = [
- "libdisplay",
-]
-
-cc_test {
- srcs: ["vrflinger_test.cpp"],
- // See go/apct-presubmit for documentation on how this .filter file is used
- // by Android's automated testing infrastructure for test filtering.
- data: ["vrflinger_test.filter"],
- static_libs: static_libs,
- shared_libs: shared_libs,
- cflags: [
- "-DLOG_TAG=\"VrFlingerTest\"",
- "-DTRACE=0",
- "-O0",
- "-g",
- "-Wall",
- "-Werror",
- ],
- header_libs: ["libsurfaceflinger_headers"],
- name: "vrflinger_test",
-}
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
deleted file mode 100644
index ac44f74..0000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-#include <SurfaceFlingerProperties.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware_buffer.h>
-#include <binder/IServiceManager.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <gtest/gtest.h>
-#include <gui/ISurfaceComposer.h>
-#include <log/log.h>
-#include <utils/StrongPointer.h>
-
-#include <chrono>
-#include <memory>
-#include <mutex>
-#include <optional>
-#include <thread>
-
-#include <private/dvr/display_client.h>
-
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using android::dvr::display::DisplayClient;
-using android::dvr::display::Surface;
-using android::dvr::display::SurfaceAttribute;
-using android::dvr::display::SurfaceAttributeValue;
-
-namespace android {
-namespace dvr {
-
-// The transaction code for asking surface flinger if vr flinger is active. This
-// is done as a hidden api since it's only used for tests. See the "case 1028"
-// block in SurfaceFlinger::onTransact() in SurfaceFlinger.cpp.
-constexpr uint32_t kIsVrFlingerActiveTransactionCode = 1028;
-
-// The maximum amount of time to give vr flinger to activate/deactivate. If the
-// switch hasn't completed in this amount of time, the test will fail.
-constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1);
-
-// How long to wait between each check to see if the vr flinger switch
-// completed.
-constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50);
-
-// A Binder connection to surface flinger.
-class SurfaceFlingerConnection {
- public:
- static std::unique_ptr<SurfaceFlingerConnection> Create() {
- sp<ISurfaceComposer> surface_flinger = interface_cast<ISurfaceComposer>(
- defaultServiceManager()->getService(String16("SurfaceFlinger")));
- if (surface_flinger == nullptr) {
- return nullptr;
- }
-
- return std::unique_ptr<SurfaceFlingerConnection>(
- new SurfaceFlingerConnection(surface_flinger));
- }
-
- // Returns true if the surface flinger process is still running. We use this
- // to detect if surface flinger has crashed.
- bool IsAlive() {
- IInterface::asBinder(surface_flinger_)->pingBinder();
- return IInterface::asBinder(surface_flinger_)->isBinderAlive();
- }
-
- // Return true if vr flinger is currently active, false otherwise. If there's
- // an error communicating with surface flinger, std::nullopt is returned.
- std::optional<bool> IsVrFlingerActive() {
- Parcel data, reply;
- status_t result =
- data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
- if (result != OK) {
- return std::nullopt;
- }
- result = IInterface::asBinder(surface_flinger_)
- ->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
- if (result != OK) {
- return std::nullopt;
- }
- bool vr_flinger_active;
- result = reply.readBool(&vr_flinger_active);
- if (result != OK) {
- return std::nullopt;
- }
- return vr_flinger_active;
- }
-
- enum class VrFlingerSwitchResult : int8_t {
- kSuccess,
- kTimedOut,
- kCommunicationError,
- kSurfaceFlingerDied
- };
-
- // Wait for vr flinger to become active or inactive.
- VrFlingerSwitchResult WaitForVrFlinger(bool wait_active) {
- return WaitForVrFlingerTimed(wait_active, kVrFlingerSwitchPollInterval,
- kVrFlingerSwitchMaxTime);
- }
-
- // Wait for vr flinger to become active or inactive, specifying custom timeouts.
- VrFlingerSwitchResult WaitForVrFlingerTimed(bool wait_active,
- std::chrono::milliseconds pollInterval, std::chrono::seconds timeout) {
- auto start_time = std::chrono::steady_clock::now();
- while (1) {
- std::this_thread::sleep_for(pollInterval);
- if (!IsAlive()) {
- return VrFlingerSwitchResult::kSurfaceFlingerDied;
- }
- std::optional<bool> vr_flinger_active = IsVrFlingerActive();
- if (!vr_flinger_active.has_value()) {
- return VrFlingerSwitchResult::kCommunicationError;
- }
- if (vr_flinger_active.value() == wait_active) {
- return VrFlingerSwitchResult::kSuccess;
- } else if (std::chrono::steady_clock::now() - start_time > timeout) {
- return VrFlingerSwitchResult::kTimedOut;
- }
- }
- }
-
- private:
- SurfaceFlingerConnection(sp<ISurfaceComposer> surface_flinger)
- : surface_flinger_(surface_flinger) {}
-
- sp<ISurfaceComposer> surface_flinger_ = nullptr;
-};
-
-// This test activates vr flinger by creating a vr flinger surface, then
-// deactivates vr flinger by destroying the surface. We verify that vr flinger
-// is activated and deactivated as expected, and that surface flinger doesn't
-// crash.
-//
-// If the device doesn't support vr flinger (as repoted by ConfigStore), the
-// test does nothing.
-//
-// If the device is a standalone vr device, the test also does nothing, since
-// this test verifies the behavior of display handoff from surface flinger to vr
-// flinger and back, and standalone devices never hand control of the display
-// back to surface flinger.
-TEST(VrFlingerTest, ActivateDeactivate) {
- android::ProcessState::self()->startThreadPool();
-
- // Exit immediately if the device doesn't support vr flinger. This ConfigStore
- // check is the same mechanism used by surface flinger to decide if it should
- // initialize vr flinger.
- bool vr_flinger_enabled = android::sysprop::use_vr_flinger(false);
- if (!vr_flinger_enabled) {
- return;
- }
-
- auto surface_flinger_connection = SurfaceFlingerConnection::Create();
- ASSERT_NE(surface_flinger_connection, nullptr);
-
- // Verify we start off with vr flinger disabled.
- ASSERT_TRUE(surface_flinger_connection->IsAlive());
- auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive();
- ASSERT_TRUE(vr_flinger_active.has_value());
- ASSERT_FALSE(vr_flinger_active.value());
-
- // Create a vr flinger surface, and verify vr flinger becomes active.
- // Introduce a scope so that, at the end of the scope, the vr flinger surface
- // is destroyed, and vr flinger deactivates.
- {
- auto display_client = DisplayClient::Create();
- ASSERT_NE(display_client, nullptr);
- auto metrics = display_client->GetDisplayMetrics();
- ASSERT_TRUE(metrics.ok());
-
- auto surface = Surface::CreateSurface({
- {SurfaceAttribute::Direct, SurfaceAttributeValue(true)},
- {SurfaceAttribute::Visible, SurfaceAttributeValue(true)},
- });
- ASSERT_TRUE(surface.ok());
- ASSERT_TRUE(surface.get() != nullptr);
-
- auto queue = surface.get()->CreateQueue(
- metrics.get().display_width, metrics.get().display_height,
- /*layer_count=*/1, AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
- AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
- AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
- /*capacity=*/1,
- /*metadata_size=*/0);
- ASSERT_TRUE(queue.ok());
- ASSERT_TRUE(queue.get() != nullptr);
-
- size_t slot;
- pdx::LocalHandle release_fence;
- auto buffer = queue.get()->Dequeue(/*timeout=*/0, &slot, &release_fence);
- ASSERT_TRUE(buffer.ok());
- ASSERT_TRUE(buffer.get() != nullptr);
-
- ASSERT_EQ(buffer.get()->width(), metrics.get().display_width);
- ASSERT_EQ(buffer.get()->height(), metrics.get().display_height);
-
- void* raw_buf = nullptr;
- ASSERT_GE(buffer.get()->buffer()->Lock(
- AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, /*x=*/0, /*y=*/0,
- buffer.get()->width(), buffer.get()->height(), &raw_buf),
- 0);
- ASSERT_NE(raw_buf, nullptr);
- uint32_t* pixels = static_cast<uint32_t*>(raw_buf);
-
- for (int i = 0; i < buffer.get()->stride() * buffer.get()->height(); ++i) {
- pixels[i] = 0x0000ff00;
- }
-
- ASSERT_GE(buffer.get()->buffer()->Unlock(), 0);
-
- ASSERT_GE(buffer.get()->Post(/*ready_fence=*/pdx::LocalHandle()), 0);
-
- ASSERT_EQ(
- surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/true),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
- }
-
- // Now that the vr flinger surface is destroyed, vr flinger should deactivate.
- ASSERT_EQ(
- surface_flinger_connection->WaitForVrFlinger(/*wait_active=*/false),
- SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess);
-}
-
-} // namespace dvr
-} // namespace android
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.filter b/libs/vr/libvrflinger/tests/vrflinger_test.filter
deleted file mode 100644
index 030bb7b..0000000
--- a/libs/vr/libvrflinger/tests/vrflinger_test.filter
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "presubmit": {
- "filter": "BootVrFlingerTest.*"
- }
-}
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
deleted file mode 100644
index a8a8476..0000000
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <dvr/vr_flinger.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <memory>
-
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-#include <private/dvr/display_client.h>
-#include <processgroup/sched_policy.h>
-#include <sys/prctl.h>
-#include <sys/resource.h>
-
-#include <functional>
-
-#include "DisplayHardware/ComposerHal.h"
-#include "display_manager_service.h"
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-std::unique_ptr<VrFlinger> VrFlinger::Create(
- Hwc2::Composer* hidl, hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- std::unique_ptr<VrFlinger> vr_flinger(new VrFlinger);
- if (vr_flinger->Init(hidl, primary_display_id, request_display_callback))
- return vr_flinger;
- else
- return nullptr;
-}
-
-VrFlinger::VrFlinger() {}
-
-VrFlinger::~VrFlinger() {
- if (persistent_vr_state_callback_.get()) {
- sp<IVrManager> vr_manager = interface_cast<IVrManager>(
- defaultServiceManager()->checkService(String16("vrmanager")));
- if (vr_manager.get()) {
- vr_manager->unregisterPersistentVrStateListener(
- persistent_vr_state_callback_);
- }
- }
-
- if (dispatcher_)
- dispatcher_->SetCanceled(true);
- if (dispatcher_thread_.joinable())
- dispatcher_thread_.join();
-}
-
-bool VrFlinger::Init(Hwc2::Composer* hidl,
- hwc2_display_t primary_display_id,
- RequestDisplayCallback request_display_callback) {
- if (!hidl || !request_display_callback)
- return false;
-
- std::shared_ptr<android::pdx::Service> service;
-
- ALOGI("Starting up VrFlinger...");
-
- // We need to be able to create endpoints with full perms.
- umask(0000);
-
- android::ProcessState::self()->startThreadPool();
-
- request_display_callback_ = request_display_callback;
-
- dispatcher_ = android::pdx::ServiceDispatcher::Create();
- CHECK_ERROR(!dispatcher_, error, "Failed to create service dispatcher.");
-
- display_service_ = android::dvr::DisplayService::Create(
- hidl, primary_display_id, request_display_callback);
- CHECK_ERROR(!display_service_, error, "Failed to create display service.");
- dispatcher_->AddService(display_service_);
-
- service = android::dvr::DisplayManagerService::Create(display_service_);
- CHECK_ERROR(!service, error, "Failed to create display manager service.");
- dispatcher_->AddService(service);
-
- dispatcher_thread_ = std::thread([this]() {
- prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
- ALOGI("Entering message loop.");
-
- setpriority(PRIO_PROCESS, 0, android::PRIORITY_URGENT_DISPLAY);
- set_sched_policy(0, SP_FOREGROUND);
-
- int ret = dispatcher_->EnterDispatchLoop();
- if (ret < 0) {
- ALOGE("Dispatch loop exited because: %s\n", strerror(-ret));
- }
- });
-
- return true;
-
-error:
- return false;
-}
-
-void VrFlinger::OnBootFinished() {
- display_service_->OnBootFinished();
- sp<IVrManager> vr_manager = interface_cast<IVrManager>(
- defaultServiceManager()->checkService(String16("vrmanager")));
- if (vr_manager.get()) {
- persistent_vr_state_callback_ =
- new PersistentVrStateCallback(request_display_callback_);
- vr_manager->registerPersistentVrStateListener(
- persistent_vr_state_callback_);
- } else {
- ALOGE("Unable to register vr flinger for persistent vr mode changes");
- }
-}
-
-void VrFlinger::GrantDisplayOwnership() {
- display_service_->GrantDisplayOwnership();
-}
-
-void VrFlinger::SeizeDisplayOwnership() {
- display_service_->SeizeDisplayOwnership();
-}
-
-std::string VrFlinger::Dump() {
- // TODO(karthikrs): Add more state information here.
- return display_service_->DumpState(0/*unused*/);
-}
-
-void VrFlinger::PersistentVrStateCallback::onPersistentVrStateChanged(
- bool enabled) {
- ALOGV("Notified persistent vr mode is %s", enabled ? "on" : "off");
- // TODO(eieio): Determine the correct signal to request display control.
- // Persistent VR mode is not enough.
- // request_display_callback_(enabled);
-}
-} // namespace dvr
-} // namespace android
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index de36a7a..f4dbe49 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -29,6 +29,7 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <stdlib.h>
#include <string.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <condition_variable>
#include <deque>
@@ -564,9 +565,11 @@
newList.push_back(EGL_NONE);
}
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
// Gets the native pixel format corrsponding to the passed EGLConfig.
void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
- android_pixel_format* format) {
+ PixelFormat* format) {
// Set the native window's buffers format to match what this config requests.
// Whether to use sRGB gamma is not part of the EGLconfig, but is part
// of our native format. So if sRGB gamma is requested, we have to
@@ -599,28 +602,30 @@
// strip colorspace from attribs.
// endif
if (a == 0) {
- if (colorDepth <= 16) {
- *format = HAL_PIXEL_FORMAT_RGB_565;
+ if (8 == r && 0 == g && 0 == b) {
+ *format = PixelFormat::R_8;
+ } else if (colorDepth <= 16) {
+ *format = PixelFormat::RGB_565;
} else {
if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ *format = PixelFormat::RGBA_1010102;
} else {
- *format = HAL_PIXEL_FORMAT_RGBX_8888;
+ *format = PixelFormat::RGBX_8888;
}
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ *format = PixelFormat::RGBA_FP16;
}
}
} else {
if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ *format = PixelFormat::RGBA_1010102;
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_8888;
+ *format = PixelFormat::RGBA_8888;
}
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ *format = PixelFormat::RGBA_FP16;
}
}
}
@@ -678,7 +683,7 @@
}
EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
+ PixelFormat format;
getNativePixelFormat(iDpy, cnx, config, &format);
// now select correct colorspace and dataspace based on user's attribute list
@@ -694,7 +699,7 @@
attrib_list = strippedAttribList.data();
if (!cnx->useAngle) {
- int err = native_window_set_buffers_format(window, format);
+ int err = native_window_set_buffers_format(window, static_cast<int>(format));
if (err != 0) {
ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
@@ -818,7 +823,7 @@
if (!dp) return EGL_NO_SURFACE;
EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
+ PixelFormat format;
getNativePixelFormat(iDpy, cnx, config, &format);
// Select correct colorspace based on user's attribute list
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
index d6fc695..d205231 100644
--- a/services/automotive/display/AutomotiveDisplayProxyService.cpp
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -34,7 +34,10 @@
sp<IBinder> displayToken = nullptr;
sp<SurfaceControl> surfaceControl = nullptr;
if (it == mDisplays.end()) {
- displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+ if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ }
+
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
return nullptr;
@@ -157,7 +160,11 @@
HwDisplayConfig activeConfig;
HwDisplayState activeState;
- auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id));
+ sp<IBinder> displayToken;
+ if (const auto displayId = DisplayId::fromValue<PhysicalDisplayId>(id)) {
+ displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
+ }
+
if (displayToken == nullptr) {
ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
} else {
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 73e5749..8cdb706 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -73,7 +73,6 @@
"libui",
"lib-platform-compat-native-api",
"server_configurable_flags",
- "InputFlingerProperties",
],
static_libs: [
"libattestation",
@@ -125,7 +124,7 @@
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
- "VibrationElement.cpp"
+ "VibrationElement.cpp",
],
}
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index a9cbd5a..19cad7b 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -68,7 +68,8 @@
}
static bool isTouchEvent(const NotifyMotionArgs& args) {
- return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+ return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
+ isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
}
// --- ClassifierEvent ---
@@ -345,7 +346,7 @@
// --- InputClassifier ---
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+InputClassifier::InputClassifier(InputListenerInterface& listener)
: mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
void InputClassifier::setMotionClassifierEnabled(bool enabled) {
@@ -369,12 +370,12 @@
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
// pass through
- mListener->notifyConfigurationChanged(args);
+ mListener.notifyConfigurationChanged(args);
}
void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
// pass through
- mListener->notifyKey(args);
+ mListener.notifyKey(args);
}
void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
@@ -382,28 +383,28 @@
// MotionClassifier is only used for touch events, for now
const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
if (!sendToMotionClassifier) {
- mListener->notifyMotion(args);
+ mListener.notifyMotion(args);
return;
}
NotifyMotionArgs newArgs(*args);
newArgs.classification = mMotionClassifier->classify(newArgs);
- mListener->notifyMotion(&newArgs);
+ mListener.notifyMotion(&newArgs);
}
void InputClassifier::notifySensor(const NotifySensorArgs* args) {
// pass through
- mListener->notifySensor(args);
+ mListener.notifySensor(args);
}
void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
// pass through
- mListener->notifyVibratorState(args);
+ mListener.notifyVibratorState(args);
}
void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
// pass through
- mListener->notifySwitch(args);
+ mListener.notifySwitch(args);
}
void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
@@ -412,12 +413,12 @@
mMotionClassifier->reset(*args);
}
// continue to next stage
- mListener->notifyDeviceReset(args);
+ mListener.notifyDeviceReset(args);
}
void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
// pass through
- mListener->notifyPointerCaptureChanged(args);
+ mListener.notifyPointerCaptureChanged(args);
}
void InputClassifier::setMotionClassifier(
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 1eef020..deeae7c 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -18,7 +18,6 @@
#define _UI_INPUT_CLASSIFIER_H
#include <android-base/thread_annotations.h>
-#include <utils/RefBase.h>
#include <thread>
#include <unordered_map>
@@ -88,7 +87,7 @@
* Base interface for an InputListener stage.
* Provides classification to events.
*/
-class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
+class InputClassifierInterface : public InputListenerInterface {
public:
virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
@@ -96,7 +95,7 @@
* This method may be called on any thread (usually by the input manager).
*/
virtual void dump(std::string& dump) = 0;
-protected:
+
InputClassifierInterface() { }
virtual ~InputClassifierInterface() { }
};
@@ -223,7 +222,7 @@
*/
class InputClassifier : public InputClassifierInterface {
public:
- explicit InputClassifier(const sp<InputListenerInterface>& listener);
+ explicit InputClassifier(InputListenerInterface& listener);
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -245,7 +244,7 @@
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
// The next stage to pass input events to
- sp<InputListenerInterface> mListener;
+ InputListenerInterface& mListener;
std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
std::thread mInitializeMotionClassifierThread;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 71b0f5f..158d0eb 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -44,8 +44,8 @@
return id == rhs.id && eventTime == rhs.eventTime;
}
-void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyConfigurationChanged(this);
+void NotifyConfigurationChangedArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyConfigurationChanged(this);
}
// --- NotifyKeyArgs ---
@@ -89,8 +89,8 @@
downTime == rhs.downTime;
}
-void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyKey(this);
+void NotifyKeyArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyKey(this);
}
// --- NotifyMotionArgs ---
@@ -188,8 +188,8 @@
return true;
}
-void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyMotion(this);
+void NotifyMotionArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyMotion(this);
}
// --- NotifySwitchArgs ---
@@ -212,8 +212,8 @@
switchValues == rhs.switchValues && switchMask == rhs.switchMask;
}
-void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifySwitch(this);
+void NotifySwitchArgs::notify(InputListenerInterface& listener) const {
+ listener.notifySwitch(this);
}
// --- NotifySensorArgs ---
@@ -247,8 +247,8 @@
hwTimestamp == rhs.hwTimestamp && values == rhs.values;
}
-void NotifySensorArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifySensor(this);
+void NotifySensorArgs::notify(InputListenerInterface& listener) const {
+ listener.notifySensor(this);
}
// --- NotifyVibratorStateArgs ---
@@ -265,8 +265,8 @@
isOn == rhs.isOn;
}
-void NotifyVibratorStateArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyVibratorState(this);
+void NotifyVibratorStateArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyVibratorState(this);
}
// --- NotifyDeviceResetArgs ---
@@ -281,8 +281,8 @@
return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId;
}
-void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyDeviceReset(this);
+void NotifyDeviceResetArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyDeviceReset(this);
}
// --- NotifyPointerCaptureChangedArgs ---
@@ -299,8 +299,8 @@
return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request;
}
-void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
- listener->notifyPointerCaptureChanged(this);
+void NotifyPointerCaptureChangedArgs::notify(InputListenerInterface& listener) const {
+ listener.notifyPointerCaptureChanged(this);
}
// --- QueuedInputListener ---
@@ -312,9 +312,8 @@
}
}
-QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
- mInnerListener(innerListener) {
-}
+QueuedInputListener::QueuedInputListener(InputListenerInterface& innerListener)
+ : mInnerListener(innerListener) {}
QueuedInputListener::~QueuedInputListener() {
size_t count = mArgsQueue.size();
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 7b3658d..221e193 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -58,8 +58,8 @@
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = createInputDispatcher(dispatcherPolicy);
- mClassifier = new InputClassifier(mDispatcher);
- mReader = createInputReader(readerPolicy, mClassifier);
+ mClassifier = std::make_unique<InputClassifier>(*mDispatcher);
+ mReader = createInputReader(readerPolicy, *mClassifier);
}
InputManager::~InputManager() {
@@ -102,16 +102,16 @@
return status;
}
-sp<InputReaderInterface> InputManager::getReader() {
- return mReader;
+InputReaderInterface& InputManager::getReader() {
+ return *mReader;
}
-sp<InputClassifierInterface> InputManager::getClassifier() {
- return mClassifier;
+InputClassifierInterface& InputManager::getClassifier() {
+ return *mClassifier;
}
-sp<InputDispatcherInterface> InputManager::getDispatcher() {
- return mDispatcher;
+InputDispatcherInterface& InputManager::getDispatcher() {
+ return *mDispatcher;
}
// Used by tests only.
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 4c07c22..a6baf2f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -30,7 +30,6 @@
#include <input/InputTransport.h>
#include <android/os/BnInputFlinger.h>
-#include <android/os/IInputFlinger.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -76,13 +75,13 @@
virtual status_t stop() = 0;
/* Gets the input reader. */
- virtual sp<InputReaderInterface> getReader() = 0;
+ virtual InputReaderInterface& getReader() = 0;
/* Gets the input classifier */
- virtual sp<InputClassifierInterface> getClassifier() = 0;
+ virtual InputClassifierInterface& getClassifier() = 0;
/* Gets the input dispatcher. */
- virtual sp<InputDispatcherInterface> getDispatcher() = 0;
+ virtual InputDispatcherInterface& getDispatcher() = 0;
};
class InputManager : public InputManagerInterface, public BnInputFlinger {
@@ -97,9 +96,9 @@
status_t start() override;
status_t stop() override;
- sp<InputReaderInterface> getReader() override;
- sp<InputClassifierInterface> getClassifier() override;
- sp<InputDispatcherInterface> getDispatcher() override;
+ InputReaderInterface& getReader() override;
+ InputClassifierInterface& getClassifier() override;
+ InputDispatcherInterface& getDispatcher() override;
status_t dump(int fd, const Vector<String16>& args) override;
binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override;
@@ -107,11 +106,11 @@
binder::Status setFocusedWindow(const gui::FocusRequest&) override;
private:
- sp<InputReaderInterface> mReader;
+ std::unique_ptr<InputReaderInterface> mReader;
- sp<InputClassifierInterface> mClassifier;
+ std::unique_ptr<InputClassifierInterface> mClassifier;
- sp<InputDispatcherInterface> mDispatcher;
+ std::unique_ptr<InputDispatcherInterface> mDispatcher;
};
} // namespace android
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 05ef489..a864cf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,12 +19,12 @@
//#define LOG_NDEBUG 0
#include "InputReaderBase.h"
-#include <ftl/NamedEnum.h>
#include "input/DisplayViewport.h"
#include "input/Input.h"
-#include <android/log.h>
#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <ftl/enum.h>
#define INDENT " "
#define INDENT2 " "
@@ -117,7 +117,7 @@
}
if (count > 1) {
ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
- NamedEnum::string(type).c_str());
+ ftl::enum_string(type).c_str());
}
return result;
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 6ce0313..41e9ce2 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -21,6 +21,7 @@
#include <gui/constants.h>
#include "../dispatcher/InputDispatcher.h"
+using android::base::Result;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
using android::os::IInputConstants;
@@ -162,15 +163,15 @@
}
protected:
- explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
- : mDispatcher(dispatcher) {
- mClientChannel = *mDispatcher->createInputChannel(name);
+ explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) {
+ Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name);
+ LOG_ALWAYS_FATAL_IF(!channelResult.ok());
+ mClientChannel = std::move(*channelResult);
mConsumer = std::make_unique<InputConsumer>(mClientChannel);
}
virtual ~FakeInputReceiver() {}
- sp<InputDispatcher> mDispatcher;
std::shared_ptr<InputChannel> mClientChannel;
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
@@ -182,7 +183,7 @@
static const int32_t HEIGHT = 200;
FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, const std::string name)
+ InputDispatcher& dispatcher, const std::string name)
: FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
inputApplicationHandle->updateInfo();
updateInfo();
@@ -236,8 +237,8 @@
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
identityTransform, /* xPrecision */ 0,
/* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, currentTime,
+ currentTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
return event;
}
@@ -272,13 +273,13 @@
static void benchmarkNotifyMotion(benchmark::State& state) {
// Create dispatcher
sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
- sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher->start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
@@ -306,13 +307,13 @@
static void benchmarkInjectMotion(benchmark::State& state) {
// Create dispatcher
sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
- sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ std::unique_ptr<InputDispatcher> dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
dispatcher->start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, *dispatcher, "Fake Window");
dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 171f2b5..4757d31 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -68,7 +68,6 @@
"libutils",
"lib-platform-compat-native-api",
"server_configurable_flags",
- "InputFlingerProperties",
],
static_libs: [
"libattestation",
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index cee9c39..b4497fd 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -22,7 +22,7 @@
Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator)
- : status(STATUS_NORMAL),
+ : status(Status::NORMAL),
inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel),
@@ -40,19 +40,6 @@
return "?";
}
-const char* Connection::getStatusLabel() const {
- switch (status) {
- case STATUS_NORMAL:
- return "NORMAL";
- case STATUS_BROKEN:
- return "BROKEN";
- case STATUS_ZOMBIE:
- return "ZOMBIE";
- default:
- return "UNKNOWN";
- }
-}
-
std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
if ((*it)->seq == seq) {
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index c4262ad..dc6a081 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -20,6 +20,7 @@
#include "InputState.h"
#include <input/InputTransport.h>
+#include <utils/RefBase.h>
#include <deque>
namespace android::inputdispatcher {
@@ -32,13 +33,16 @@
virtual ~Connection();
public:
- enum Status {
+ enum class Status {
// Everything is peachy.
- STATUS_NORMAL,
+ NORMAL,
// An unrecoverable communication error has occurred.
- STATUS_BROKEN,
+ BROKEN,
// The input channel has been unregistered.
- STATUS_ZOMBIE
+ ZOMBIE,
+
+ ftl_first = NORMAL,
+ ftl_last = ZOMBIE,
};
Status status;
@@ -65,7 +69,6 @@
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
const std::string getWindowName() const;
- const char* getStatusLabel() const;
std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
};
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index b3c5709..4636820 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -18,7 +18,7 @@
#define _UI_INPUT_INPUTDISPATCHER_DRAGSTATE_H
#include <gui/WindowInfo.h>
-#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
#include <string>
namespace android {
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 82b4fe4..936ecf9 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -32,25 +32,26 @@
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
entry.action,
- entry.downTime,
entry.flags & VERIFIED_KEY_EVENT_FLAGS,
+ entry.downTime,
entry.keyCode,
entry.scanCode,
entry.metaState,
entry.repeatCount};
}
-VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) {
- const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
- const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
+ const ui::Transform& rawTransform) {
+ const vec2 rawXY = MotionEvent::calculateTransformedXY(entry.source, rawTransform,
+ entry.pointerCoords[0].getXYValue());
const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
- rawX,
- rawY,
+ rawXY.x,
+ rawXY.y,
actionMasked,
- entry.downTime,
entry.flags & VERIFIED_MOTION_EVENT_FLAGS,
+ entry.downTime,
entry.metaState,
entry.buttonState};
}
@@ -174,12 +175,13 @@
if (!GetBoolProperty("ro.debuggable", false)) {
return "KeyEvent";
}
- return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
- ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
- "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
+ return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
+ ", action=%s, "
+ "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
- flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
+ deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
+ KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode),
+ keyCode, scanCode, metaState, repeatCount, policyFlags);
}
void KeyEntry::recycle() {
@@ -191,6 +193,18 @@
interceptKeyWakeupTime = 0;
}
+// --- TouchModeEntry ---
+
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode)
+ : EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
+ inTouchMode(inTouchMode) {}
+
+TouchModeEntry::~TouchModeEntry() {}
+
+std::string TouchModeEntry::getDescription() const {
+ return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false");
+}
+
// --- MotionEntry ---
MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
@@ -235,12 +249,12 @@
}
std::string msg;
msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
- ", source=0x%08x, displayId=%" PRId32
+ ", source=%s, displayId=%" PRId32
", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, eventTime, source, displayId,
+ deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
buttonState, motionClassificationToString(classification), edgeFlags,
xPrecision, yPrecision, xCursorPosition, yCursorPosition);
@@ -275,9 +289,10 @@
std::string SensorEntry::getDescription() const {
std::string msg;
- msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, "
+ msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
"accuracy=0x%08x, hwTimestamp=%" PRId64,
- deviceId, source, sensorType, accuracy, hwTimestamp);
+ deviceId, inputEventSourceToString(source).c_str(),
+ ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
if (!GetBoolProperty("ro.debuggable", false)) {
for (size_t i = 0; i < values.size(); i++) {
@@ -296,15 +311,14 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor,
- uint32_t displayOrientation, int2 displaySize)
+ const ui::Transform& transform, const ui::Transform& rawTransform,
+ float globalScaleFactor)
: seq(nextSeq()),
eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
transform(transform),
+ rawTransform(rawTransform),
globalScaleFactor(globalScaleFactor),
- displayOrientation(displayOrientation),
- displaySize(displaySize),
deliveryTime(0),
resolvedAction(0),
resolvedFlags(0) {}
@@ -318,16 +332,4 @@
return seq;
}
-// --- CommandEntry ---
-
-CommandEntry::CommandEntry(Command command)
- : command(command),
- eventTime(0),
- keyEntry(nullptr),
- userActivityEventType(0),
- seq(0),
- handled(false) {}
-
-CommandEntry::~CommandEntry() {}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ffe3bb6..477781a 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -39,6 +39,9 @@
SENSOR,
POINTER_CAPTURE_CHANGED,
DRAG,
+ TOUCH_MODE_CHANGED,
+
+ ftl_last = TOUCH_MODE_CHANGED
};
int32_t id;
@@ -185,7 +188,7 @@
float xOffset, float yOffset);
std::string getDescription() const override;
- virtual ~MotionEntry();
+ ~MotionEntry() override;
};
struct SensorEntry : EventEntry {
@@ -207,6 +210,15 @@
~SensorEntry() override;
};
+struct TouchModeEntry : EventEntry {
+ bool inTouchMode;
+
+ TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode);
+ std::string getDescription() const override;
+
+ ~TouchModeEntry() override;
+};
+
// Tracks the progress of dispatching a particular event to a particular connection.
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
@@ -214,9 +226,8 @@
std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
int32_t targetFlags;
ui::Transform transform;
+ ui::Transform rawTransform;
float globalScaleFactor;
- uint32_t displayOrientation;
- int2 displaySize;
// Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
// and will be undefined before that.
nsecs_t deliveryTime; // time when the event was actually delivered
@@ -229,8 +240,8 @@
int32_t resolvedFlags;
DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
- ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation,
- int2 displaySize);
+ const ui::Transform& transform, const ui::Transform& rawTransform,
+ float globalScaleFactor);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -243,56 +254,8 @@
};
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
-VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
-
-class InputDispatcher;
-// A command entry captures state and behavior for an action to be performed in the
-// dispatch loop after the initial processing has taken place. It is essentially
-// a kind of continuation used to postpone sensitive policy interactions to a point
-// in the dispatch loop where it is safe to release the lock (generally after finishing
-// the critical parts of the dispatch cycle).
-//
-// The special thing about commands is that they can voluntarily release and reacquire
-// the dispatcher lock at will. Initially when the command starts running, the
-// dispatcher lock is held. However, if the command needs to call into the policy to
-// do some work, it can release the lock, do the work, then reacquire the lock again
-// before returning.
-//
-// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
-// never calls into the policy while holding its lock.
-//
-// Commands are implicitly 'LockedInterruptible'.
-struct CommandEntry;
-typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
-
-class Connection;
-struct CommandEntry {
- explicit CommandEntry(Command command);
- ~CommandEntry();
-
- Command command;
-
- // parameters for the command (usage varies by command)
- sp<Connection> connection;
- nsecs_t eventTime;
- std::shared_ptr<KeyEntry> keyEntry;
- std::shared_ptr<SensorEntry> sensorEntry;
- std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
- std::string reason;
- int32_t userActivityEventType;
- uint32_t seq;
- bool handled;
- sp<IBinder> connectionToken;
- sp<IBinder> oldToken;
- sp<IBinder> newToken;
- std::string obscuringPackage;
- PointerCaptureRequest pointerCaptureRequest;
- int32_t pid;
- nsecs_t consumeTime; // time when the event was consumed by InputConsumer
- int32_t displayId;
- float x;
- float y;
-};
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
+ const ui::Transform& rawTransform);
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags
index 2836467..2c5fe21 100644
--- a/services/inputflinger/dispatcher/EventLogTags.logtags
+++ b/services/inputflinger/dispatcher/EventLogTags.logtags
@@ -37,6 +37,7 @@
62000 input_interaction (windows|4)
62001 input_focus (window|3),(reason|3)
+62003 input_cancel (window|3),(reason|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 4a75773..600f02b 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -27,7 +27,7 @@
#include <android-base/stringprintf.h>
#include <binder/Binder.h>
-#include <ftl/NamedEnum.h>
+#include <ftl/enum.h>
#include <gui/WindowInfo.h>
#include <log/log.h>
@@ -65,7 +65,7 @@
if (result == Focusability::OK) {
return std::nullopt;
}
- removeFocusReason = NamedEnum::string(result);
+ removeFocusReason = ftl::enum_string(result);
}
// We don't have a focused window or the currently focused window is no longer focusable. Check
@@ -79,7 +79,7 @@
if (result == Focusability::OK) {
return updateFocusedWindow(displayId,
"Window became focusable. Previous reason: " +
- NamedEnum::string(previousResult),
+ ftl::enum_string(previousResult),
requestedFocus, request->windowName);
}
}
@@ -116,7 +116,7 @@
request.token, request.windowName);
}
ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
- request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ request.windowName.c_str(), displayId, ftl::enum_string(result).c_str());
return std::nullopt;
}
@@ -134,7 +134,7 @@
// The requested window is not currently focusable. Wait for the window to become focusable
// but remove focus from the current window so that input events can go into a pending queue
// and be sent to the window when it becomes focused.
- return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
nullptr);
}
@@ -212,7 +212,7 @@
for (const auto& [displayId, request] : mFocusRequestByDisplay) {
auto it = mLastFocusResultByDisplay.find(displayId);
std::string result =
- it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
displayId, request.windowName.c_str(), result.c_str());
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index 1d6cd9a..6d11a77 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -77,6 +77,8 @@
NO_WINDOW,
NOT_FOCUSABLE,
NOT_VISIBLE,
+
+ ftl_last = NOT_VISIBLE
};
// Checks if the window token can be focused on a display. The token can be focused if there is
@@ -113,4 +115,4 @@
std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId);
};
-} // namespace android::inputdispatcher
\ No newline at end of file
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d53495f..c9397c3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -19,35 +19,6 @@
#define LOG_NDEBUG 1
-// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 0
-
-// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 0
-
-// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 0
-
-// Log debug messages about channel creation
-#define DEBUG_CHANNEL_CREATION 0
-
-// Log debug messages about input event injection.
-#define DEBUG_INJECTION 0
-
-// Log debug messages about input focus tracking.
-static constexpr bool DEBUG_FOCUS = false;
-
-// Log debug messages about touch occlusion
-// STOPSHIP(b/169067926): Set to false
-static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
-
-// Log debug messages about the app switch latency optimization.
-#define DEBUG_APP_SWITCH 0
-
-// Log debug messages about hover events.
-#define DEBUG_HOVER 0
-
-#include <InputFlingerProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -55,6 +26,7 @@
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
#include <com/android/internal/compat/IPlatformCompatNative.h>
+#include <ftl/enum.h>
#include <gui/SurfaceComposerClient.h>
#include <input/InputDevice.h>
#include <log/log.h>
@@ -82,6 +54,7 @@
using android::base::HwTimeoutMultiplier;
using android::base::Result;
using android::base::StringPrintf;
+using android::gui::DisplayInfo;
using android::gui::FocusRequest;
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
@@ -94,14 +67,49 @@
namespace android::inputdispatcher {
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-static bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
+namespace {
- return PER_WINDOW_INPUT_ROTATION;
-}
+// Log detailed debug messages about each inbound event notification to the dispatcher.
+constexpr bool DEBUG_INBOUND_EVENT_DETAILS = false;
+
+// Log detailed debug messages about each outbound event processed by the dispatcher.
+constexpr bool DEBUG_OUTBOUND_EVENT_DETAILS = false;
+
+// Log debug messages about the dispatch cycle.
+constexpr bool DEBUG_DISPATCH_CYCLE = false;
+
+// Log debug messages about channel creation
+constexpr bool DEBUG_CHANNEL_CREATION = false;
+
+// Log debug messages about input event injection.
+constexpr bool DEBUG_INJECTION = false;
+
+// Log debug messages about input focus tracking.
+constexpr bool DEBUG_FOCUS = false;
+
+// Log debug messages about touch mode event
+constexpr bool DEBUG_TOUCH_MODE = false;
+
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
+// Log debug messages about the app switch latency optimization.
+constexpr bool DEBUG_APP_SWITCH = false;
+
+// Log debug messages about hover events.
+constexpr bool DEBUG_HOVER = false;
+
+// Temporarily releases a held mutex for the lifetime of the instance.
+// Named to match std::scoped_lock
+class scoped_unlock {
+public:
+ explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
+ ~scoped_unlock() { mMutex.lock(); }
+
+private:
+ std::mutex& mMutex;
+};
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
@@ -135,28 +143,29 @@
// Event log tags. See EventLogTags.logtags for reference
constexpr int LOGTAG_INPUT_INTERACTION = 62000;
constexpr int LOGTAG_INPUT_FOCUS = 62001;
+constexpr int LOGTAG_INPUT_CANCEL = 62003;
-static inline nsecs_t now() {
+inline nsecs_t now() {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
-static inline const char* toString(bool value) {
+inline const char* toString(bool value) {
return value ? "true" : "false";
}
-static inline const std::string toString(sp<IBinder> binder) {
+inline const std::string toString(const sp<IBinder>& binder) {
if (binder == nullptr) {
return "<null>";
}
return StringPrintf("%p", binder.get());
}
-static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
+inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
}
-static bool isValidKeyAction(int32_t action) {
+bool isValidKeyAction(int32_t action) {
switch (action) {
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
@@ -166,7 +175,7 @@
}
}
-static bool validateKeyEvent(int32_t action) {
+bool validateKeyEvent(int32_t action) {
if (!isValidKeyAction(action)) {
ALOGE("Key event has invalid action code 0x%x", action);
return false;
@@ -174,7 +183,7 @@
return true;
}
-static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
+bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
@@ -199,12 +208,12 @@
}
}
-static int64_t millis(std::chrono::nanoseconds t) {
+int64_t millis(std::chrono::nanoseconds t) {
return std::chrono::duration_cast<std::chrono::milliseconds>(t).count();
}
-static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
- const PointerProperties* pointerProperties) {
+bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount,
+ const PointerProperties* pointerProperties) {
if (!isValidMotionAction(action, actionButton, pointerCount)) {
ALOGE("Motion event has invalid action code 0x%x", action);
return false;
@@ -231,7 +240,7 @@
return true;
}
-static std::string dumpRegion(const Region& region) {
+std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
return "<empty>";
}
@@ -252,7 +261,7 @@
return dump;
}
-static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
+std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
constexpr size_t maxEntries = 50; // max events to print
constexpr size_t skipBegin = maxEntries / 2;
const size_t skipEnd = queue.size() - maxEntries / 2;
@@ -291,12 +300,12 @@
* Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
*/
template <typename K, typename V>
-static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+V getValueByKey(const std::unordered_map<K, V>& map, K key) {
auto it = map.find(key);
return it != map.end() ? it->second : V{};
}
-static bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
+bool haveSameToken(const sp<WindowInfoHandle>& first, const sp<WindowInfoHandle>& second) {
if (first == second) {
return true;
}
@@ -308,7 +317,7 @@
return first->getToken() == second->getToken();
}
-static bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
+bool haveSameApplicationToken(const WindowInfo* first, const WindowInfo* second) {
if (first == nullptr || second == nullptr) {
return false;
}
@@ -316,33 +325,18 @@
first->applicationInfo.token == second->applicationInfo.token;
}
-static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
-static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
- std::shared_ptr<EventEntry> eventEntry,
- int32_t inputTargetFlags) {
- if (eventEntry->type == EventEntry::Type::MOTION) {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
- (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
- const ui::Transform identityTransform;
- // Use identity transform for joystick and position-based (touchpad) events because they
- // don't depend on the window transform.
- return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- 1.0f /*globalScaleFactor*/,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
- }
- }
-
+std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ std::shared_ptr<EventEntry> eventEntry,
+ int32_t inputTargetFlags) {
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
- inputTarget.globalScaleFactor,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
+ inputTarget.displayTransform,
+ inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
@@ -393,27 +387,13 @@
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
- firstPointerTransform, inputTarget.globalScaleFactor,
- inputTarget.displayOrientation,
- inputTarget.displaySize);
+ firstPointerTransform, inputTarget.displayTransform,
+ inputTarget.globalScaleFactor);
return dispatchEntry;
}
-static void addGestureMonitors(const std::vector<Monitor>& monitors,
- std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
- float yOffset = 0) {
- if (monitors.empty()) {
- return;
- }
- outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
- for (const Monitor& monitor : monitors) {
- outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
- }
-}
-
-static status_t openInputChannelPair(const std::string& name,
- std::shared_ptr<InputChannel>& serverChannel,
- std::unique_ptr<InputChannel>& clientChannel) {
+status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel,
+ std::unique_ptr<InputChannel>& clientChannel) {
std::unique_ptr<InputChannel> uniqueServerChannel;
status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel);
@@ -422,7 +402,7 @@
}
template <typename T>
-static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
+bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
if (lhs == nullptr && rhs == nullptr) {
return true;
}
@@ -432,7 +412,7 @@
return *lhs == *rhs;
}
-static sp<IPlatformCompatNative> getCompatService() {
+sp<IPlatformCompatNative> getCompatService() {
sp<IBinder> service(defaultServiceManager()->getService(String16("platform_compat_native")));
if (service == nullptr) {
ALOGE("Failed to link to compat service");
@@ -441,7 +421,7 @@
return interface_cast<IPlatformCompatNative>(service);
}
-static KeyEvent createKeyEvent(const KeyEntry& entry) {
+KeyEvent createKeyEvent(const KeyEntry& entry) {
KeyEvent event;
event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
@@ -449,7 +429,7 @@
return event;
}
-static std::optional<int32_t> findMonitorPidByToken(
+std::optional<int32_t> findMonitorPidByToken(
const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay,
const sp<IBinder>& token) {
for (const auto& it : monitorsByDisplay) {
@@ -463,7 +443,7 @@
return std::nullopt;
}
-static bool shouldReportMetricsForConnection(const Connection& connection) {
+bool shouldReportMetricsForConnection(const Connection& connection) {
// Do not keep track of gesture monitors. They receive every event and would disproportionately
// affect the statistics.
if (connection.monitor) {
@@ -476,8 +456,7 @@
return true;
}
-static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry,
- const Connection& connection) {
+bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, const Connection& connection) {
const EventEntry& eventEntry = *dispatchEntry.eventEntry;
const int32_t& inputEventId = eventEntry.id;
if (inputEventId != dispatchEntry.resolvedEventId) {
@@ -513,6 +492,45 @@
return true;
}
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+bool isConnectionResponsive(const Connection& connection) {
+ const nsecs_t currentTime = now();
+ for (const DispatchEntry* entry : connection.waitQueue) {
+ if (entry->timeoutTime < currentTime) {
+ return false;
+ }
+ }
+ return true;
+}
+
+vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+ const vec2 transformedXy = transform.transform(x, y);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
+// Returns true if the event type passed as argument represents a user activity.
+bool isUserActivityEvent(const EventEntry& eventEntry) {
+ switch (eventEntry.type) {
+ case EventEntry::Type::FOCUS:
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ return false;
+ case EventEntry::Type::DEVICE_RESET:
+ case EventEntry::Type::KEY:
+ case EventEntry::Type::MOTION:
+ return true;
+ }
+}
+
+} // namespace
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -529,7 +547,7 @@
// mInTouchMode will be initialized by the WindowManager to the default device config.
// To avoid leaking stack in case that call never comes, and for tests,
// initialize it here anyways.
- mInTouchMode(true),
+ mInTouchMode(kDefaultInTouchMode),
mMaximumObscuringOpacityForTouch(1.0f),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
@@ -539,30 +557,29 @@
mLooper = new Looper(false);
mReporter = createInputReporter();
+ mWindowInfoListener = new DispatcherWindowListener(*this);
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
+
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
}
InputDispatcher::~InputDispatcher() {
- { // acquire lock
- std::scoped_lock _l(mLock);
+ std::scoped_lock _l(mLock);
- resetKeyRepeatLocked();
- releasePendingEventLocked();
- drainInboundQueueLocked();
- }
+ resetKeyRepeatLocked();
+ releasePendingEventLocked();
+ drainInboundQueueLocked();
+ mCommandQueue.clear();
while (!mConnectionsByToken.empty()) {
sp<Connection> connection = mConnectionsByToken.begin()->second;
- removeInputChannel(connection->inputChannel->getConnectionToken());
+ removeInputChannelLocked(connection->inputChannel->getConnectionToken(),
+ false /* notify */);
}
}
-void InputDispatcher::onFirstRef() {
- SurfaceComposerClient::getDefault()->addWindowInfosListener(this);
-}
-
status_t InputDispatcher::start() {
if (mThread) {
return ALREADY_EXISTS;
@@ -595,7 +612,7 @@
// Run all pending commands if there are any.
// If any commands were run then force the next poll to wake up immediately.
- if (runCommandsLockedInterruptible()) {
+ if (runCommandsLockedInterruptable()) {
nextWakeupTime = LONG_LONG_MIN;
}
@@ -798,6 +815,14 @@
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED: {
+ const auto typedEntry = std::static_pointer_cast<TouchModeEntry>(mPendingEvent);
+ dispatchTouchModeChangeLocked(currentTime, typedEntry);
+ done = true;
+ dropReason = DropReason::NOT_DROPPED; // touch mode events are never dropped
+ break;
+ }
+
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto typedEntry =
std::static_pointer_cast<PointerCaptureChangedEntry>(mPendingEvent);
@@ -885,7 +910,7 @@
*/
bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
- (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+ isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER);
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
@@ -910,11 +935,9 @@
}
// Alternatively, maybe there's a gesture monitor that could handle this event
- std::vector<TouchedMonitor> gestureMonitors =
- findTouchedGestureMonitorsLocked(displayId, {});
- for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+ for (const auto& monitor : getValueByKey(mGestureMonitorsByDisplay, displayId)) {
sp<Connection> connection =
- getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+ getConnectionLocked(monitor.inputChannel->getConnectionToken());
if (connection != nullptr && connection->responsive) {
// This monitor could take more input. Drop all events preceding this
// event, so that gesture monitor could get a chance to receive the stream
@@ -956,9 +979,9 @@
mAppSwitchSawKeyDown = true;
} else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
-#if DEBUG_APP_SWITCH
- ALOGD("App switch is pending!");
-#endif
+ if (DEBUG_APP_SWITCH) {
+ ALOGD("App switch is pending!");
+ }
mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
@@ -979,6 +1002,7 @@
LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
@@ -1005,11 +1029,9 @@
sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets,
- bool addPortalWindows,
bool ignoreDragWindow) {
- if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
- LOG_ALWAYS_FATAL(
- "Must provide a valid touch state if adding portal windows or outside targets");
+ if (addOutsideTargets && touchState == nullptr) {
+ LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
}
// Traverse windows from front to back to find touched window.
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
@@ -1026,16 +1048,6 @@
bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
!flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- int32_t portalToDisplayId = windowInfo->portalToDisplayId;
- if (portalToDisplayId != ADISPLAY_ID_NONE &&
- portalToDisplayId != displayId) {
- if (addPortalWindows) {
- // For the monitoring channels of the display.
- touchState->addPortalWindow(windowHandle);
- }
- return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
- addOutsideTargets, addPortalWindows);
- }
// Found window.
return windowHandle;
}
@@ -1052,28 +1064,13 @@
return nullptr;
}
-std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
- int32_t displayId, const std::vector<sp<WindowInfoHandle>>& portalWindows) const {
- std::vector<TouchedMonitor> touchedMonitors;
-
- std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
- addGestureMonitors(monitors, touchedMonitors);
- for (const sp<WindowInfoHandle>& portalWindow : portalWindows) {
- const WindowInfo* windowInfo = portalWindow->getInfo();
- monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId);
- addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft,
- -windowInfo->frameTop);
- }
- return touchedMonitors;
-}
-
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
case DropReason::POLICY:
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("Dropped event because policy consumed it.");
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("Dropped event because policy consumed it.");
+ }
reason = "inbound event was dropped because the policy consumed it";
break;
case DropReason::DISABLED:
@@ -1131,9 +1128,10 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
- LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str());
+ LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str());
break;
}
}
@@ -1157,37 +1155,35 @@
void InputDispatcher::resetPendingAppSwitchLocked(bool handled) {
mAppSwitchDueTime = LONG_LONG_MAX;
-#if DEBUG_APP_SWITCH
- if (handled) {
- ALOGD("App switch has arrived.");
- } else {
- ALOGD("App switch was abandoned.");
+ if (DEBUG_APP_SWITCH) {
+ if (handled) {
+ ALOGD("App switch has arrived.");
+ } else {
+ ALOGD("App switch was abandoned.");
+ }
}
-#endif
}
bool InputDispatcher::haveCommandsLocked() const {
return !mCommandQueue.empty();
}
-bool InputDispatcher::runCommandsLockedInterruptible() {
+bool InputDispatcher::runCommandsLockedInterruptable() {
if (mCommandQueue.empty()) {
return false;
}
do {
- std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+ auto command = std::move(mCommandQueue.front());
mCommandQueue.pop_front();
- Command command = commandEntry->command;
- command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
-
- commandEntry->connection.clear();
+ // Commands are run with the lock held, but may release and re-acquire the lock from within.
+ command();
} while (!mCommandQueue.empty());
return true;
}
-void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
- mCommandQueue.push_back(std::move(commandEntry));
+void InputDispatcher::postCommandLocked(Command&& command) {
+ mCommandQueue.push_back(command);
}
void InputDispatcher::drainInboundQueueLocked() {
@@ -1209,9 +1205,9 @@
void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("Injected inbound event was dropped.");
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Injected inbound event was dropped.");
+ }
setInjectionResult(*entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
@@ -1246,27 +1242,28 @@
bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
const ConfigurationChangedEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
+ }
// Reset key repeating in case a keyboard device was added or removed or something.
resetKeyRepeatLocked();
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
- commandEntry->eventTime = entry.eventTime;
- postCommandLocked(std::move(commandEntry));
+ auto command = [this, eventTime = entry.eventTime]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyConfigurationChanged(eventTime);
+ };
+ postCommandLocked(std::move(command));
return true;
}
bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
const DeviceResetEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
- entry.deviceId);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+ entry.deviceId);
+ }
// Reset key repeating in case a keyboard device was disabled or enabled.
if (mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId == entry.deviceId) {
@@ -1390,6 +1387,43 @@
dropReason = DropReason::NOT_DROPPED;
}
+void InputDispatcher::dispatchTouchModeChangeLocked(nsecs_t currentTime,
+ const std::shared_ptr<TouchModeEntry>& entry) {
+ const std::vector<sp<WindowInfoHandle>>& windowHandles =
+ getWindowHandlesLocked(mFocusedDisplayId);
+ if (windowHandles.empty()) {
+ return;
+ }
+ const std::vector<InputTarget> inputTargets =
+ getInputTargetsFromWindowHandlesLocked(windowHandles);
+ if (inputTargets.empty()) {
+ return;
+ }
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, inputTargets);
+}
+
+std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked(
+ const std::vector<sp<WindowInfoHandle>>& windowHandles) const {
+ std::vector<InputTarget> inputTargets;
+ for (const sp<WindowInfoHandle>& handle : windowHandles) {
+ // TODO(b/193718270): Due to performance concerns, consider notifying visible windows only.
+ const sp<IBinder>& token = handle->getToken();
+ if (token == nullptr) {
+ continue;
+ }
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(token);
+ if (channel == nullptr) {
+ continue; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ inputTargets.push_back(target);
+ }
+ return inputTargets;
+}
+
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
@@ -1421,9 +1455,9 @@
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
// The key on device 'deviceId' is still down, do not stop key repeat
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
+ }
} else if (!entry->syntheticRepeat) {
resetKeyRepeatLocked();
}
@@ -1454,13 +1488,13 @@
// Give the policy a chance to intercept the key.
if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<IBinder> focusedWindowToken =
mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
- commandEntry->connectionToken = focusedWindowToken;
- commandEntry->keyEntry = entry;
- postCommandLocked(std::move(commandEntry));
+
+ auto command = [this, focusedWindowToken, entry]() REQUIRES(mLock) {
+ doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
+ };
+ postCommandLocked(std::move(command));
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1502,47 +1536,42 @@
}
void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
- "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
- "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
- entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
- entry.repeatCount, entry.downTime);
-#endif
-}
-
-void InputDispatcher::doNotifySensorLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- const std::shared_ptr<SensorEntry>& entry = commandEntry->sensorEntry;
- if (entry->accuracyChanged) {
- mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+ "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
+ "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
+ prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+ entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode,
+ entry.metaState, entry.repeatCount, entry.downTime);
}
- mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
- entry->hwTimestamp, entry->values);
- mLock.lock();
}
-void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime,
+ const std::shared_ptr<SensorEntry>& entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
- "source=0x%x, sensorType=%s",
- entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
- NamedEnum::string(entry->sensorType).c_str());
-#endif
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifySensorLockedInterruptible);
- commandEntry->sensorEntry = entry;
- postCommandLocked(std::move(commandEntry));
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, "
+ "source=0x%x, sensorType=%s",
+ entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source,
+ ftl::enum_string(entry->sensorType).c_str());
+ }
+ auto command = [this, entry]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+
+ if (entry->accuracyChanged) {
+ mPolicy->notifySensorAccuracy(entry->deviceId, entry->sensorType, entry->accuracy);
+ }
+ mPolicy->notifySensorEvent(entry->deviceId, entry->sensorType, entry->accuracy,
+ entry->hwTimestamp, entry->values);
+ };
+ postCommandLocked(std::move(command));
}
bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
- NamedEnum::string(sensorType).c_str());
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId,
+ ftl::enum_string(sensorType).c_str());
+ }
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -1575,7 +1604,7 @@
return true;
}
- bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;
+ const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);
// Identify targets.
std::vector<InputTarget> inputTargets;
@@ -1613,23 +1642,6 @@
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
- if (isPointerEvent) {
- std::unordered_map<int32_t, TouchState>::iterator it =
- mTouchStatesByDisplay.find(entry->displayId);
- if (it != mTouchStatesByDisplay.end()) {
- const TouchState& state = it->second;
- if (!state.portalWindows.empty()) {
- // The event has gone through these portal windows, so we add monitoring targets of
- // the corresponding displays as well.
- for (size_t i = 0; i < state.portalWindows.size(); i++) {
- const WindowInfo* windowInfo = state.portalWindows[i]->getInfo();
- addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId,
- -windowInfo->frameLeft, -windowInfo->frameTop);
- }
- }
- }
- }
-
// Dispatch the motion.
if (conflictingPointerActions) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
@@ -1670,42 +1682,43 @@
}
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
- ", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, "
- "metaState=0x%x, buttonState=0x%x,"
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
- entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState,
- entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime);
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ ", policyFlags=0x%x, "
+ "action=%s, actionButton=0x%x, flags=0x%x, "
+ "metaState=0x%x, buttonState=0x%x,"
+ "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
+ prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
+ entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
+ entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
+ entry.xPrecision, entry.yPrecision, entry.downTime);
- for (uint32_t i = 0; i < entry.pointerCount; i++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ ALOGD(" Pointer %d: id=%d, toolType=%d, "
+ "x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ }
}
-#endif
}
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
std::shared_ptr<EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("dispatchEventToCurrentInputTargets");
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("dispatchEventToCurrentInputTargets");
+ }
updateInteractionTokensLocked(*eventEntry, inputTargets);
@@ -1736,7 +1749,7 @@
// pile up.
ALOGW("Canceling events for %s because it is unresponsive",
connection->inputChannel->getName().c_str());
- if (connection->status == Connection::STATUS_NORMAL) {
+ if (connection->status == Connection::Status::NORMAL) {
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
"application not responding");
synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -1771,13 +1784,14 @@
displayId = motionEntry.displayId;
break;
}
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
- ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
+ ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
}
@@ -1829,7 +1843,7 @@
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
"display %" PRId32 ".",
- NamedEnum::string(entry.type).c_str(), displayId);
+ ftl::enum_string(entry.type).c_str(), displayId);
return InputEventInjectionResult::FAILED;
}
@@ -1859,7 +1873,7 @@
} else if (currentTime > *mNoFocusedWindowTimeoutTime) {
// Already raised ANR. Drop the event
ALOGE("Dropping %s event because there is no focused window",
- NamedEnum::string(entry.type).c_str());
+ ftl::enum_string(entry.type).c_str());
return InputEventInjectionResult::FAILED;
} else {
// Still waiting for the focused window
@@ -1911,16 +1925,16 @@
* Given a list of monitors, remove the ones we cannot find a connection for, and the ones
* that are currently unresponsive.
*/
-std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
- const std::vector<TouchedMonitor>& monitors) const {
- std::vector<TouchedMonitor> responsiveMonitors;
+std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked(
+ const std::vector<Monitor>& monitors) const {
+ std::vector<Monitor> responsiveMonitors;
std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
- [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
- sp<Connection> connection = getConnectionLocked(
- monitor.monitor.inputChannel->getConnectionToken());
+ [this](const Monitor& monitor) REQUIRES(mLock) {
+ sp<Connection> connection =
+ getConnectionLocked(monitor.inputChannel->getConnectionToken());
if (connection == nullptr) {
ALOGE("Could not find connection for monitor %s",
- monitor.monitor.inputChannel->getName().c_str());
+ monitor.inputChannel->getName().c_str());
return false;
}
if (!connection->responsive) {
@@ -1976,7 +1990,7 @@
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
- const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE;
+ const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
bool wrongDevice = false;
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
@@ -2021,10 +2035,9 @@
x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
}
- bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
- newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
- isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
+ const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+ newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+ isDown /*addOutsideTargets*/);
// Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr &&
@@ -2039,6 +2052,8 @@
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
+ ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
+ displayId);
// Try to assign the pointer to the first foreground window we find, if there is one.
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
@@ -2071,7 +2086,7 @@
ALOGD("%s", log.c_str());
}
}
- onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
+ sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
ALOGW("Dropping untrusted touch event due to %s/%d",
occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
@@ -2085,9 +2100,9 @@
newTouchedWindowHandle = nullptr;
}
- const std::vector<TouchedMonitor> newGestureMonitors = isDown
+ const std::vector<Monitor> newGestureMonitors = isDown
? selectResponsiveMonitorsLocked(
- findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows))
+ getValueByKey(mGestureMonitorsByDisplay, displayId))
: tempTouchState.gestureMonitors;
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
@@ -2207,10 +2222,10 @@
if (mLastHoverWindowHandle != nullptr &&
(maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
mLastHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
- ALOGD("Sending hover exit event to window %s.",
- mLastHoverWindowHandle->getName().c_str());
-#endif
+ if (DEBUG_HOVER) {
+ ALOGD("Sending hover exit event to window %s.",
+ mLastHoverWindowHandle->getName().c_str());
+ }
tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
}
@@ -2220,10 +2235,10 @@
if (newHoverWindowHandle != nullptr &&
(maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
newHoverWindowHandle != newTouchedWindowHandle)) {
-#if DEBUG_HOVER
- ALOGD("Sending hover enter event to window %s.",
- newHoverWindowHandle->getName().c_str());
-#endif
+ if (DEBUG_HOVER) {
+ ALOGD("Sending hover enter event to window %s.",
+ newHoverWindowHandle->getName().c_str());
+ }
tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
BitSet32(0));
@@ -2313,9 +2328,8 @@
touchedWindow.pointerIds, inputTargets);
}
- for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
- addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
- touchedMonitor.yOffset, inputTargets);
+ for (const auto& monitor : tempTouchState.gestureMonitors) {
+ addMonitoringTargetLocked(monitor, displayId, inputTargets);
}
// Drop the outside or hover touch windows since we will not care about them
@@ -2413,13 +2427,12 @@
void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
const sp<WindowInfoHandle> dropWindow =
findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/,
- false /*addOutsideTargets*/, false /*addPortalWindows*/,
- true /*ignoreDragWindow*/);
+ false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
if (dropWindow) {
vec2 local = dropWindow->getInfo()->transform.transform(x, y);
- notifyDropWindowLocked(dropWindow->getToken(), local.x, local.y);
+ sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
} else {
- notifyDropWindowLocked(nullptr, 0, 0);
+ sendDropWindowCommandLocked(nullptr, 0, 0);
}
mDragState.reset();
}
@@ -2448,8 +2461,7 @@
const sp<WindowInfoHandle> hoverWindowHandle =
findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
- false /*addOutsideTargets*/, false /*addPortalWindows*/,
- true /*ignoreDragWindow*/);
+ false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
// enqueue drag exit if needed.
if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
!haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -2466,7 +2478,7 @@
} else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
finishDragAndDrop(entry.displayId, x, y);
} else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
- notifyDropWindowLocked(nullptr, 0, 0);
+ sendDropWindowCommandLocked(nullptr, 0, 0);
mDragState.reset();
}
}
@@ -2494,9 +2506,12 @@
inputTarget.inputChannel = inputChannel;
inputTarget.flags = targetFlags;
inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
- inputTarget.displayOrientation = windowInfo->displayOrientation;
- inputTarget.displaySize =
- int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight);
+ const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId);
+ if (displayInfoIt != mDisplayInfos.end()) {
+ inputTarget.displayTransform = displayInfoIt->second.transform;
+ } else {
+ ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId);
+ }
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
}
@@ -2508,27 +2523,29 @@
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId, float xOffset,
- float yOffset) {
+ int32_t displayId) {
std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it =
mGlobalMonitorsByDisplay.find(displayId);
if (it != mGlobalMonitorsByDisplay.end()) {
const std::vector<Monitor>& monitors = it->second;
for (const Monitor& monitor : monitors) {
- addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets);
+ addMonitoringTargetLocked(monitor, displayId, inputTargets);
}
}
}
-void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset,
- float yOffset,
+void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
std::vector<InputTarget>& inputTargets) {
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
ui::Transform t;
- t.set(xOffset, yOffset);
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ const auto& displayTransform = it->second.transform;
+ target.displayTransform = displayTransform;
+ t = displayTransform;
+ }
target.setDefaultPointerTransform(t);
inputTargets.push_back(target);
}
@@ -2665,8 +2682,7 @@
"frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
"], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
"hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
- (isTouchedWindow) ? "[TOUCHED] " : "",
- NamedEnum::string(info->type, "%" PRId32).c_str(),
+ isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(),
info->packageName.c_str(), info->ownerUid, info->id,
toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
info->frameTop, info->frameRight, info->frameBottom,
@@ -2742,11 +2758,8 @@
}
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
- if (eventEntry.type == EventEntry::Type::FOCUS ||
- eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
- eventEntry.type == EventEntry::Type::DRAG) {
- // Focus or pointer capture changed events are passed to apps, but do not represent user
- // activity.
+ if (!isUserActivityEvent(eventEntry)) {
+ // Not poking user activity if the event type does not represent a user activity
return;
}
int32_t displayId = getTargetDisplayId(eventEntry);
@@ -2754,9 +2767,9 @@
if (focusedWindowHandle != nullptr) {
const WindowInfo* info = focusedWindowHandle->getInfo();
if (info->inputFeatures.test(WindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str());
+ }
return;
}
}
@@ -2782,24 +2795,19 @@
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
- case EventEntry::Type::FOCUS:
- case EventEntry::Type::CONFIGURATION_CHANGED:
- case EventEntry::Type::DEVICE_RESET:
- case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::DRAG: {
+ default: {
LOG_ALWAYS_FATAL("%s events are not user activity",
- NamedEnum::string(eventEntry.type).c_str());
+ ftl::enum_string(eventEntry.type).c_str());
break;
}
}
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
- commandEntry->eventTime = eventEntry.eventTime;
- commandEntry->userActivityEventType = eventType;
- commandEntry->displayId = displayId;
- postCommandLocked(std::move(commandEntry));
+ auto command = [this, eventTime = eventEntry.eventTime, eventType, displayId]()
+ REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->pokeUserActivity(eventTime, eventType, displayId);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
@@ -2812,21 +2820,22 @@
connection->getInputChannelName().c_str(), eventEntry->id);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "globalScaleFactor=%f, pointerIds=0x%x %s",
- connection->getInputChannelName().c_str(), inputTarget.flags,
- inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
- inputTarget.getPointerInfoString().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+ "globalScaleFactor=%f, pointerIds=0x%x %s",
+ connection->getInputChannelName().c_str(), inputTarget.flags,
+ inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+ inputTarget.getPointerInfoString().c_str());
+ }
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
- if (connection->status != Connection::STATUS_NORMAL) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getInputChannelName().c_str(), connection->getStatusLabel());
-#endif
+ if (connection->status != Connection::Status::NORMAL) {
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
+ connection->getInputChannelName().c_str(),
+ ftl::enum_string(connection->status).c_str());
+ }
return;
}
@@ -2834,7 +2843,7 @@
if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
"Entry type %s should not have FLAG_SPLIT",
- NamedEnum::string(eventEntry->type).c_str());
+ ftl::enum_string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2843,6 +2852,11 @@
if (!splitMotionEntry) {
return; // split event was dropped
}
+ if (splitMotionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+ std::string reason = std::string("reason=pointer cancel on split window");
+ android_log_event_list(LOGTAG_INPUT_CANCEL)
+ << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
+ }
if (DEBUG_FOCUS) {
ALOGD("channel '%s' ~ Split motion event.",
connection->getInputChannelName().c_str());
@@ -2925,10 +2939,11 @@
if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
- connection->getInputChannelName().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key "
+ "event",
+ connection->getInputChannelName().c_str());
+ }
return; // skip the inconsistent event
}
break;
@@ -2958,11 +2973,11 @@
if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
!connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
motionEntry.displayId)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
- "event",
- connection->getInputChannelName().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover "
+ "enter event",
+ connection->getInputChannelName().c_str());
+ }
// We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because
// this is a one-to-one event conversion.
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
@@ -2978,11 +2993,11 @@
if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction,
dispatchEntry->resolvedFlags)) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
- "event",
- connection->getInputChannelName().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion "
+ "event",
+ connection->getInputChannelName().c_str());
+ }
return; // skip the inconsistent event
}
@@ -3009,6 +3024,7 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
break;
@@ -3020,7 +3036,7 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- NamedEnum::string(newEntry.type).c_str());
+ ftl::enum_string(newEntry.type).c_str());
break;
}
}
@@ -3114,10 +3130,11 @@
return;
}
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
- commandEntry->newToken = token;
- postCommandLocked(std::move(commandEntry));
+ auto command = [this, token]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->onPointerDownOutsideFocus(token);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -3127,11 +3144,11 @@
connection->getInputChannelName().c_str());
ATRACE_NAME(message.c_str());
}
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
+ }
- while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
+ while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
const std::chrono::nanoseconds timeout =
@@ -3208,9 +3225,7 @@
motionEntry.xPrecision, motionEntry.yPrecision,
motionEntry.xCursorPosition,
motionEntry.yCursorPosition,
- dispatchEntry->displayOrientation,
- dispatchEntry->displaySize.x,
- dispatchEntry->displaySize.y,
+ dispatchEntry->rawTransform,
motionEntry.downTime, motionEntry.eventTime,
motionEntry.pointerCount,
motionEntry.pointerProperties, usingCoords);
@@ -3221,8 +3236,17 @@
const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
focusEntry.id,
- focusEntry.hasFocus,
- mInTouchMode);
+ focusEntry.hasFocus);
+ break;
+ }
+
+ case EventEntry::Type::TOUCH_MODE_CHANGED: {
+ const TouchModeEntry& touchModeEntry =
+ static_cast<const TouchModeEntry&>(eventEntry);
+ status = connection->inputPublisher
+ .publishTouchModeEvent(dispatchEntry->seq, touchModeEntry.id,
+ touchModeEntry.inTouchMode);
+
break;
}
@@ -3248,7 +3272,7 @@
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
- NamedEnum::string(eventEntry.type).c_str());
+ ftl::enum_string(eventEntry.type).c_str());
return;
}
}
@@ -3267,11 +3291,11 @@
} else {
// Pipe is full and we are waiting for the app to finish process some events
// before sending more events to it.
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
- "waiting for the application to catch up",
- connection->getInputChannelName().c_str());
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
+ "waiting for the application to catch up",
+ connection->getInputChannelName().c_str());
+ }
}
} else {
ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -3315,16 +3339,18 @@
const std::array<uint8_t, 32> InputDispatcher::getSignature(
const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
- int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
- if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) {
+ const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
+ if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) {
// Only sign events up and down events as the purely move events
// are tied to their up/down counterparts so signing would be redundant.
- VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
- verifiedEvent.actionMasked = actionMasked;
- verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
- return sign(verifiedEvent);
+ return INVALID_HMAC;
}
- return INVALID_HMAC;
+
+ VerifiedMotionEvent verifiedEvent =
+ verifiedMotionEventFromMotionEntry(motionEntry, dispatchEntry.rawTransform);
+ verifiedEvent.actionMasked = actionMasked;
+ verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
+ return sign(verifiedEvent);
}
const std::array<uint8_t, 32> InputDispatcher::getSignature(
@@ -3338,27 +3364,30 @@
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection, uint32_t seq,
bool handled, nsecs_t consumeTime) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
- connection->getInputChannelName().c_str(), seq, toString(handled));
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
+ connection->getInputChannelName().c_str(), seq, toString(handled));
+ }
- if (connection->status == Connection::STATUS_BROKEN ||
- connection->status == Connection::STATUS_ZOMBIE) {
+ if (connection->status == Connection::Status::BROKEN ||
+ connection->status == Connection::Status::ZOMBIE) {
return;
}
// Notify other system components and prepare to start the next dispatch cycle.
- onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime);
+ auto command = [this, currentTime, connection, seq, handled, consumeTime]() REQUIRES(mLock) {
+ doDispatchCycleFinishedCommand(currentTime, connection, seq, handled, consumeTime);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
bool notify) {
-#if DEBUG_DISPATCH_CYCLE
- ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
- connection->getInputChannelName().c_str(), toString(notify));
-#endif
+ if (DEBUG_DISPATCH_CYCLE) {
+ ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
+ connection->getInputChannelName().c_str(), toString(notify));
+ }
// Clear the dispatch queues.
drainDispatchQueue(connection->outboundQueue);
@@ -3368,12 +3397,19 @@
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
- if (connection->status == Connection::STATUS_NORMAL) {
- connection->status = Connection::STATUS_BROKEN;
+ if (connection->status == Connection::Status::NORMAL) {
+ connection->status = Connection::Status::BROKEN;
if (notify) {
// Notify other system components.
- onDispatchCycleBrokenLocked(currentTime, connection);
+ ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
+ connection->getInputChannelName().c_str());
+
+ auto command = [this, connection]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+ };
+ postCommandLocked(std::move(command));
}
}
}
@@ -3440,7 +3476,7 @@
gotOne = true;
}
if (gotOne) {
- runCommandsLockedInterruptible();
+ runCommandsLockedInterruptable();
if (status == WOULD_BLOCK) {
return 1;
}
@@ -3505,7 +3541,7 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const sp<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3517,12 +3553,16 @@
if (cancelationEvents.empty()) {
return;
}
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
- "with reality: %s, mode=%d.",
- connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
- options.mode);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+ "with reality: %s, mode=%d.",
+ connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+ options.mode);
+ }
+
+ std::string reason = std::string("reason=").append(options.reason);
+ android_log_event_list(LOGTAG_INPUT_CANCEL)
+ << connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
InputTarget target;
sp<WindowInfoHandle> windowHandle =
@@ -3549,17 +3589,18 @@
break;
}
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("Canceling %s events is not supported",
- NamedEnum::string(cancelationEventEntry->type).c_str());
+ ftl::enum_string(cancelationEventEntry->type).c_str());
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- NamedEnum::string(cancelationEventEntry->type).c_str());
+ ftl::enum_string(cancelationEventEntry->type).c_str());
break;
}
}
@@ -3573,7 +3614,7 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const sp<Connection>& connection) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3586,10 +3627,10 @@
return;
}
-#if DEBUG_OUTBOUND_EVENT_DETAILS
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
connection->getInputChannelName().c_str(), downEvents.size());
-#endif
+ }
InputTarget target;
sp<WindowInfoHandle> windowHandle =
@@ -3612,13 +3653,14 @@
case EventEntry::Type::KEY:
case EventEntry::Type::FOCUS:
+ case EventEntry::Type::TOUCH_MODE_CHANGED:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
- NamedEnum::string(downEventEntry->type).c_str());
+ ftl::enum_string(downEventEntry->type).c_str());
break;
}
}
@@ -3732,11 +3774,11 @@
}
void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime);
+ }
- bool needWake;
+ bool needWake = false;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -3788,14 +3830,14 @@
}
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
- "policyFlags=0x%x, action=0x%x, "
- "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
- args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
- args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
- args->downTime);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
+ "policyFlags=0x%x, action=0x%x, "
+ "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64,
+ args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
+ args->action, args->flags, args->keyCode, args->scanCode, args->metaState,
+ args->downTime);
+ }
if (!validateKeyEvent(args->action)) {
return;
}
@@ -3831,7 +3873,7 @@
std::to_string(t.duration().count()).c_str());
}
- bool needWake;
+ bool needWake = false;
{ // acquire lock
mLock.lock();
@@ -3866,33 +3908,33 @@
}
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
- "displayId=%" PRId32 ", policyFlags=0x%x, "
- "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
- "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
- "yCursorPosition=%f, downTime=%" PRId64,
- args->id, args->eventTime, args->deviceId, args->source, args->displayId,
- args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
- args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
- args->xCursorPosition, args->yCursorPosition, args->downTime);
- for (uint32_t i = 0; i < args->pointerCount; i++) {
- ALOGD(" Pointer %d: id=%d, toolType=%d, "
- "x=%f, y=%f, pressure=%f, size=%f, "
- "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
- "orientation=%f",
- i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+ "displayId=%" PRId32 ", policyFlags=0x%x, "
+ "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+ "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
+ "yCursorPosition=%f, downTime=%" PRId64,
+ args->id, args->eventTime, args->deviceId, args->source, args->displayId,
+ args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
+ args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition, args->downTime);
+ for (uint32_t i = 0; i < args->pointerCount; i++) {
+ ALOGD(" Pointer %d: id=%d, toolType=%d, "
+ "x=%f, y=%f, pressure=%f, size=%f, "
+ "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
+ "orientation=%f",
+ i, args->pointerProperties[i].id, args->pointerProperties[i].toolType,
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+ args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ }
}
-#endif
if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
args->pointerProperties)) {
return;
@@ -3908,22 +3950,26 @@
std::to_string(t.duration().count()).c_str());
}
- bool needWake;
+ bool needWake = false;
{ // acquire lock
mLock.lock();
if (shouldSendMotionToInputFilterLocked(args)) {
+ ui::Transform displayTransform;
+ if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+ displayTransform = it->second.transform;
+ }
+
mLock.unlock();
MotionEvent event;
- ui::Transform transform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
- args->metaState, args->buttonState, args->classification, transform,
- args->xPrecision, args->yPrecision, args->xCursorPosition,
- args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE,
- INVALID_DISPLAY_SIZE, args->downTime, args->eventTime,
- args->pointerCount, args->pointerProperties, args->pointerCoords);
+ args->metaState, args->buttonState, args->classification,
+ displayTransform, args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition, displayTransform,
+ args->downTime, args->eventTime, args->pointerCount,
+ args->pointerProperties, args->pointerCoords);
policyFlags |= POLICY_FLAG_FILTERED;
if (!mPolicy->filterInputEvent(&event, policyFlags)) {
@@ -3962,14 +4008,14 @@
}
void InputDispatcher::notifySensor(const NotifySensorArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
- " sensorType=%s",
- args->id, args->eventTime, args->deviceId, args->source,
- NamedEnum::string(args->sensorType).c_str());
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+ " sensorType=%s",
+ args->id, args->eventTime, args->deviceId, args->source,
+ ftl::enum_string(args->sensorType).c_str());
+ }
- bool needWake;
+ bool needWake = false;
{ // acquire lock
mLock.lock();
@@ -3990,10 +4036,10 @@
}
void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime,
- args->deviceId, args->isOn);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime,
+ args->deviceId, args->isOn);
+ }
mPolicy->notifyVibratorState(args->deviceId, args->isOn);
}
@@ -4002,11 +4048,11 @@
}
void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
- "switchMask=0x%08x",
- args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, "
+ "switchMask=0x%08x",
+ args->eventTime, args->policyFlags, args->switchValues, args->switchMask);
+ }
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4014,12 +4060,12 @@
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
- args->deviceId);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime,
+ args->deviceId);
+ }
- bool needWake;
+ bool needWake = false;
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -4034,12 +4080,12 @@
}
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
- args->request.enable ? "true" : "false");
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime,
+ args->request.enable ? "true" : "false");
+ }
- bool needWake;
+ bool needWake = false;
{ // acquire lock
std::scoped_lock _l(mLock);
auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime,
@@ -4055,11 +4101,11 @@
InputEventInjectionResult InputDispatcher::injectInputEvent(
const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
-#if DEBUG_INBOUND_EVENT_DETAILS
- ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
- "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
- event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
-#endif
+ if (DEBUG_INBOUND_EVENT_DETAILS) {
+ ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
+ "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+ event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+ }
nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
policyFlags |= POLICY_FLAG_INJECTED;
@@ -4128,12 +4174,17 @@
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- int32_t action = motionEvent.getAction();
- size_t pointerCount = motionEvent.getPointerCount();
+ const int32_t action = motionEvent.getAction();
+ const bool isPointerEvent =
+ isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER);
+ // If a pointer event has no displayId specified, inject it to the default display.
+ const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
+ ? ADISPLAY_ID_DEFAULT
+ : event->getDisplayId();
+ const size_t pointerCount = motionEvent.getPointerCount();
const PointerProperties* pointerProperties = motionEvent.getPointerProperties();
- int32_t actionButton = motionEvent.getActionButton();
+ const int32_t actionButton = motionEvent.getActionButton();
int32_t flags = motionEvent.getFlags();
- int32_t displayId = motionEvent.getDisplayId();
if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) {
return InputEventInjectionResult::FAILED;
}
@@ -4158,8 +4209,8 @@
std::unique_ptr<MotionEntry> injectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- motionEvent.getDisplayId(), policyFlags, action,
- actionButton, flags, motionEvent.getMetaState(),
+ displayId, policyFlags, action, actionButton,
+ flags, motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4171,6 +4222,7 @@
pointerProperties, samplePointerCoords,
motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*injectedEntry);
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -4178,9 +4230,8 @@
std::unique_ptr<MotionEntry> nextInjectedEntry =
std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes,
resolvedDeviceId, motionEvent.getSource(),
- motionEvent.getDisplayId(), policyFlags,
- action, actionButton, flags,
- motionEvent.getMetaState(),
+ displayId, policyFlags, action, actionButton,
+ flags, motionEvent.getMetaState(),
motionEvent.getButtonState(),
motionEvent.getClassification(),
motionEvent.getEdgeFlags(),
@@ -4192,6 +4243,7 @@
uint32_t(pointerCount), pointerProperties,
samplePointerCoords, motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*nextInjectedEntry);
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -4237,10 +4289,10 @@
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Timed out waiting for injection result "
- "to become available.");
-#endif
+ if (DEBUG_INJECTION) {
+ ALOGD("injectInputEvent - Timed out waiting for injection result "
+ "to become available.");
+ }
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -4251,16 +4303,16 @@
if (injectionResult == InputEventInjectionResult::SUCCEEDED &&
syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) {
while (injectionState->pendingForegroundDispatches != 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
- injectionState->pendingForegroundDispatches);
-#endif
+ if (DEBUG_INJECTION) {
+ ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.",
+ injectionState->pendingForegroundDispatches);
+ }
nsecs_t remainingTimeout = endTime - now();
if (remainingTimeout <= 0) {
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Timed out waiting for pending foreground "
- "dispatches to finish.");
-#endif
+ if (DEBUG_INJECTION) {
+ ALOGD("injectInputEvent - Timed out waiting for pending foreground "
+ "dispatches to finish.");
+ }
injectionResult = InputEventInjectionResult::TIMED_OUT;
break;
}
@@ -4273,10 +4325,10 @@
injectionState->release();
} // release lock
-#if DEBUG_INJECTION
- ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
- injectionResult, injectorPid, injectorUid);
-#endif
+ if (DEBUG_INJECTION) {
+ ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
+ injectionResult, injectorPid, injectorUid);
+ }
return injectionResult;
}
@@ -4323,11 +4375,11 @@
InputEventInjectionResult injectionResult) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
-#if DEBUG_INJECTION
- ALOGD("Setting input event injection result to %d. "
- "injectorPid=%d, injectorUid=%d",
- injectionResult, injectionState->injectorPid, injectionState->injectorUid);
-#endif
+ if (DEBUG_INJECTION) {
+ ALOGD("Setting input event injection result to %d. "
+ "injectorPid=%d, injectorUid=%d",
+ injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+ }
if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
@@ -4355,6 +4407,38 @@
}
}
+void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
+ const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
+ if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ return;
+ }
+
+ // Input injection works in the logical display coordinate space, but the input pipeline works
+ // display space, so we need to transform the injected events accordingly.
+ const auto it = mDisplayInfos.find(entry.displayId);
+ if (it == mDisplayInfos.end()) return;
+ const auto& transformToDisplay = it->second.transform.inverse();
+
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ PointerCoords& pc = entry.pointerCoords[i];
+ const auto xy = isRelativeMouseEvent
+ ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
+ : transformToDisplay.transform(pc.getXYValue());
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ // Axes with relative values never represent points on a screen, so they should never have
+ // translation applied. If a device does not report relative values, these values are always
+ // 0, and will remain unaffected by the following operation.
+ const auto rel =
+ transformWithoutTranslation(transformToDisplay,
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
+ }
+}
+
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
@@ -4487,8 +4571,7 @@
std::vector<sp<WindowInfoHandle>> newHandles;
for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
const WindowInfo* info = handle->getInfo();
- if ((getInputChannelLocked(handle->getToken()) == nullptr &&
- info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+ if (getInputChannelLocked(handle->getToken()) == nullptr) {
const bool noInputChannel =
info->inputFeatures.test(WindowInfo::Feature::NO_INPUT_CHANNEL);
const bool canReceiveInput = !info->flags.test(WindowInfo::Flag::NOT_TOUCHABLE) ||
@@ -4522,6 +4605,7 @@
void InputDispatcher::setInputWindows(
const std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>>& handlesPerDisplay) {
+ // TODO(b/198444055): Remove setInputWindows from InputDispatcher.
{ // acquire lock
std::scoped_lock _l(mLock);
for (const auto& [displayId, handles] : handlesPerDisplay) {
@@ -4602,6 +4686,20 @@
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
+ // Since we are about to drop the touch, cancel the events for the wallpaper as
+ // well.
+ if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
+ touchedWindow.windowHandle->getInfo()->hasWallpaper) {
+ sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
+ if (wallpaper != nullptr) {
+ sp<Connection> wallpaperConnection =
+ getConnectionLocked(wallpaper->getToken());
+ if (wallpaperConnection != nullptr) {
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+ options);
+ }
+ }
+ }
}
state.windows.erase(state.windows.begin() + i);
} else {
@@ -4618,21 +4716,19 @@
}
}
- if (isPerWindowInputRotationEnabled()) {
- // Determine if the orientation of any of the input windows have changed, and cancel all
- // pointer events if necessary.
- for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
- if (newWindowHandle != nullptr &&
- newWindowHandle->getInfo()->transform.getOrientation() !=
- oldWindowOrientations[oldWindowHandle->getId()]) {
- std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(newWindowHandle->getToken());
- if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "touched window's orientation changed");
- synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
- }
+ // Determine if the orientation of any of the input windows have changed, and cancel all
+ // pointer events if necessary.
+ for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
+ const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
+ if (newWindowHandle != nullptr &&
+ newWindowHandle->getInfo()->transform.getOrientation() !=
+ oldWindowOrientations[oldWindowHandle->getId()]) {
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(newWindowHandle->getToken());
+ if (inputChannel != nullptr) {
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "touched window's orientation changed");
+ synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
}
}
@@ -4733,7 +4829,7 @@
// Find new focused window and validate
sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
- notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
+ sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
if (newFocusedWindowToken == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
@@ -4810,8 +4906,29 @@
}
void InputDispatcher::setInTouchMode(bool inTouchMode) {
- std::scoped_lock lock(mLock);
- mInTouchMode = inTouchMode;
+ bool needWake = false;
+ {
+ std::scoped_lock lock(mLock);
+ if (mInTouchMode == inTouchMode) {
+ return;
+ }
+ if (DEBUG_TOUCH_MODE) {
+ ALOGD("Request to change touch mode from %s to %s", toString(mInTouchMode),
+ toString(inTouchMode));
+ // TODO(b/198487159): Also print the current last interacted apps.
+ }
+
+ // TODO(b/198499018): Store touch mode per display.
+ mInTouchMode = inTouchMode;
+
+ // TODO(b/198487159): Enforce that only last interacted apps can change touch mode.
+ auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode);
+ needWake = enqueueInboundEventLocked(std::move(entry));
+ } // release lock
+
+ if (needWake) {
+ mLooper->wake();
+ }
}
void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) {
@@ -5030,14 +5147,6 @@
} else {
dump += INDENT3 "Windows: <none>\n";
}
- if (!state.portalWindows.empty()) {
- dump += INDENT3 "Portal windows:\n";
- for (size_t i = 0; i < state.portalWindows.size(); i++) {
- const sp<WindowInfoHandle> portalWindowHandle = state.portalWindows[i];
- dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i,
- portalWindowHandle->getName().c_str());
- }
- }
}
} else {
dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5049,9 +5158,17 @@
}
if (!mWindowHandlesByDisplay.empty()) {
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>> windowHandles = it.second;
- dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first);
+ for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
+ dump += StringPrintf(INDENT "Display: %" PRId32 "\n", displayId);
+ if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
+ const auto& displayInfo = it->second;
+ dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
+ displayInfo.logicalHeight);
+ displayInfo.transform.dump(dump, "transform", INDENT4);
+ } else {
+ dump += INDENT2 "No DisplayInfo found!\n";
+ }
+
if (!windowHandles.empty()) {
dump += INDENT2 "Windows:\n";
for (size_t i = 0; i < windowHandles.size(); i++) {
@@ -5059,7 +5176,7 @@
const WindowInfo* windowInfo = windowHandle->getInfo();
dump += StringPrintf(INDENT3 "%zu: name='%s', id=%" PRId32 ", displayId=%d, "
- "portalToDisplayId=%d, paused=%s, focusable=%s, "
+ "paused=%s, focusable=%s, "
"hasWallpaper=%s, visible=%s, alpha=%.2f, "
"flags=%s, type=%s, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
@@ -5067,13 +5184,12 @@
"applicationInfo.token=%s, "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->id,
- windowInfo->displayId, windowInfo->portalToDisplayId,
- toString(windowInfo->paused),
+ windowInfo->displayId, toString(windowInfo->paused),
toString(windowInfo->focusable),
toString(windowInfo->hasWallpaper),
toString(windowInfo->visible), windowInfo->alpha,
windowInfo->flags.string().c_str(),
- NamedEnum::string(windowInfo->type).c_str(),
+ ftl::enum_string(windowInfo->type).c_str(),
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
@@ -5084,13 +5200,12 @@
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
"ms, trustedOverlay=%s, hasToken=%s, "
- "touchOcclusionMode=%s, displayOrientation=%d\n",
+ "touchOcclusionMode=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
millis(windowInfo->dispatchingTimeout),
toString(windowInfo->trustedOverlay),
toString(windowInfo->token != nullptr),
- toString(windowInfo->touchOcclusionMode).c_str(),
- windowInfo->displayOrientation);
+ toString(windowInfo->touchOcclusionMode).c_str());
windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
@@ -5165,6 +5280,12 @@
dump += INDENT "ReplacedKeys: <empty>\n";
}
+ if (!mCommandQueue.empty()) {
+ dump += StringPrintf(INDENT "CommandQueue: size=%zu\n", mCommandQueue.size());
+ } else {
+ dump += INDENT "CommandQueue: <empty>\n";
+ }
+
if (!mConnectionsByToken.empty()) {
dump += INDENT "Connections:\n";
for (const auto& [token, connection] : mConnectionsByToken) {
@@ -5172,7 +5293,8 @@
"status=%s, monitor=%s, responsive=%s\n",
connection->inputChannel->getFd().get(),
connection->getInputChannelName().c_str(),
- connection->getWindowName().c_str(), connection->getStatusLabel(),
+ connection->getWindowName().c_str(),
+ ftl::enum_string(connection->status).c_str(),
toString(connection->monitor), toString(connection->responsive));
if (!connection->outboundQueue.empty()) {
@@ -5231,9 +5353,9 @@
};
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
-#if DEBUG_CHANNEL_CREATION
- ALOGD("channel '%s' ~ createInputChannel", name.c_str());
-#endif
+ if (DEBUG_CHANNEL_CREATION) {
+ ALOGD("channel '%s' ~ createInputChannel", name.c_str());
+ }
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
@@ -5345,7 +5467,7 @@
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
- connection->status = Connection::STATUS_ZOMBIE;
+ connection->status = Connection::Status::ZOMBIE;
return OK;
}
@@ -5397,9 +5519,9 @@
TouchState& state = stateIt->second;
std::shared_ptr<InputChannel> requestingChannel;
std::optional<int32_t> foundDeviceId;
- for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
- if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
- requestingChannel = touchedMonitor.monitor.inputChannel;
+ for (const auto& monitor : state.gestureMonitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ requestingChannel = monitor.inputChannel;
foundDeviceId = state.deviceId;
}
}
@@ -5514,46 +5636,92 @@
mConnectionsByToken.erase(connection->inputChannel->getConnectionToken());
}
-void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
- const sp<Connection>& connection, uint32_t seq,
- bool handled, nsecs_t consumeTime) {
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
- commandEntry->connection = connection;
- commandEntry->eventTime = currentTime;
- commandEntry->seq = seq;
- commandEntry->handled = handled;
- commandEntry->consumeTime = consumeTime;
- postCommandLocked(std::move(commandEntry));
+void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime,
+ const sp<Connection>& connection, uint32_t seq,
+ bool handled, nsecs_t consumeTime) {
+ // Handle post-event policy actions.
+ std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+ if (dispatchEntryIt == connection->waitQueue.end()) {
+ return;
+ }
+ DispatchEntry* dispatchEntry = *dispatchEntryIt;
+ const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+ if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+ ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+ ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
+ }
+ if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
+ mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
+ connection->inputChannel->getConnectionToken(),
+ dispatchEntry->deliveryTime, consumeTime, finishTime);
+ }
+
+ bool restartEvent;
+ if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+ KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
+ restartEvent =
+ afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+ } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+ MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
+ restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, motionEntry,
+ handled);
+ } else {
+ restartEvent = false;
+ }
+
+ // Dequeue the event and start the next cycle.
+ // Because the lock might have been released, it is possible that the
+ // contents of the wait queue to have been drained, so we need to double-check
+ // a few things.
+ dispatchEntryIt = connection->findWaitQueueEntry(seq);
+ if (dispatchEntryIt != connection->waitQueue.end()) {
+ dispatchEntry = *dispatchEntryIt;
+ connection->waitQueue.erase(dispatchEntryIt);
+ const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
+ mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
+ if (!connection->responsive) {
+ connection->responsive = isConnectionResponsive(*connection);
+ if (connection->responsive) {
+ // The connection was unresponsive, and now it's responsive.
+ processConnectionResponsiveLocked(*connection);
+ }
+ }
+ traceWaitQueueLength(*connection);
+ if (restartEvent && connection->status == Connection::Status::NORMAL) {
+ connection->outboundQueue.push_front(dispatchEntry);
+ traceOutboundQueueLength(*connection);
+ } else {
+ releaseDispatchEntry(dispatchEntry);
+ }
+ }
+
+ // Start the next dispatch cycle for this connection.
+ startDispatchCycleLocked(now(), connection);
}
-void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
- const sp<Connection>& connection) {
- ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
- connection->getInputChannelName().c_str());
-
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
- commandEntry->connection = connection;
- postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
+ const sp<IBinder>& newToken) {
+ auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyFocusChanged(oldToken, newToken);
+ };
+ postCommandLocked(std::move(command));
}
-void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
- const sp<IBinder>& newToken) {
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
- commandEntry->oldToken = oldToken;
- commandEntry->newToken = newToken;
- postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) {
+ auto command = [this, token, x, y]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyDropWindow(token, x, y);
+ };
+ postCommandLocked(std::move(command));
}
-void InputDispatcher::notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) {
- std::unique_ptr<CommandEntry> commandEntry =
- std::make_unique<CommandEntry>(&InputDispatcher::doNotifyDropWindowLockedInterruptible);
- commandEntry->newToken = token;
- commandEntry->x = x;
- commandEntry->y = y;
- postCommandLocked(std::move(commandEntry));
+void InputDispatcher::sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) {
+ auto command = [this, obscuringPackage]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyUntrustedTouch(obscuringPackage);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
@@ -5596,17 +5764,11 @@
StringPrintf("%s does not have a focused window", application->getName().c_str());
updateLastAnrStateLocked(*application, reason);
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible);
- commandEntry->inputApplicationHandle = std::move(application);
- postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) {
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible);
- commandEntry->obscuringPackage = obscuringPackage;
- postCommandLocked(std::move(commandEntry));
+ auto command = [this, application = std::move(application)]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyNoFocusedWindowAnr(application);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::updateLastAnrStateLocked(const sp<WindowInfoHandle>& window,
@@ -5637,109 +5799,24 @@
dumpDispatchStateLocked(mLastAnrState);
}
-void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyConfigurationChanged(commandEntry->eventTime);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
-
- if (connection->status != Connection::STATUS_ZOMBIE) {
- mLock.unlock();
-
- mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
-
- mLock.lock();
- }
-}
-
-void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) {
- sp<IBinder> oldToken = commandEntry->oldToken;
- sp<IBinder> newToken = commandEntry->newToken;
- mLock.unlock();
- mPolicy->notifyFocusChanged(oldToken, newToken);
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) {
- sp<IBinder> newToken = commandEntry->newToken;
- mLock.unlock();
- mPolicy->notifyDropWindow(newToken, commandEntry->x, commandEntry->y);
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyNoFocusedWindowAnr(commandEntry->inputApplicationHandle);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyWindowResponsive(commandEntry->connectionToken);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyMonitorResponsive(commandEntry->pid);
-
- mLock.lock();
-}
-
-void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage);
-
- mLock.lock();
-}
-
-void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
- CommandEntry* commandEntry) {
- KeyEntry& entry = *(commandEntry->keyEntry);
- KeyEvent event = createKeyEvent(entry);
-
- mLock.unlock();
-
- android::base::Timer t;
- const sp<IBinder>& token = commandEntry->connectionToken;
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
- if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
- ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
- std::to_string(t.duration().count()).c_str());
- }
-
- mLock.lock();
+void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+ KeyEntry& entry) {
+ const KeyEvent event = createKeyEvent(entry);
+ nsecs_t delay = 0;
+ { // release lock
+ scoped_unlock unlock(mLock);
+ android::base::Timer t;
+ delay = mPolicy->interceptKeyBeforeDispatching(focusedWindowToken, &event,
+ entry.policyFlags);
+ if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
+ ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
+ std::to_string(t.duration().count()).c_str());
+ }
+ } // acquire lock
if (delay < 0) {
entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
- } else if (!delay) {
+ } else if (delay == 0) {
entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
} else {
entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
@@ -5747,122 +5824,37 @@
}
}
-void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
- mPolicy->onPointerDownOutsideFocus(commandEntry->newToken);
- mLock.lock();
-}
-
-/**
- * Connection is responsive if it has no events in the waitQueue that are older than the
- * current time.
- */
-static bool isConnectionResponsive(const Connection& connection) {
- const nsecs_t currentTime = now();
- for (const DispatchEntry* entry : connection.waitQueue) {
- if (entry->timeoutTime < currentTime) {
- return false;
- }
- }
- return true;
-}
-
-void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
- sp<Connection> connection = commandEntry->connection;
- const nsecs_t finishTime = commandEntry->eventTime;
- uint32_t seq = commandEntry->seq;
- const bool handled = commandEntry->handled;
-
- // Handle post-event policy actions.
- std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
- if (dispatchEntryIt == connection->waitQueue.end()) {
- return;
- }
- DispatchEntry* dispatchEntry = *dispatchEntryIt;
- const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
- if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
- ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
- ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
- }
- if (shouldReportFinishedEvent(*dispatchEntry, *connection)) {
- mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id,
- connection->inputChannel->getConnectionToken(),
- dispatchEntry->deliveryTime, commandEntry->consumeTime,
- finishTime);
- }
-
- bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
- restartEvent =
- afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
- restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
- handled);
- } else {
- restartEvent = false;
- }
-
- // Dequeue the event and start the next cycle.
- // Because the lock might have been released, it is possible that the
- // contents of the wait queue to have been drained, so we need to double-check
- // a few things.
- dispatchEntryIt = connection->findWaitQueueEntry(seq);
- if (dispatchEntryIt != connection->waitQueue.end()) {
- dispatchEntry = *dispatchEntryIt;
- connection->waitQueue.erase(dispatchEntryIt);
- const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken();
- mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken);
- if (!connection->responsive) {
- connection->responsive = isConnectionResponsive(*connection);
- if (connection->responsive) {
- // The connection was unresponsive, and now it's responsive.
- processConnectionResponsiveLocked(*connection);
- }
- }
- traceWaitQueueLength(*connection);
- if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
- connection->outboundQueue.push_front(dispatchEntry);
- traceOutboundQueueLength(*connection);
- } else {
- releaseDispatchEntry(dispatchEntry);
- }
- }
-
- // Start the next dispatch cycle for this connection.
- startDispatchCycleLocked(now(), connection);
-}
-
void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) {
- std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible);
- monitorUnresponsiveCommand->pid = pid;
- monitorUnresponsiveCommand->reason = std::move(reason);
- postCommandLocked(std::move(monitorUnresponsiveCommand));
+ auto command = [this, pid, reason = std::move(reason)]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyMonitorUnresponsive(pid, reason);
+ };
+ postCommandLocked(std::move(command));
}
-void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken,
+void InputDispatcher::sendWindowUnresponsiveCommandLocked(const sp<IBinder>& token,
std::string reason) {
- std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible);
- windowUnresponsiveCommand->connectionToken = std::move(connectionToken);
- windowUnresponsiveCommand->reason = std::move(reason);
- postCommandLocked(std::move(windowUnresponsiveCommand));
+ auto command = [this, token, reason = std::move(reason)]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyWindowUnresponsive(token, reason);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) {
- std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible);
- monitorResponsiveCommand->pid = pid;
- postCommandLocked(std::move(monitorResponsiveCommand));
+ auto command = [this, pid]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyMonitorResponsive(pid);
+ };
+ postCommandLocked(std::move(command));
}
-void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) {
- std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>(
- &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible);
- windowResponsiveCommand->connectionToken = std::move(connectionToken);
- postCommandLocked(std::move(windowResponsiveCommand));
+void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) {
+ auto command = [this, connectionToken]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->notifyWindowResponsive(connectionToken);
+ };
+ postCommandLocked(std::move(command));
}
/**
@@ -5910,7 +5902,7 @@
sendWindowResponsiveCommandLocked(connectionToken);
}
-bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterKeyEventLockedInterruptable(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
KeyEntry& keyEntry, bool handled) {
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
@@ -5935,11 +5927,12 @@
// then cancel the associated fallback key, if any.
if (fallbackKeyCode != -1) {
// Dispatch the unhandled key to the policy with the cancel flag.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount,
+ keyEntry.policyFlags);
+ }
KeyEvent event = createKeyEvent(keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
@@ -5967,21 +5960,21 @@
// Then ask the policy what to do with it.
bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
if (fallbackKeyCode == -1 && !initialDown) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Skipping unhandled key event processing "
- "since this is not an initial down. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("Unhandled key event: Skipping unhandled key event processing "
+ "since this is not an initial down. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+ }
return false;
}
// Dispatch the unhandled key to the policy.
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Asking policy to perform fallback action. "
- "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("Unhandled key event: Asking policy to perform fallback action. "
+ "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
+ }
KeyEvent event = createKeyEvent(keyEntry);
mLock.unlock();
@@ -5992,7 +5985,7 @@
mLock.lock();
- if (connection->status != Connection::STATUS_NORMAL) {
+ if (connection->status != Connection::Status::NORMAL) {
connection->inputState.removeFallbackKey(originalKeyCode);
return false;
}
@@ -6015,19 +6008,19 @@
// longer dispatch a fallback key to the application.
if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
(!fallback || fallbackKeyCode != event.getKeyCode())) {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- if (fallback) {
- ALOGD("Unhandled key event: Policy requested to send key %d"
- "as a fallback for %d, but on the DOWN it had requested "
- "to send %d instead. Fallback canceled.",
- event.getKeyCode(), originalKeyCode, fallbackKeyCode);
- } else {
- ALOGD("Unhandled key event: Policy did not request fallback for %d, "
- "but on the DOWN it had requested to send %d. "
- "Fallback canceled.",
- originalKeyCode, fallbackKeyCode);
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ if (fallback) {
+ ALOGD("Unhandled key event: Policy requested to send key %d"
+ "as a fallback for %d, but on the DOWN it had requested "
+ "to send %d instead. Fallback canceled.",
+ event.getKeyCode(), originalKeyCode, fallbackKeyCode);
+ } else {
+ ALOGD("Unhandled key event: Policy did not request fallback for %d, "
+ "but on the DOWN it had requested to send %d. "
+ "Fallback canceled.",
+ originalKeyCode, fallbackKeyCode);
+ }
}
-#endif
CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
"canceling fallback, policy no longer desires it");
@@ -6041,18 +6034,18 @@
}
}
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- {
- std::string msg;
- const KeyedVector<int32_t, int32_t>& fallbackKeys =
- connection->inputState.getFallbackKeys();
- for (size_t i = 0; i < fallbackKeys.size(); i++) {
- msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ {
+ std::string msg;
+ const KeyedVector<int32_t, int32_t>& fallbackKeys =
+ connection->inputState.getFallbackKeys();
+ for (size_t i = 0; i < fallbackKeys.size(); i++) {
+ msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i));
+ }
+ ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
+ fallbackKeys.size(), msg.c_str());
}
- ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.",
- fallbackKeys.size(), msg.c_str());
}
-#endif
if (fallback) {
// Restart the dispatch cycle using the fallback key.
@@ -6068,16 +6061,16 @@
keyEntry.downTime = event.getDownTime();
keyEntry.syntheticRepeat = false;
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: Dispatching fallback key. "
- "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry.metaState);
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("Unhandled key event: Dispatching fallback key. "
+ "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
+ originalKeyCode, fallbackKeyCode, keyEntry.metaState);
+ }
return true; // restart the event
} else {
-#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("Unhandled key event: No fallback key.");
-#endif
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ ALOGD("Unhandled key event: No fallback key.");
+ }
// Report the key as unhandled, since there is no fallback key.
mReporter->reportUnhandledKey(keyEntry.id);
@@ -6086,21 +6079,12 @@
return false;
}
-bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+bool InputDispatcher::afterMotionEventLockedInterruptable(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
MotionEntry& motionEntry, bool handled) {
return false;
}
-void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
- commandEntry->displayId);
-
- mLock.lock();
-}
-
void InputDispatcher::traceInboundQueueLengthLocked() {
if (ATRACE_ENABLED()) {
ATRACE_INT("iq", mInboundQueue.size());
@@ -6212,7 +6196,7 @@
disablePointerCaptureForcedLocked();
if (mFocusedDisplayId == changes.displayId) {
- notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
+ sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
}
}
@@ -6244,22 +6228,14 @@
mInboundQueue.push_front(std::move(entry));
}
-void InputDispatcher::setPointerCaptureLocked(bool enabled) {
- mCurrentPointerCaptureRequest.enable = enabled;
+void InputDispatcher::setPointerCaptureLocked(bool enable) {
+ mCurrentPointerCaptureRequest.enable = enable;
mCurrentPointerCaptureRequest.seq++;
- std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
- &InputDispatcher::doSetPointerCaptureLockedInterruptible);
- commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest;
- postCommandLocked(std::move(commandEntry));
-}
-
-void InputDispatcher::doSetPointerCaptureLockedInterruptible(
- android::inputdispatcher::CommandEntry* commandEntry) {
- mLock.unlock();
-
- mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest);
-
- mLock.lock();
+ auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy->setPointerCapture(request);
+ };
+ postCommandLocked(std::move(command));
}
void InputDispatcher::displayRemoved(int32_t displayId) {
@@ -6277,16 +6253,29 @@
mLooper->wake();
}
-void InputDispatcher::onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) {
+void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& displayInfos) {
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
-
for (const auto& info : windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
}
- setInputWindows(handlesPerDisplay);
+
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ mDisplayInfos.clear();
+ for (const auto& displayInfo : displayInfos) {
+ mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
+ }
+
+ for (const auto& [displayId, handles] : handlesPerDisplay) {
+ setInputWindowsLocked(handles, displayId);
+ }
+ }
+ // Wake up poll loop since it may need to make new input dispatching choices.
+ mLooper->wake();
}
bool InputDispatcher::shouldDropInput(
@@ -6296,7 +6285,7 @@
isWindowObscuredLocked(windowHandle))) {
ALOGW("Dropping %s event targeting %s as requested by input feature %s on display "
"%" PRId32 ".",
- NamedEnum::string(entry.type).c_str(), windowHandle->getName().c_str(),
+ ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
windowHandle->getInfo()->inputFeatures.string().c_str(),
windowHandle->getInfo()->displayId);
return true;
@@ -6304,4 +6293,10 @@
return false;
}
+void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
+ const std::vector<gui::WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& displayInfos) {
+ mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 04913d4..8a551cf 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -47,7 +47,6 @@
#include <unistd.h>
#include <utils/BitSet.h>
#include <utils/Looper.h>
-#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/threads.h>
#include <condition_variable>
@@ -81,12 +80,12 @@
*
* A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa.
*/
-class InputDispatcher : public android::InputDispatcherInterface, public gui::WindowInfosListener {
-protected:
- ~InputDispatcher() override;
-
+class InputDispatcher : public android::InputDispatcherInterface {
public:
+ static constexpr bool kDefaultInTouchMode = true;
+
explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy);
+ ~InputDispatcher() override;
void dump(std::string& dump) override;
void monitor() override;
@@ -143,7 +142,9 @@
void displayRemoved(int32_t displayId) override;
- void onWindowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos) override;
+ // Public because it's also used by tests to simulate the WindowInfosListener callback
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos,
+ const std::vector<android::gui::DisplayInfo>& displayInfos);
private:
enum class DropReason {
@@ -171,7 +172,26 @@
std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
- std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
+
+ // A command entry captures state and behavior for an action to be performed in the
+ // dispatch loop after the initial processing has taken place. It is essentially
+ // a kind of continuation used to postpone sensitive policy interactions to a point
+ // in the dispatch loop where it is safe to release the lock (generally after finishing
+ // the critical parts of the dispatch cycle).
+ //
+ // The special thing about commands is that they can voluntarily release and reacquire
+ // the dispatcher lock at will. Initially when the command starts running, the
+ // dispatcher lock is held. However, if the command needs to call into the policy to
+ // do some work, it can release the lock, do the work, then reacquire the lock again
+ // before returning.
+ //
+ // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
+ // never calls into the policy while holding its lock.
+ //
+ // Commands are called with the lock held, but they can release and re-acquire the lock from
+ // within.
+ using Command = std::function<void()>;
+ std::deque<Command> mCommandQueue GUARDED_BY(mLock);
DropReason mLastDropReason GUARDED_BY(mLock);
@@ -215,7 +235,6 @@
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets = false,
- bool addPortalWindows = false,
bool ignoreDragWindow = false)
REQUIRES(mLock);
@@ -261,6 +280,7 @@
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
void setInjectionResult(EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
+ void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry& entry);
@@ -296,8 +316,8 @@
// Deferred command processing.
bool haveCommandsLocked() const REQUIRES(mLock);
- bool runCommandsLockedInterruptible() REQUIRES(mLock);
- void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+ bool runCommandsLockedInterruptable() REQUIRES(mLock);
+ void postCommandLocked(Command&& command) REQUIRES(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
@@ -319,8 +339,22 @@
float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock);
android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock);
- std::unordered_map<int32_t, std::vector<sp<android::gui::WindowInfoHandle>>>
+ class DispatcherWindowListener : public gui::WindowInfosListener {
+ public:
+ explicit DispatcherWindowListener(InputDispatcher& dispatcher) : mDispatcher(dispatcher){};
+ void onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>& windowInfos,
+ const std::vector<android::gui::DisplayInfo>& displayInfos) override;
+
+ private:
+ InputDispatcher& mDispatcher;
+ };
+ sp<gui::WindowInfosListener> mWindowInfoListener;
+
+ std::unordered_map<int32_t /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>>
mWindowHandlesByDisplay GUARDED_BY(mLock);
+ std::unordered_map<int32_t /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
+ GUARDED_BY(mLock);
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
int32_t displayId) REQUIRES(mLock);
@@ -343,6 +377,12 @@
bool hasResponsiveConnectionLocked(android::gui::WindowInfoHandle& windowHandle) const
REQUIRES(mLock);
+ // Gets all the input targets (with their respective input channels) from the window handles
+ // passed as argument.
+ std::vector<InputTarget> getInputTargetsFromWindowHandlesLocked(
+ const std::vector<sp<android::gui::WindowInfoHandle>>& windowHandles) const
+ REQUIRES(mLock);
+
/*
* Validate and update InputWindowHandles for a given display.
*/
@@ -405,9 +445,12 @@
void dispatchPointerCaptureChangedLocked(
nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry,
DropReason& dropReason) REQUIRES(mLock);
+ void dispatchTouchModeChangeLocked(nsecs_t currentTime,
+ const std::shared_ptr<TouchModeEntry>& entry)
+ REQUIRES(mLock);
void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
+ void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<SensorEntry>& entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
@@ -454,24 +497,11 @@
*/
void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock);
- /**
- * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command.
- */
void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock);
- /**
- * Post `doNotifyWindowUnresponsiveLockedInterruptible` command.
- */
- void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason)
+ void sendWindowUnresponsiveCommandLocked(const sp<IBinder>& connectionToken, std::string reason)
REQUIRES(mLock);
- /**
- * Post `doNotifyMonitorResponsiveLockedInterruptible` command.
- */
void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock);
- /**
- * Post `doNotifyWindowResponsiveLockedInterruptible` command.
- */
- void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
-
+ void sendWindowResponsiveCommandLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -499,20 +529,16 @@
android::os::InputEventInjectionResult findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock);
- std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
- int32_t displayId,
- const std::vector<sp<android::gui::WindowInfoHandle>>& portalWindows) const
- REQUIRES(mLock);
- std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
- const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
+ std::vector<Monitor> selectResponsiveMonitorsLocked(
+ const std::vector<Monitor>& gestureMonitors) const REQUIRES(mLock);
void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset,
+ void addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId,
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
- void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
- float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
+ REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
const InjectionState* injectionState);
@@ -611,53 +637,30 @@
REQUIRES(mLock);
// Interesting events that we might like to log or tell the framework about.
- void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection,
- uint32_t seq, bool handled, nsecs_t consumeTime)
+ void doDispatchCycleFinishedCommand(nsecs_t finishTime, const sp<Connection>& connection,
+ uint32_t seq, bool handled, nsecs_t consumeTime)
REQUIRES(mLock);
- void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
- REQUIRES(mLock);
+ void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
+ KeyEntry& entry) REQUIRES(mLock);
void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
- void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+ void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
- void notifyDropWindowLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+ void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
+ void sendUntrustedTouchCommandLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock);
- void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock);
void updateLastAnrStateLocked(const sp<android::gui::WindowInfoHandle>& window,
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const InputApplicationHandle& application,
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
-
- // Outbound policy interactions.
- void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
- void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyDropWindowLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-
- // ANR-related callbacks - start
- void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- // ANR-related callbacks - end
- void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
- REQUIRES(mLock);
- void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doSetPointerCaptureLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
+ bool afterKeyEventLockedInterruptable(const sp<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
- bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
+ bool afterMotionEventLockedInterruptable(const sp<Connection>& connection,
DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
- void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
- void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
// Find touched state and touched window by token.
std::pair<TouchState*, TouchedWindow*> findTouchStateAndWindowLocked(const sp<IBinder>& token)
@@ -672,8 +675,6 @@
sp<InputReporterInterface> mReporter;
sp<com::android::internal::compat::IPlatformCompatNative> mCompatService;
-
- void onFirstRef() override;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
index 8d7fa75..bca1600 100644
--- a/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp
@@ -19,9 +19,9 @@
namespace android {
-sp<InputDispatcherInterface> createInputDispatcher(
+std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
const sp<InputDispatcherPolicyInterface>& policy) {
- return new android::inputdispatcher::InputDispatcher(policy);
+ return std::make_unique<android::inputdispatcher::InputDispatcher>(policy);
}
} // namespace android
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 7c463c8..0725389 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -21,7 +21,6 @@
#include <input/InputTransport.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
namespace android::inputdispatcher {
@@ -101,11 +100,8 @@
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- // Current display orientation
- uint32_t displayOrientation = ui::Transform::ROT_0;
-
- // Display-size in its natural rotation. Used for compatibility transform of raw coordinates.
- int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE};
+ // Current display transform. Used for compatibility for raw coordinates.
+ ui::Transform displayTransform;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp
index bbce759..43a82d5 100644
--- a/services/inputflinger/dispatcher/Monitor.cpp
+++ b/services/inputflinger/dispatcher/Monitor.cpp
@@ -22,8 +22,4 @@
Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid)
: inputChannel(inputChannel), pid(pid) {}
-// --- TouchedMonitor ---
-TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset)
- : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {}
-
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h
index 7be0760..365d5be 100644
--- a/services/inputflinger/dispatcher/Monitor.h
+++ b/services/inputflinger/dispatcher/Monitor.h
@@ -29,15 +29,6 @@
explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, int32_t pid);
};
-// For tracking the offsets we need to apply when adding gesture monitor targets.
-struct TouchedMonitor {
- Monitor monitor;
- float xOffset = 0.f;
- float yOffset = 0.f;
-
- explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset);
-};
-
} // namespace android::inputdispatcher
#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 20b6ead..759b3e7 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -37,7 +37,6 @@
source = 0;
displayId = ADISPLAY_ID_NONE;
windows.clear();
- portalWindows.clear();
gestureMonitors.clear();
}
@@ -48,7 +47,6 @@
source = other.source;
displayId = other.displayId;
windows = other.windows;
- portalWindows = other.portalWindows;
gestureMonitors = other.gestureMonitors;
}
@@ -77,17 +75,7 @@
windows.push_back(touchedWindow);
}
-void TouchState::addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle) {
- size_t numWindows = portalWindows.size();
- for (size_t i = 0; i < numWindows; i++) {
- if (portalWindows[i] == windowHandle) {
- return;
- }
- }
- portalWindows.push_back(windowHandle);
-}
-
-void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) {
+void TouchState::addGestureMonitors(const std::vector<Monitor>& newMonitors) {
const size_t newSize = gestureMonitors.size() + newMonitors.size();
gestureMonitors.reserve(newSize);
gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors),
@@ -119,7 +107,6 @@
void TouchState::filterNonMonitors() {
windows.clear();
- portalWindows.clear();
}
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -147,4 +134,14 @@
return haveSlipperyForegroundWindow;
}
+sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
+ for (size_t i = 0; i < windows.size(); i++) {
+ const TouchedWindow& window = windows[i];
+ if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) {
+ return window.windowHandle;
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index a4e52b0..4a62051 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -36,12 +36,7 @@
int32_t displayId; // id to the display that currently has a touch, others are rejected
std::vector<TouchedWindow> windows;
- // This collects the portal windows that the touch has gone through. Each portal window
- // targets a display (embedded display for most cases). With this info, we can add the
- // monitoring channels of the displays touched.
- std::vector<sp<android::gui::WindowInfoHandle>> portalWindows;
-
- std::vector<TouchedMonitor> gestureMonitors;
+ std::vector<Monitor> gestureMonitors;
TouchState();
~TouchState();
@@ -50,12 +45,13 @@
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle);
- void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
+ void addGestureMonitors(const std::vector<Monitor>& monitors);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
void filterNonMonitors();
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
+ sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
index a359557..38d0c32 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h
@@ -17,7 +17,7 @@
#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H
-#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
#include "InputDispatcherInterface.h"
#include "InputDispatcherPolicyInterface.h"
@@ -25,7 +25,7 @@
namespace android {
// This factory method is used to encapsulate implementation details in internal header files.
-sp<InputDispatcherInterface> createInputDispatcher(
+std::unique_ptr<InputDispatcherInterface> createInputDispatcher(
const sp<InputDispatcherPolicyInterface>& policy);
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index a7dccf0..714e7a0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -33,12 +33,10 @@
/* Notifies the system about input events generated by the input reader.
* The dispatcher is expected to be mostly asynchronous. */
-class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface {
-protected:
+class InputDispatcherInterface : public InputListenerInterface {
+public:
InputDispatcherInterface() {}
virtual ~InputDispatcherInterface() {}
-
-public:
/* Dumps the state of the input dispatcher.
*
* This method may be called on any thread (usually by the input manager). */
@@ -202,6 +200,7 @@
* InputDispatcher is the source of truth of Pointer Capture.
*/
virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) = 0;
+
/* Flush input device motion sensor.
*
* Returns true on success.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index fe74214..db63104 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -22,7 +22,6 @@
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/TouchVideoFrame.h>
-#include <utils/RefBase.h>
namespace android {
@@ -40,7 +39,7 @@
virtual ~NotifyArgs() { }
- virtual void notify(const sp<InputListenerInterface>& listener) const = 0;
+ virtual void notify(InputListenerInterface& listener) const = 0;
};
@@ -57,7 +56,7 @@
virtual ~NotifyConfigurationChangedArgs() { }
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
@@ -88,7 +87,7 @@
virtual ~NotifyKeyArgs() { }
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
@@ -142,7 +141,7 @@
bool operator==(const NotifyMotionArgs& rhs) const;
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
/* Describes a sensor event. */
@@ -167,7 +166,7 @@
~NotifySensorArgs() override {}
- void notify(const sp<InputListenerInterface>& listener) const override;
+ void notify(InputListenerInterface& listener) const override;
};
/* Describes a switch event. */
@@ -187,7 +186,7 @@
virtual ~NotifySwitchArgs() { }
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
@@ -206,7 +205,7 @@
virtual ~NotifyDeviceResetArgs() { }
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
/* Describes a change in the state of Pointer Capture. */
@@ -224,7 +223,7 @@
virtual ~NotifyPointerCaptureChangedArgs() {}
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
/* Describes a vibrator state event. */
@@ -242,18 +241,19 @@
virtual ~NotifyVibratorStateArgs() {}
- virtual void notify(const sp<InputListenerInterface>& listener) const;
+ void notify(InputListenerInterface& listener) const override;
};
/*
* The interface used by the InputReader to notify the InputListener about input events.
*/
-class InputListenerInterface : public virtual RefBase {
-protected:
+class InputListenerInterface {
+public:
InputListenerInterface() { }
+ InputListenerInterface(const InputListenerInterface&) = delete;
+ InputListenerInterface& operator=(const InputListenerInterface&) = delete;
virtual ~InputListenerInterface() { }
-public:
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) = 0;
virtual void notifyKey(const NotifyKeyArgs* args) = 0;
virtual void notifyMotion(const NotifyMotionArgs* args) = 0;
@@ -264,17 +264,15 @@
virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) = 0;
};
-
/*
* An implementation of the listener interface that queues up and defers dispatch
* of decoded events until flushed.
*/
class QueuedInputListener : public InputListenerInterface {
-protected:
- virtual ~QueuedInputListener();
public:
- explicit QueuedInputListener(const sp<InputListenerInterface>& innerListener);
+ explicit QueuedInputListener(InputListenerInterface& innerListener);
+ virtual ~QueuedInputListener();
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -288,7 +286,7 @@
void flush();
private:
- sp<InputListenerInterface> mInnerListener;
+ InputListenerInterface& mInnerListener;
std::vector<NotifyArgs*> mArgsQueue;
};
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 3c8ac1c..1aab856 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -51,12 +51,11 @@
* The implementation must guarantee thread safety for this interface. However, since the input
* listener is NOT thread safe, all calls to the listener must happen from the same thread.
*/
-class InputReaderInterface : public virtual RefBase {
-protected:
+class InputReaderInterface {
+public:
InputReaderInterface() { }
virtual ~InputReaderInterface() { }
-public:
/* Dumps the state of the input reader.
*
* This method may be called on any thread (usually by the input manager). */
diff --git a/services/inputflinger/include/InputReaderFactory.h b/services/inputflinger/include/InputReaderFactory.h
index 9db6233..dad14d6 100644
--- a/services/inputflinger/include/InputReaderFactory.h
+++ b/services/inputflinger/include/InputReaderFactory.h
@@ -22,8 +22,7 @@
class InputReaderPolicyInterface;
class InputListenerInterface;
-sp<InputReaderInterface> createInputReader(
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener);
+std::unique_ptr<InputReaderInterface> createInputReader(
+ const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener);
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 85d7247..db4228d 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -20,7 +20,6 @@
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <utils/BitSet.h>
-#include <utils/RefBase.h>
namespace android {
@@ -31,7 +30,8 @@
* fingers
*
* The pointer controller is responsible for providing synchronization and for tracking
- * display orientation changes if needed.
+ * display orientation changes if needed. It works in the display panel's coordinate space, which
+ * is the same coordinate space used by InputReader.
*/
class PointerControllerInterface {
protected:
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index ee7b392..51546ce 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,6 @@
"libstatslog",
"libui",
"libutils",
- "InputFlingerProperties",
],
static_libs: [
"libc++fs",
@@ -86,7 +85,7 @@
name: "libinputreader",
defaults: [
"inputflinger_defaults",
- "libinputreader_defaults"
+ "libinputreader_defaults",
],
srcs: [
"InputReaderFactory.cpp",
@@ -100,6 +99,6 @@
"libinputreader_headers",
],
static_libs: [
- "libc++fs"
+ "libc++fs",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 68d5e7c..d10f8b6 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -40,6 +40,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
@@ -263,7 +264,7 @@
*/
static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot,
SysfsClass clazz) {
- std::string nodeStr = NamedEnum::string(clazz);
+ std::string nodeStr = ftl::enum_string(clazz);
std::for_each(nodeStr.begin(), nodeStr.end(),
[](char& c) { c = std::tolower(static_cast<unsigned char>(c)); });
std::vector<std::filesystem::path> nodes;
@@ -1209,6 +1210,15 @@
return false;
}
+bool EventHub::hasKeyCode(int32_t deviceId, int32_t keyCode) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device != nullptr) {
+ return device->hasKeycodeLocked(keyCode);
+ }
+ return false;
+}
+
bool EventHub::hasLed(int32_t deviceId, int32_t led) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 1e9ec54..7cff672 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -379,21 +379,22 @@
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
+ rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
+ rawEvent->when);
+ }
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
- ALOGD("Recovered from input event buffer overrun.");
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Recovered from input event buffer overrun.");
+ }
} else {
-#if DEBUG_RAW_EVENTS
- ALOGD("Dropped input event while waiting for next input sync.");
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Dropped input event while waiting for next input sync.");
+ }
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
@@ -559,7 +560,13 @@
}
void InputDevice::updateMetaState(int32_t keyCode) {
- for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
+ first_in_mappers<bool>([keyCode](InputMapper& mapper) {
+ if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) &&
+ mapper.updateMetaState(keyCode)) {
+ return std::make_optional(true);
+ }
+ return std::optional<bool>();
+ });
}
void InputDevice::bumpGeneration() {
@@ -568,7 +575,7 @@
void InputDevice::notifyReset(nsecs_t when) {
NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
- mContext->getListener()->notifyDeviceReset(&args);
+ mContext->getListener().notifyDeviceReset(&args);
}
std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 5120860..7704a84 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -42,10 +42,11 @@
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener)
+ InputListenerInterface& listener)
: mContext(this),
mEventHub(eventHub),
mPolicy(policy),
+ mQueuedListener(listener),
mGlobalMetaState(0),
mLedMetaState(AMETA_NUM_LOCK_ON),
mGeneration(1),
@@ -53,14 +54,8 @@
mDisableVirtualKeysTimeout(LLONG_MIN),
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
- mQueuedListener = new QueuedInputListener(listener);
-
- { // acquire lock
- std::scoped_lock _l(mLock);
-
- refreshConfigurationLocked(0);
- updateGlobalMetaStateLocked();
- } // release lock
+ refreshConfigurationLocked(0);
+ updateGlobalMetaStateLocked();
}
InputReader::~InputReader() {}
@@ -118,9 +113,9 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+ }
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
@@ -144,7 +139,7 @@
// 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();
+ mQueuedListener.flush();
}
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
@@ -160,9 +155,9 @@
}
batchSize += 1;
}
-#if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
+ }
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
@@ -340,7 +335,7 @@
// Enqueue configuration changed.
NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
- mQueuedListener->notifyConfigurationChanged(&args);
+ mQueuedListener.notifyConfigurationChanged(&args);
}
void InputReader::refreshConfigurationLocked(uint32_t changes) {
@@ -374,7 +369,7 @@
mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest;
const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now,
mCurrentPointerCaptureRequest);
- mQueuedListener->notifyPointerCaptureChanged(&args);
+ mQueuedListener.notifyPointerCaptureChanged(&args);
}
}
}
@@ -561,6 +556,7 @@
}
if (device->isIgnored()) {
+ ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId);
return;
}
@@ -786,12 +782,8 @@
std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
// No associated display. By default, can dispatch to all displays.
- if (!associatedDisplayId) {
- return true;
- }
-
- if (*associatedDisplayId == ADISPLAY_ID_NONE) {
- ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
+ if (!associatedDisplayId ||
+ *associatedDisplayId == ADISPLAY_ID_NONE) {
return true;
}
@@ -951,8 +943,8 @@
return mReader->mPolicy.get();
}
-InputListenerInterface* InputReader::ContextImpl::getListener() {
- return mReader->mQueuedListener.get();
+InputListenerInterface& InputReader::ContextImpl::getListener() {
+ return mReader->mQueuedListener;
}
EventHubInterface* InputReader::ContextImpl::getEventHub() {
diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
index a897141..2d9ffc3 100644
--- a/services/inputflinger/reader/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -20,9 +20,9 @@
namespace android {
-sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener) {
- return new InputReader(std::make_unique<EventHub>(), policy, listener);
+std::unique_ptr<InputReaderInterface> createInputReader(
+ const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener) {
+ return std::make_unique<InputReader>(std::make_unique<EventHub>(), policy, listener);
}
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 827e31a..d837689 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -22,28 +22,25 @@
//#define LOG_NDEBUG 0
// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
+static constexpr bool DEBUG_RAW_EVENTS = false;
// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
+static constexpr bool DEBUG_VIRTUAL_KEYS = false;
// Log debug messages about pointers.
-#define DEBUG_POINTERS 0
+static constexpr bool DEBUG_POINTERS = false;
// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
+static constexpr bool DEBUG_POINTER_ASSIGNMENT = false;
// Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
+static constexpr bool DEBUG_GESTURES = false;
// Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
+static constexpr bool DEBUG_VIBRATOR = false;
// Log debug messages about fusing stylus data.
-#define DEBUG_STYLUS_FUSION 0
+static constexpr bool DEBUG_STYLUS_FUSION = false;
#define INDENT " "
#define INDENT2 " "
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index 9c8a29a..a693496 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -17,9 +17,9 @@
#include <locale>
#include <regex>
-#include "../Macros.h"
+#include <ftl/enum.h>
-#include <ftl/NamedEnum.h>
+#include "../Macros.h"
#include "PeripheralController.h"
// Log detailed debug messages about input device lights.
@@ -286,7 +286,7 @@
for (const auto& [lightId, light] : mLights) {
dump += StringPrintf(INDENT4 "Id: %d", lightId);
dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
- dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
+ dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
light->dump(dump);
}
}
@@ -487,7 +487,7 @@
auto& light = it->second;
if (DEBUG_LIGHT_DETAILS) {
ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color);
+ ftl::enum_string(light->type).c_str(), color);
}
return light->setLightColor(color);
}
@@ -501,7 +501,7 @@
std::optional<int32_t> color = light->getLightColor();
if (DEBUG_LIGHT_DETAILS) {
ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
- NamedEnum::string(light->type).c_str(), color.value_or(0));
+ ftl::enum_string(light->type).c_str(), color.value_or(0));
}
return color;
}
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 3c3f88e..1f96294 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -146,6 +146,8 @@
enum class SysfsClass : uint32_t {
POWER_SUPPLY = 0,
LEDS = 1,
+
+ ftl_last = LEDS
};
enum class LightColor : uint32_t {
@@ -312,6 +314,7 @@
uint8_t* outFlags) const = 0;
virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0;
+ virtual bool hasKeyCode(int32_t deviceId, int32_t keyCode) const = 0;
/* LED related functions expect Android LED constants, not scan codes or HID usages */
virtual bool hasLed(int32_t deviceId, int32_t led) const = 0;
@@ -489,6 +492,7 @@
std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final;
bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final;
+ bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override final;
bool hasLed(int32_t deviceId, int32_t led) const override final;
void setLedState(int32_t deviceId, int32_t led, bool on) override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index f32472d..518aaa0 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -323,6 +323,7 @@
inline bool hasScanCode(int32_t scanCode) const {
return mEventHub->hasScanCode(mId, scanCode);
}
+ inline bool hasKeyCode(int32_t keyCode) const { return mEventHub->hasKeyCode(mId, keyCode); }
inline bool hasLed(int32_t led) const { return mEventHub->hasLed(mId, led); }
inline void setLedState(int32_t led, bool on) { return mEventHub->setLedState(mId, led, on); }
inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index e44aa0f..fb1d166 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -52,8 +52,7 @@
class InputReader : public InputReaderInterface {
public:
InputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener);
+ const sp<InputReaderPolicyInterface>& policy, InputListenerInterface& listener);
virtual ~InputReader();
void dump(std::string& dump) override;
@@ -143,7 +142,7 @@
void dispatchExternalStylusState(const StylusState& outState)
REQUIRES(mReader->mLock) override;
InputReaderPolicyInterface* getPolicy() REQUIRES(mReader->mLock) override;
- InputListenerInterface* getListener() REQUIRES(mReader->mLock) override;
+ InputListenerInterface& getListener() REQUIRES(mReader->mLock) override;
EventHubInterface* getEventHub() REQUIRES(mReader->mLock) override;
int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override;
void updateLedMetaState(int32_t metaState) REQUIRES(mReader->mLock) override;
@@ -164,7 +163,7 @@
// in parallel to passing it to the InputReader.
std::shared_ptr<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
- sp<QueuedInputListener> mQueuedListener;
+ QueuedInputListener mQueuedListener;
InputReaderConfiguration mConfig GUARDED_BY(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index dc807f7..823d160 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -55,7 +55,7 @@
virtual void dispatchExternalStylusState(const StylusState& outState) = 0;
virtual InputReaderPolicyInterface* getPolicy() = 0;
- virtual InputListenerInterface* getListener() = 0;
+ virtual InputListenerInterface& getListener() = 0;
virtual EventHubInterface* getEventHub() = 0;
virtual int32_t getNextId() = 0;
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 2ac41b1..fcb56ef 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -176,7 +176,7 @@
bumpGeneration();
if (changes) {
NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener()->notifyDeviceReset(&args);
+ getListener().notifyDeviceReset(&args);
}
}
@@ -188,33 +188,19 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mOrientation = DISPLAY_ORIENTATION_0;
- mDisplayWidth = 0;
- mDisplayHeight = 0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
- if (isPerWindowInputRotationEnabled()) {
- // When per-window input rotation is enabled, InputReader works in the un-rotated
- // coordinate space, so we don't need to do anything if the device is already
- // orientation-aware. If the device is not orientation-aware, then we need to apply the
- // inverse rotation of the display so that when the display rotation is applied later
- // as a part of the per-window transform, we get the expected screen coordinates.
- if (!isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = getInverseRotation(internalViewport->orientation);
- mDisplayWidth = internalViewport->deviceWidth;
- mDisplayHeight = internalViewport->deviceHeight;
- }
- }
- } else {
- if (isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
- }
+ // InputReader works in the un-rotated display coordinate space, so we don't need to do
+ // anything if the device is already orientation-aware. If the device is not
+ // orientation-aware, then we need to apply the inverse rotation of the display so that
+ // when the display rotation is applied later as a part of the per-window transform, we
+ // get the expected screen coordinates.
+ if (!isOrientedDevice) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
+ if (internalViewport) {
+ mOrientation = getInverseRotation(internalViewport->orientation);
}
}
@@ -345,15 +331,7 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (moved) {
- float dx = deltaX;
- float dy = deltaY;
- if (isPerWindowInputRotationEnabled()) {
- // Rotate the delta from InputReader's un-rotated coordinate space to
- // PointerController's rotated coordinate space that is oriented with the
- // viewport.
- rotateDelta(getInverseRotation(mOrientation), &dx, &dy);
- }
- mPointerController->move(dx, dy);
+ mPointerController->move(deltaX, deltaY);
}
if (buttonsChanged) {
@@ -364,12 +342,7 @@
}
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
- if (isPerWindowInputRotationEnabled()) {
- // Rotate the cursor position that is in PointerController's rotated coordinate space
- // to InputReader's un-rotated coordinate space.
- rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/,
- mDisplayWidth, mDisplayHeight);
- }
+
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
@@ -424,7 +397,7 @@
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&releaseArgs);
+ getListener().notifyMotion(&releaseArgs);
}
}
@@ -434,7 +407,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
if (buttonsPressed) {
BitSet32 pressed(buttonsPressed);
@@ -449,7 +422,7 @@
&pointerCoords, mXPrecision, mYPrecision,
xCursorPosition, yCursorPosition, downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&pressArgs);
+ getListener().notifyMotion(&pressArgs);
}
}
@@ -464,7 +437,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&hoverArgs);
+ getListener().notifyMotion(&hoverArgs);
}
// Send scroll events.
@@ -479,7 +452,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
yCursorPosition, downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
+ getListener().notifyMotion(&scrollArgs);
}
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 88e947f..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -105,8 +105,6 @@
VelocityControl mWheelYVelocityControl;
int32_t mOrientation;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index df1acd4..b9aef54 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -84,7 +84,9 @@
return 0;
}
-void InputMapper::updateMetaState(int32_t keyCode) {}
+bool InputMapper::updateMetaState(int32_t keyCode) {
+ return false;
+}
void InputMapper::updateExternalStylusState(const StylusState& state) {}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 15cff1c..f1c0e5a 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -48,7 +48,7 @@
inline const std::string getDeviceName() { return mDeviceContext.getName(); }
inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
- inline InputListenerInterface* getListener() { return getContext()->getListener(); }
+ inline InputListenerInterface& getListener() { return getContext()->getListener(); }
virtual uint32_t getSources() = 0;
virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
@@ -83,7 +83,11 @@
virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
virtual int32_t getMetaState();
- virtual void updateMetaState(int32_t keyCode);
+ /**
+ * Process the meta key and update the global meta state when changed.
+ * Return true if the meta key could be handled by the InputMapper.
+ */
+ virtual bool updateMetaState(int32_t keyCode);
virtual void updateExternalStylusState(const StylusState& state);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 0dc312e..6bdb121 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -344,7 +344,7 @@
&pointerProperties, &pointerCoords, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis,
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 104d087..a8602a4 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -354,7 +354,7 @@
getDisplayId(), policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
- getListener()->notifyKey(&args);
+ getListener().notifyKey(&args);
}
ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
@@ -384,8 +384,13 @@
return mMetaState;
}
-void KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+bool KeyboardInputMapper::updateMetaState(int32_t keyCode) {
+ if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) {
+ return false;
+ }
+
updateMetaStateIfNeeded(keyCode, false);
+ return true;
}
bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index ca41712..fc92320 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -40,7 +40,7 @@
const int32_t* keyCodes, uint8_t* outFlags) override;
virtual int32_t getMetaState() override;
- virtual void updateMetaState(int32_t keyCode) override;
+ virtual bool updateMetaState(int32_t keyCode) override;
virtual std::optional<int32_t> getAssociatedDisplayId() override;
virtual void updateLedState(bool reset);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index fab7f4c..4bd1cd8 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -95,13 +95,13 @@
}
if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) {
-#if DEBUG_POINTERS
- if (newSlot) {
- ALOGW("MultiTouch device emitted invalid slot index %d but it "
- "should be between 0 and %zd; ignoring this slot.",
- mCurrentSlot, mSlotCount - 1);
+ if (DEBUG_POINTERS) {
+ if (newSlot) {
+ ALOGW("MultiTouch device emitted invalid slot index %d but it "
+ "should be between 0 and %zd; ignoring this slot.",
+ mCurrentSlot, mSlotCount - 1);
+ }
}
-#endif
} else {
Slot* slot = &mSlots[mCurrentSlot];
// If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
@@ -273,19 +273,19 @@
if (id) {
outState->rawPointerData.canceledIdBits.markBit(id.value());
}
-#if DEBUG_POINTERS
- ALOGI("Stop processing slot %zu for it received a palm event from device %s", inIndex,
- getDeviceName().c_str());
-#endif
+ if (DEBUG_POINTERS) {
+ ALOGI("Stop processing slot %zu for it received a palm event from device %s",
+ inIndex, getDeviceName().c_str());
+ }
continue;
}
if (outCount >= MAX_POINTERS) {
-#if DEBUG_POINTERS
- ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
- "ignoring the rest.",
- getDeviceName().c_str(), MAX_POINTERS);
-#endif
+ if (DEBUG_POINTERS) {
+ ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+ "ignoring the rest.",
+ getDeviceName().c_str(), MAX_POINTERS);
+ }
break; // too many fingers!
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index e9d0189..b83a8fc 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -127,7 +127,7 @@
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
&pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
- getListener()->notifyMotion(&scrollArgs);
+ getListener().notifyMotion(&scrollArgs);
}
mRotaryEncoderScrollAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index a507632..677a372 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -16,8 +16,9 @@
#include <locale>
-#include "../Macros.h"
+#include <ftl/enum.h>
+#include "../Macros.h"
#include "SensorInputMapper.h"
// Log detailed debug messages about each sensor event notification to the dispatcher.
@@ -93,7 +94,7 @@
dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp);
dump += INDENT3 "Sensors:\n";
for (const auto& [sensorType, sensor] : mSensors) {
- dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str());
+ dump += StringPrintf(INDENT4 "%s\n", ftl::enum_string(sensorType).c_str());
dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled);
dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count());
dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n",
@@ -208,10 +209,10 @@
axis.max /* maxRange */, axis.scale /* resolution */,
0.0f /* power */, 0 /* minDelay */,
0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */,
- NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */,
+ ftl::enum_string(sensorType), 0 /* maxDelay */, 0 /* flags */,
getDeviceId());
- std::string prefix = "sensor." + NamedEnum::string(sensorType);
+ std::string prefix = "sensor." + ftl::enum_string(sensorType);
transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower);
int32_t reportingMode = 0;
@@ -335,7 +336,7 @@
std::chrono::microseconds maxBatchReportLatency) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld",
- NamedEnum::string(sensorType).c_str(), samplingPeriod.count(),
+ ftl::enum_string(sensorType).c_str(), samplingPeriod.count(),
maxBatchReportLatency.count());
}
@@ -359,7 +360,7 @@
void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str());
+ ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str());
}
if (!setSensorEnabled(sensorType, false /* enabled */)) {
@@ -393,13 +394,12 @@
nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when;
if (DEBUG_SENSOR_EVENT_DETAILS) {
ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]",
- NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1],
- values[2]);
+ ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]);
}
if (sensor.lastSampleTimeNs.has_value() &&
timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) {
if (DEBUG_SENSOR_EVENT_DETAILS) {
- ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str());
+ ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str());
}
} else {
// Convert to Android unit
@@ -411,7 +411,7 @@
sensor.sensorInfo.accuracy /* accuracyChanged */,
timestamp /* hwTimestamp */, values);
- getListener()->notifySensor(&args);
+ getListener().notifySensor(&args);
sensor.lastSampleTimeNs = timestamp;
sensor.accuracy = sensor.sensorInfo.accuracy;
}
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4f73681..3237824 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -58,7 +58,7 @@
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
updatedSwitchValues, mUpdatedSwitchMask);
- getListener()->notifySwitch(&args);
+ getListener().notifySwitch(&args);
mUpdatedSwitchMask = 0;
}
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 7347b2c..31a3d2e 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,7 +17,6 @@
#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
-#include <InputFlingerProperties.sysprop.h>
#include <input/DisplayViewport.h>
#include <stdint.h>
@@ -29,13 +28,6 @@
// --- Static Definitions ---
-// When per-window input rotation is enabled, display transformations such as rotation and
-// projection are part of the input window's transform. This means InputReader should work in the
-// un-rotated coordinate space.
-static bool isPerWindowInputRotationEnabled() {
- return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(false);
-}
-
static int32_t getInverseRotation(int32_t orientation) {
switch (orientation) {
case DISPLAY_ORIENTATION_90:
@@ -72,26 +64,6 @@
}
}
-// Rotates the given point (x, y) by the supplied orientation. The width and height are the
-// dimensions of the surface prior to this rotation being applied.
-static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) {
- rotateDelta(orientation, &x, &y);
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- y += width;
- break;
- case DISPLAY_ORIENTATION_180:
- x += width;
- y += height;
- break;
- case DISPLAY_ORIENTATION_270:
- x += height;
- break;
- default:
- break;
- }
-}
-
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
static bool isPointerDown(int32_t buttonState) {
@@ -110,7 +82,7 @@
!(currentButtonState & buttonState))) {
NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
- context->getListener()->notifyKey(&args);
+ context->getListener().notifyKey(&args);
}
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ac5f6b6..6f49f31 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -18,9 +18,10 @@
#include "../Macros.h"
// clang-format on
-#include <ftl/NamedEnum.h>
#include "TouchInputMapper.h"
+#include <ftl/enum.h>
+
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "TouchButtonAccumulator.h"
@@ -167,17 +168,13 @@
: InputMapper(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
- mRawSurfaceWidth(-1),
- mRawSurfaceHeight(-1),
- mSurfaceLeft(0),
- mSurfaceTop(0),
- mSurfaceRight(0),
- mSurfaceBottom(0),
+ mDisplayWidth(-1),
+ mDisplayHeight(-1),
mPhysicalWidth(-1),
mPhysicalHeight(-1),
mPhysicalLeft(0),
mPhysicalTop(0),
- mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}
+ mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -259,17 +256,15 @@
void TouchInputMapper::dump(std::string& dump) {
dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n",
- NamedEnum::string(mDeviceMode).c_str());
+ ftl::enum_string(mDeviceMode).c_str());
dumpParameters(dump);
dumpVirtualKeys(dump);
dumpRawPointerAxes(dump);
dumpCalibration(dump);
dumpAffineTransformation(dump);
- dumpSurface(dump);
+ dumpDisplay(dump);
dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
- dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
- dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
@@ -389,16 +384,16 @@
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
- // Configure device sources, surface dimensions, orientation and
+ // Configure device sources, display dimensions, orientation and
// scaling factors.
- configureSurface(when, &resetNeeded);
+ configureInputDevice(when, &resetNeeded);
}
if (changes && resetNeeded) {
// Send reset, unless this is the first time the device has been configured,
// in which case the reader will call reset itself after all mappers are ready.
NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
- getListener()->notifyDeviceReset(&args);
+ getListener().notifyDeviceReset(&args);
}
}
@@ -515,9 +510,9 @@
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
- dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n";
+ dump += INDENT4 "GestureMode: " + ftl::enum_string(mParameters.gestureMode) + "\n";
- dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n";
+ dump += INDENT4 "DeviceType: " + ftl::enum_string(mParameters.deviceType) + "\n";
dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, "
"displayId='%s'\n",
@@ -525,7 +520,7 @@
toString(mParameters.associatedDisplayIsExternal),
mParameters.uniqueDisplayId.c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
- dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
+ dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n";
}
void TouchInputMapper::configureRawPointerAxes() {
@@ -614,7 +609,7 @@
return std::make_optional(newViewport);
}
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
+void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
@@ -672,31 +667,28 @@
}
// Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.getRawWidth();
- int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ const int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ const int32_t rawHeight = mRawPointerAxes.getRawHeight();
- bool viewportChanged = mViewport != *newViewport;
+ const bool viewportChanged = mViewport != *newViewport;
bool skipViewportUpdate = false;
if (viewportChanged) {
- bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
+ const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
mViewport = *newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
- // Convert rotated viewport to natural surface coordinates.
- int32_t naturalLogicalWidth, naturalLogicalHeight;
+ // Convert rotated viewport to the natural orientation.
int32_t naturalPhysicalWidth, naturalPhysicalHeight;
int32_t naturalPhysicalLeft, naturalPhysicalTop;
int32_t naturalDeviceWidth, naturalDeviceHeight;
- // Apply the inverse of the input device orientation so that the surface is configured
- // in the same orientation as the device. The input device orientation will be
- // re-applied to mSurfaceOrientation.
- const int32_t naturalSurfaceOrientation =
+ // Apply the inverse of the input device orientation so that the input device is
+ // configured in the same orientation as the viewport. The input device orientation will
+ // be re-applied by mInputDeviceOrientation.
+ const int32_t naturalDeviceOrientation =
(mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
- switch (naturalSurfaceOrientation) {
+ switch (naturalDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
@@ -705,8 +697,6 @@
naturalDeviceHeight = mViewport.deviceWidth;
break;
case DISPLAY_ORIENTATION_180:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
@@ -715,8 +705,6 @@
naturalDeviceHeight = mViewport.deviceHeight;
break;
case DISPLAY_ORIENTATION_270:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalLeft = mViewport.physicalTop;
@@ -726,8 +714,6 @@
break;
case DISPLAY_ORIENTATION_0:
default:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalLeft = mViewport.physicalLeft;
@@ -748,49 +734,36 @@
mPhysicalLeft = naturalPhysicalLeft;
mPhysicalTop = naturalPhysicalTop;
- const int32_t oldSurfaceWidth = mRawSurfaceWidth;
- const int32_t oldSurfaceHeight = mRawSurfaceHeight;
- mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
- mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
- mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
+ const int32_t oldDisplayWidth = mDisplayWidth;
+ const int32_t oldDisplayHeight = mDisplayHeight;
+ mDisplayWidth = naturalDeviceWidth;
+ mDisplayHeight = naturalDeviceHeight;
- if (isPerWindowInputRotationEnabled()) {
- // When per-window input rotation is enabled, InputReader works in the un-rotated
- // coordinate space, so we don't need to do anything if the device is already
- // orientation-aware. If the device is not orientation-aware, then we need to apply
- // the inverse rotation of the display so that when the display rotation is applied
- // later as a part of the per-window transform, we get the expected screen
- // coordinates.
- mSurfaceOrientation = mParameters.orientationAware
- ? DISPLAY_ORIENTATION_0
- : getInverseRotation(mViewport.orientation);
- // For orientation-aware devices that work in the un-rotated coordinate space, the
- // viewport update should be skipped if it is only a change in the orientation.
- skipViewportUpdate = mParameters.orientationAware &&
- mRawSurfaceWidth == oldSurfaceWidth &&
- mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
- } else {
- mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
- : DISPLAY_ORIENTATION_0;
- }
+ // InputReader works in the un-rotated display coordinate space, so we don't need to do
+ // anything if the device is already orientation-aware. If the device is not
+ // orientation-aware, then we need to apply the inverse rotation of the display so that
+ // when the display rotation is applied later as a part of the per-window transform, we
+ // get the expected screen coordinates.
+ mInputDeviceOrientation = mParameters.orientationAware
+ ? DISPLAY_ORIENTATION_0
+ : getInverseRotation(mViewport.orientation);
+ // For orientation-aware devices that work in the un-rotated coordinate space, the
+ // viewport update should be skipped if it is only a change in the orientation.
+ skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth &&
+ mDisplayHeight == oldDisplayHeight && viewportOrientationChanged;
// Apply the input device orientation for the device.
- mSurfaceOrientation =
- (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+ mInputDeviceOrientation =
+ (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
} else {
mPhysicalWidth = rawWidth;
mPhysicalHeight = rawHeight;
mPhysicalLeft = 0;
mPhysicalTop = 0;
- mRawSurfaceWidth = rawWidth;
- mRawSurfaceHeight = rawHeight;
- mSurfaceLeft = 0;
- mSurfaceTop = 0;
- mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+ mDisplayWidth = rawWidth;
+ mDisplayHeight = rawHeight;
+ mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
}
}
@@ -819,14 +792,12 @@
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
- mSurfaceOrientation, mDeviceMode, mViewport.displayId);
+ getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+ mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
// Configure X and Y factors.
- mXScale = float(mRawSurfaceWidth) / rawWidth;
- mYScale = float(mRawSurfaceHeight) / rawHeight;
- mXTranslate = -mSurfaceLeft;
- mYTranslate = -mSurfaceTop;
+ mXScale = float(mDisplayWidth) / rawWidth;
+ mYScale = float(mDisplayHeight) / rawHeight;
mXPrecision = 1.0f / mXScale;
mYPrecision = 1.0f / mYScale;
@@ -843,7 +814,7 @@
mGeometricScale = avg(mXScale, mYScale);
// Size of diagonal axis.
- float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
+ float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
// Size factors.
if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
@@ -1005,21 +976,21 @@
// Compute oriented precision, scales and ranges.
// Note that the maximum value reported is an inclusive maximum value so it is one
- // unit less than the total width or height of surface.
- switch (mSurfaceOrientation) {
+ // unit less than the total width or height of the display.
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
case DISPLAY_ORIENTATION_270:
mOrientedXPrecision = mYPrecision;
mOrientedYPrecision = mXPrecision;
- mOrientedRanges.x.min = mYTranslate;
- mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.x.min = 0;
+ mOrientedRanges.x.max = mDisplayHeight - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
- mOrientedRanges.y.min = mXTranslate;
- mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.y.min = 0;
+ mOrientedRanges.y.max = mDisplayWidth - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -1029,14 +1000,14 @@
mOrientedXPrecision = mXPrecision;
mOrientedYPrecision = mYPrecision;
- mOrientedRanges.x.min = mXTranslate;
- mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.x.min = 0;
+ mOrientedRanges.x.max = mDisplayWidth - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
- mOrientedRanges.y.min = mYTranslate;
- mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.y.min = 0;
+ mOrientedRanges.y.max = mDisplayHeight - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -1049,7 +1020,7 @@
if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
+ float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
@@ -1083,19 +1054,15 @@
}
}
-void TouchInputMapper::dumpSurface(std::string& dump) {
+void TouchInputMapper::dumpDisplay(std::string& dump) {
dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
- dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
- dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
- dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
- dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
- dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
+ dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
+ dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
- dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
+ dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
}
void TouchInputMapper::configureVirtualKeys() {
@@ -1134,16 +1101,16 @@
int32_t halfHeight = virtualKeyDefinition.height / 2;
virtualKey.hitLeft =
- (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+ (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
touchScreenLeft;
virtualKey.hitRight =
- (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+ (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
touchScreenLeft;
- virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
- mRawSurfaceHeight +
+ virtualKey.hitTop =
+ (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
touchScreenTop;
- virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
- mRawSurfaceHeight +
+ virtualKey.hitBottom =
+ (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
touchScreenTop;
mVirtualKeys.push_back(virtualKey);
}
@@ -1409,7 +1376,7 @@
void TouchInputMapper::updateAffineTransformation() {
mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(),
- mSurfaceOrientation);
+ mInputDeviceOrientation);
}
void TouchInputMapper::reset(nsecs_t when) {
@@ -1498,14 +1465,14 @@
assignPointerIds(last, next);
}
-#if DEBUG_RAW_EVENTS
- ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
- last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
- last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
- last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
- next.rawPointerData.canceledIdBits.value);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+ "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
+ last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
+ last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
+ last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
+ next.rawPointerData.canceledIdBits.value);
+ }
if (!next.rawPointerData.touchingIdBits.isEmpty() &&
!next.rawPointerData.hoveringIdBits.isEmpty() &&
@@ -1559,9 +1526,9 @@
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(mLastRawState);
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, synthesizing event with new stylus data");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Timeout expired, synthesizing event with new stylus data");
+ }
const nsecs_t readTime = when; // consider this synthetic event to be zero latency
cookAndDispatch(when, readTime);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
@@ -1701,9 +1668,10 @@
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
- setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
+ mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mViewport.displayId);
}
bool TouchInputMapper::isTouchScreen() {
@@ -1747,24 +1715,24 @@
state.rawPointerData.pointerCount != 0;
if (initialDown) {
if (mExternalStylusState.pressure != 0.0f) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Have both stylus and touch data, beginning fusion");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Have both stylus and touch data, beginning fusion");
+ }
mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
} else if (timeout) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, assuming touch is not a stylus.");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Timeout expired, assuming touch is not a stylus.");
+ }
resetExternalStylus();
} else {
if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
}
-#if DEBUG_STYLUS_FUSION
- ALOGD("No stylus data but stylus is connected, requesting timeout "
- "(%" PRId64 "ms)",
- mExternalStylusFusionTimeout);
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("No stylus data but stylus is connected, requesting timeout "
+ "(%" PRId64 "ms)",
+ mExternalStylusFusionTimeout);
+ }
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
return true;
}
@@ -1772,9 +1740,9 @@
// Check if the stylus pointer has gone up.
if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Stylus pointer is going up");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Stylus pointer is going up");
+ }
mExternalStylusId = -1;
}
@@ -1815,10 +1783,10 @@
// Pointer went up while virtual key was down.
mCurrentVirtualKey.down = false;
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
@@ -1842,10 +1810,10 @@
// into the main display surface.
mCurrentVirtualKey.down = false;
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
- mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
AKEY_EVENT_FLAG_CANCELED);
@@ -1857,8 +1825,10 @@
// Pointer just went down. Check for virtual key press or off-screen touches.
uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
- // Exclude unscaled device for inside surface checking.
- if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) {
+ // Skip checking whether the pointer is inside the physical frame if the device is in
+ // unscaled mode.
+ if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
+ mDeviceMode != DeviceMode::UNSCALED) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1873,10 +1843,10 @@
virtualKey->scanCode);
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
AKEY_EVENT_FLAG_FROM_SYSTEM |
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
@@ -1919,7 +1889,7 @@
NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
keyEventFlags, keyCode, scanCode, metaState, downTime);
- getListener()->notifyKey(&args);
+ getListener().notifyKey(&args);
}
void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
@@ -2127,7 +2097,7 @@
}
// Walk through the the active pointers and map device coordinates onto
- // surface coordinates and adjust for display orientation.
+ // display coordinates and adjust for display orientation.
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
@@ -2287,15 +2257,15 @@
mAffineTransform.applyTo(xTransformed, yTransformed);
rotateAndScale(xTransformed, yTransformed);
- // Adjust X, Y, and coverage coords for surface orientation.
+ // Adjust X, Y, and coverage coords for input device orientation.
float left, top, right, bottom;
- switch (mSurfaceOrientation) {
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
- left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
- top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
+ left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
+ right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+ bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
+ top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
orientation -= M_PI_2;
if (mOrientedRanges.haveOrientation &&
orientation < mOrientedRanges.orientation.min) {
@@ -2306,8 +2276,8 @@
case DISPLAY_ORIENTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
- bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
- top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
+ bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
+ top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
orientation -= M_PI;
if (mOrientedRanges.haveOrientation &&
orientation < mOrientedRanges.orientation.min) {
@@ -2318,8 +2288,8 @@
case DISPLAY_ORIENTATION_270:
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
- bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+ top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
orientation += M_PI_2;
if (mOrientedRanges.haveOrientation &&
orientation > mOrientedRanges.orientation.max) {
@@ -2328,10 +2298,10 @@
}
break;
default:
- left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
+ right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+ bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+ top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
break;
}
@@ -2441,9 +2411,10 @@
}
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- setTouchSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId());
+ mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits,
+ mPointerController->getDisplayId());
}
} else {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -2593,7 +2564,8 @@
// the pointer is hovering again even if the user is not currently touching
// the touch pad. This ensures that a view will receive a fresh hover enter
// event after a tap.
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
PointerProperties pointerProperties;
pointerProperties.clear();
@@ -2611,7 +2583,7 @@
metaState, buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
// Update state.
@@ -2662,9 +2634,9 @@
// Handle TAP timeout.
if (isTimeout) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Processing timeout");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Processing timeout");
+ }
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
@@ -2673,9 +2645,9 @@
mConfig.pointerGestureTapDragInterval);
} else {
// The tap is finished.
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP finished");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP finished");
+ }
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = -1;
@@ -2771,10 +2743,11 @@
// Switch states based on button and pointer state.
if (isQuietTime) {
// Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
- ALOGD("Gestures: QUIET for next %0.3fms",
- (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: QUIET for next %0.3fms",
+ (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
+ 0.000001f);
+ }
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
*outFinishPreviousGesture = true;
}
@@ -2798,11 +2771,11 @@
// active. If the user first puts one finger down to click then adds another
// finger to drag then the active pointer should switch to the finger that is
// being dragged.
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
- "currentFingerCount=%d",
- activeTouchId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+ "currentFingerCount=%d",
+ activeTouchId, currentFingerCount);
+ }
// Reset state when just starting.
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
*outFinishPreviousGesture = true;
@@ -2827,11 +2800,11 @@
}
if (bestId >= 0 && bestId != activeTouchId) {
mPointerGesture.activeTouchId = activeTouchId = bestId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
- "bestId=%d, bestSpeed=%0.3f",
- bestId, bestSpeed);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+ "bestId=%d, bestSpeed=%0.3f",
+ bestId, bestSpeed);
+ }
}
}
@@ -2844,18 +2817,19 @@
deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
// Move the pointer using a relative motion.
// When using spots, the click will occur at the position of the anchor
// spot and all other spots will move there.
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
@@ -2881,12 +2855,13 @@
mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP");
+ }
mPointerGesture.tapUpTime = when;
getContext()->requestTimeoutAtTime(when +
@@ -2912,29 +2887,29 @@
tapped = true;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
+ y - mPointerGesture.tapY);
+ }
}
} else {
-#if DEBUG_GESTURES
- if (mPointerGesture.tapDownTime != LLONG_MIN) {
- ALOGD("Gestures: Not a TAP, %0.3fms since down",
- (when - mPointerGesture.tapDownTime) * 0.000001f);
- } else {
- ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ if (DEBUG_GESTURES) {
+ if (mPointerGesture.tapDownTime != LLONG_MIN) {
+ ALOGD("Gestures: Not a TAP, %0.3fms since down",
+ (when - mPointerGesture.tapDownTime) * 0.000001f);
+ } else {
+ ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ }
}
-#endif
}
}
mPointerVelocityControl.reset();
if (!tapped) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: NEUTRAL");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: NEUTRAL");
+ }
mPointerGesture.activeGestureId = -1;
mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
@@ -2949,21 +2924,22 @@
mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX, y - mPointerGesture.tapY);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+ x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+ }
}
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
- (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+ (when - mPointerGesture.tapUpTime) * 0.000001f);
+ }
}
} else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2978,26 +2954,26 @@
deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
// Move the pointer using a relative motion.
// When using spots, the hover or drag will occur at the position of the anchor spot.
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
bool down;
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP_DRAG");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP_DRAG");
+ }
down = true;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: HOVER");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: HOVER");
+ }
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
*outFinishPreviousGesture = true;
}
@@ -3005,7 +2981,8 @@
down = false;
}
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3050,12 +3027,13 @@
} else if (!settled && currentFingerCount > lastFingerCount) {
// Additional pointers have gone down but not yet settled.
// Reset the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
- when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Resetting gesture since additional pointers went down for "
+ "MULTITOUCH, settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime +
+ mConfig.pointerGestureMultitouchSettleInterval - when) *
+ 0.000001f);
+ }
*outCancelPreviousGesture = true;
} else {
// Continue previous gesture.
@@ -3069,18 +3047,18 @@
mPointerVelocityControl.reset();
// Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
- when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime +
+ mConfig.pointerGestureMultitouchSettleInterval - when) *
+ 0.000001f);
+ }
mCurrentRawState.rawPointerData
.getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
&mPointerGesture.referenceTouchY);
- auto [x, y] = getMouseCursorPosition();
- mPointerGesture.referenceGestureX = x;
- mPointerGesture.referenceGestureY = y;
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
}
// Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3133,10 +3111,10 @@
if (distOverThreshold >= 2) {
if (currentFingerCount > 2) {
// There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
@@ -3152,10 +3130,11 @@
if (mutualDistance > mPointerGestureMaxSwipeWidth) {
// There are two pointers but they are too far apart for a SWIPE,
// switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > "
+ "%0.3f",
+ mutualDistance, mPointerGestureMaxSwipeWidth);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
@@ -3180,25 +3159,25 @@
float cosine = dot / (dist1 * dist2); // denominator always > 0
if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
// Pointers are moving in the same direction. Switch to SWIPE.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ }
mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
} else {
// Pointers are moving in different directions. Switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
@@ -3210,10 +3189,10 @@
// Switch from SWIPE to FREEFORM if additional pointers go down.
// Cancel previous gesture.
if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
@@ -3236,7 +3215,7 @@
commonDeltaX *= mPointerXMovementScale;
commonDeltaY *= mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
+ rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
mPointerGesture.referenceGestureX += commonDeltaX;
@@ -3247,11 +3226,11 @@
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// PRESS or SWIPE mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ }
ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
mPointerGesture.currentGestureIdBits.clear();
@@ -3268,11 +3247,11 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
} else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
// FREEFORM mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ }
ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
mPointerGesture.currentGestureIdBits.clear();
@@ -3311,13 +3290,13 @@
}
}
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM follow up "
- "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
- "activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM follow up "
+ "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value,
+ mPointerGesture.activeGestureId);
+ }
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
for (uint32_t i = 0; i < currentFingerCount; i++) {
@@ -3326,18 +3305,18 @@
if (!mappedTouchIdBits.hasBit(touchId)) {
gestureId = usedGestureIdBits.markFirstUnmarkedBit();
mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM "
+ "new mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
} else {
gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM "
+ "existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
}
mPointerGesture.currentGestureIdBits.markBit(gestureId);
mPointerGesture.currentGestureIdToIndex[gestureId] = i;
@@ -3346,7 +3325,7 @@
mCurrentRawState.rawPointerData.pointerForId(touchId);
float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerGesture.currentGestureProperties[i].clear();
mPointerGesture.currentGestureProperties[i].id = gestureId;
@@ -3366,47 +3345,46 @@
if (mPointerGesture.activeGestureId < 0) {
mPointerGesture.activeGestureId =
mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM new "
- "activeGestureId=%d",
- mPointerGesture.activeGestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM new activeGestureId=%d",
+ mPointerGesture.activeGestureId);
+ }
}
}
}
mPointerController->setButtonState(mCurrentRawState.buttonState);
-#if DEBUG_GESTURES
- ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
- "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
- "lastGestureMode=%d, lastGestureIdBits=0x%08x",
- toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
- mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
- mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
- for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
- ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+ "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+ "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+ toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+ mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+ mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+ for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+ ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+ for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+ ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
}
- for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
- ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
-#endif
return true;
}
@@ -3418,13 +3396,15 @@
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
- setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
- mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
+ mPointerController
+ ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
+ mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
down = !hovering;
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[index]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3462,10 +3442,10 @@
mLastRawState.rawPointerData.pointers[lastIndex].y) *
mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -3473,7 +3453,8 @@
down = isPointerDown(mCurrentRawState.buttonState);
hovering = !down;
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3513,7 +3494,8 @@
}
int32_t displayId = mPointerController->getDisplayId();
- auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3526,7 +3508,7 @@
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
xCursorPosition, yCursorPosition, mPointerSimple.downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
if (mPointerSimple.hovering && !hovering) {
@@ -3540,7 +3522,7 @@
&mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
xCursorPosition, yCursorPosition, mPointerSimple.downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
if (down) {
@@ -3556,7 +3538,7 @@
&mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
// Send move.
@@ -3567,7 +3549,7 @@
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, xCursorPosition, yCursorPosition,
mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
if (hovering) {
@@ -3582,7 +3564,7 @@
&mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
// Send hover move.
@@ -3593,7 +3575,7 @@
&mPointerSimple.currentCoords, mOrientedXPrecision,
mOrientedYPrecision, xCursorPosition, yCursorPosition,
mPointerSimple.downTime, /* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) {
@@ -3615,7 +3597,7 @@
&pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
xCursorPosition, yCursorPosition, mPointerSimple.downTime,
/* videoFrames */ {});
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
// Save state.
@@ -3679,21 +3661,19 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- auto [x, y] = getMouseCursorPosition();
- xCursorPosition = x;
- yCursorPosition = y;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
- [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+ [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
policyFlags, action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
downTime, std::move(frames));
- getListener()->notifyMotion(&args);
+ getListener().notifyMotion(&args);
}
bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties,
@@ -3731,60 +3711,59 @@
abortTouches(when, readTime, 0 /* policyFlags*/);
}
-// Transform raw coordinate to surface coordinate
-void TouchInputMapper::rotateAndScale(float& x, float& y) {
- // Scale to surface coordinate.
+// Transform input device coordinates to display panel coordinates.
+void TouchInputMapper::rotateAndScale(float& x, float& y) const {
const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
- // Rotate to surface coordinate.
+ // Rotate to display coordinate.
// 0 - no swap and reverse.
// 90 - swap x/y and reverse y.
// 180 - reverse x, y.
// 270 - swap x/y and reverse x.
- switch (mSurfaceOrientation) {
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_0:
- x = xScaled + mXTranslate;
- y = yScaled + mYTranslate;
+ x = xScaled;
+ y = yScaled;
break;
case DISPLAY_ORIENTATION_90:
- y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
- x = yScaled + mYTranslate;
+ y = xScaledMax;
+ x = yScaled;
break;
case DISPLAY_ORIENTATION_180:
- x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
- y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+ x = xScaledMax;
+ y = yScaledMax;
break;
case DISPLAY_ORIENTATION_270:
- y = xScaled + mXTranslate;
- x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+ y = xScaled;
+ x = yScaledMax;
break;
default:
assert(false);
}
}
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const {
const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
+ xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
+ yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
for (const VirtualKey& virtualKey : mVirtualKeys) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft,
+ virtualKey.hitTop, virtualKey.hitRight, virtualKey.hitBottom);
+ }
if (virtualKey.isHit(x, y)) {
return &virtualKey;
@@ -3881,13 +3860,13 @@
}
}
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
- heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+ heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ }
}
-#endif
// Pull matches out by increasing order of distance.
// To avoid reassigning pointers that have already been matched, the loop keeps track
@@ -3926,13 +3905,14 @@
parentIndex = childIndex;
}
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t j = 0; j < heapSize; j++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
- heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t j = 0; j < heapSize; j++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
+ j, heap[j].currentPointerIndex, heap[j].lastPointerIndex,
+ heap[j].distance);
+ }
}
-#endif
}
heapSize -= 1;
@@ -3954,11 +3934,11 @@
currentPointerIndex));
usedIdBits.markBit(id);
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
- ", distance=%" PRIu64,
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+ ", distance=%" PRIu64,
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+ }
break;
}
}
@@ -3973,9 +3953,10 @@
current.rawPointerData.markIdBit(id,
current.rawPointerData.isHovering(currentPointerIndex));
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
-#endif
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex,
+ id);
+ }
}
}
@@ -4031,63 +4012,4 @@
return std::nullopt;
}
-void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
- if (isPerWindowInputRotationEnabled()) {
- // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate
- // space that is oriented with the viewport.
- rotateDelta(mViewport.orientation, &dx, &dy);
- }
-
- mPointerController->move(dx, dy);
-}
-
-std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const {
- float x = 0;
- float y = 0;
- mPointerController->getPosition(&x, &y);
-
- if (!isPerWindowInputRotationEnabled()) return {x, y};
- if (!mViewport.isValid()) return {x, y};
-
- // Convert from PointerController's rotated coordinate space that is oriented with the viewport
- // to InputReader's un-rotated coordinate space.
- const int32_t orientation = getInverseRotation(mViewport.orientation);
- rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight);
- return {x, y};
-}
-
-void TouchInputMapper::setMouseCursorPosition(float x, float y) const {
- if (isPerWindowInputRotationEnabled() && mViewport.isValid()) {
- // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
- // coordinate space that is oriented with the viewport.
- rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
- }
-
- mPointerController->setPosition(x, y);
-}
-
-void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) {
- std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
-
- for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
- const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
- float x = spotCoords[index].getX();
- float y = spotCoords[index].getY();
- float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-
- if (isPerWindowInputRotationEnabled()) {
- // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
- // coordinate space.
- rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
- }
-
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
- }
-
- mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
-}
-
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index e104220..9b020a6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -185,6 +185,8 @@
UNSCALED, // unscaled mapping (touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
POINTER, // pointer mapping (pointer)
+
+ ftl_last = POINTER
};
DeviceMode mDeviceMode;
@@ -198,6 +200,8 @@
TOUCH_PAD,
TOUCH_NAVIGATION,
POINTER,
+
+ ftl_last = POINTER
};
DeviceType deviceType;
@@ -210,6 +214,8 @@
ORIENTATION_90 = DISPLAY_ORIENTATION_90,
ORIENTATION_180 = DISPLAY_ORIENTATION_180,
ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+
+ ftl_last = ORIENTATION_270
};
Orientation orientation;
@@ -219,6 +225,8 @@
enum class GestureMode {
SINGLE_TOUCH,
MULTI_TOUCH,
+
+ ftl_last = MULTI_TOUCH
};
GestureMode gestureMode;
@@ -398,8 +406,8 @@
virtual void dumpParameters(std::string& dump);
virtual void configureRawPointerAxes();
virtual void dumpRawPointerAxes(std::string& dump);
- virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
- virtual void dumpSurface(std::string& dump);
+ virtual void configureInputDevice(nsecs_t when, bool* outResetNeeded);
+ virtual void dumpDisplay(std::string& dump);
virtual void configureVirtualKeys();
virtual void dumpVirtualKeys(std::string& dump);
virtual void parseCalibration();
@@ -418,38 +426,27 @@
// The components of the viewport are specified in the display's rotated orientation.
DisplayViewport mViewport;
- // The surface orientation, width and height set by configureSurface().
- // The width and height are derived from the viewport but are specified
+ // The width and height are obtained from the viewport and are specified
// in the natural orientation.
- // They could be used for calculating diagonal, scaling factors, and virtual keys.
- int32_t mRawSurfaceWidth;
- int32_t mRawSurfaceHeight;
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
- // The surface origin specifies how the surface coordinates should be translated
- // to align with the logical display coordinate space.
- int32_t mSurfaceLeft;
- int32_t mSurfaceTop;
- int32_t mSurfaceRight;
- int32_t mSurfaceBottom;
-
- // Similar to the surface coordinates, but in the raw display coordinate space rather than in
- // the logical coordinate space.
+ // The physical frame is the rectangle in the display's coordinate space that maps to the
+ // the logical display frame.
int32_t mPhysicalWidth;
int32_t mPhysicalHeight;
int32_t mPhysicalLeft;
int32_t mPhysicalTop;
- // The orientation may be different from the viewport orientation as it specifies
- // the rotation of the surface coordinates required to produce the viewport's
- // requested orientation, so it will depend on whether the device is orientation aware.
- int32_t mSurfaceOrientation;
+ // The orientation of the input device relative to that of the display panel. It specifies
+ // the rotation of the input device coordinates required to produce the display panel
+ // orientation, so it will depend on whether the device is orientation aware.
+ int32_t mInputDeviceOrientation;
// Translation and scaling factors, orientation-independent.
- float mXTranslate;
float mXScale;
float mXPrecision;
- float mYTranslate;
float mYScale;
float mYPrecision;
@@ -799,23 +796,15 @@
// touchscreen.
void updateTouchSpots();
- bool isPointInsideSurface(int32_t x, int32_t y);
+ bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const;
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
static void assignPointerIds(const RawState& last, RawState& current);
const char* modeToString(DeviceMode deviceMode);
- void rotateAndScale(float& x, float& y);
-
- // Wrapper methods for interfacing with PointerController. These are used to convert points
- // between the coordinate spaces used by InputReader and PointerController, if they differ.
- void moveMouseCursor(float dx, float dy) const;
- std::pair<float, float> getMouseCursorPosition() const;
- void setMouseCursorPosition(float x, float y) const;
- void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId);
+ void rotateAndScale(float& x, float& y) const;
};
} // namespace android
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 3df6f36..1976fed 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -41,10 +41,10 @@
void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
- sequence.toString().c_str(), repeat, token);
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
+ sequence.toString().c_str(), repeat, token);
+ }
mVibrating = true;
mSequence = sequence;
@@ -54,14 +54,14 @@
// Request InputReader to notify InputManagerService for vibration started.
NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true);
- getListener()->notifyVibratorState(&args);
+ getListener().notifyVibratorState(&args);
nextStep();
}
void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+ }
if (mVibrating && mToken == token) {
stopVibrating();
@@ -87,9 +87,9 @@
}
void VibratorInputMapper::nextStep() {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
+ }
mIndex += 1;
if (size_t(mIndex) >= mSequence.pattern.size()) {
if (mRepeat < 0) {
@@ -102,16 +102,16 @@
const VibrationElement& element = mSequence.pattern[mIndex];
if (element.isOn()) {
-#if DEBUG_VIBRATOR
- std::string description = element.toString();
- ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
- description.c_str());
-#endif
+ if (DEBUG_VIBRATOR) {
+ std::string description = element.toString();
+ ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ description.c_str());
+ }
getDeviceContext().vibrate(element);
} else {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+ }
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -119,21 +119,21 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
+ }
}
void VibratorInputMapper::stopVibrating() {
mVibrating = false;
-#if DEBUG_VIBRATOR
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+ }
getDeviceContext().cancelVibrate();
// Request InputReader to notify InputManagerService for vibration complete.
NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false);
- getListener()->notifyVibratorState(&args);
+ getListener().notifyVibratorState(&args);
}
void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp
deleted file mode 100644
index b9d65ee..0000000
--- a/services/inputflinger/sysprop/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-sysprop_library {
- name: "InputFlingerProperties",
- srcs: ["*.sysprop"],
- api_packages: ["android.sysprop"],
- property_owner: "Platform",
-}
diff --git a/services/inputflinger/sysprop/InputFlingerProperties.sysprop b/services/inputflinger/sysprop/InputFlingerProperties.sysprop
deleted file mode 100644
index 1c7e724..0000000
--- a/services/inputflinger/sysprop/InputFlingerProperties.sysprop
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 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.
-
-module: "android.sysprop.InputFlingerProperties"
-owner: Platform
-
-# When per-window-input-rotation is enabled, InputReader works in the un-rotated
-# display coordinate space, and the display rotation is encoded as part of the
-# input window transform that is sent from SurfaceFlinger to InputDispatcher.
-prop {
- api_name: "per_window_input_rotation"
- type: Boolean
- scope: Internal
- access: ReadWrite
- prop_name: "persist.debug.per_window_input_rotation"
-}
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 3a9994e..f13187d 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -56,18 +56,10 @@
class InputClassifierTest : public testing::Test {
protected:
- sp<InputClassifierInterface> mClassifier;
- sp<TestInputListener> mTestListener;
+ TestInputListener mTestListener;
+ std::unique_ptr<InputClassifierInterface> mClassifier;
- virtual void SetUp() override {
- mTestListener = new TestInputListener();
- mClassifier = new InputClassifier(mTestListener);
- }
-
- virtual void TearDown() override {
- mClassifier.clear();
- mTestListener.clear();
- }
+ void SetUp() override { mClassifier = std::make_unique<InputClassifier>(mTestListener); }
};
/**
@@ -80,7 +72,7 @@
mClassifier->notifyConfigurationChanged(&args);
NotifyConfigurationChangedArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled(&outArgs));
+ ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs));
ASSERT_EQ(args, outArgs);
}
@@ -93,7 +85,7 @@
mClassifier->notifyKey(&args);
NotifyKeyArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&outArgs));
+ ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs));
ASSERT_EQ(args, outArgs);
}
@@ -106,7 +98,7 @@
NotifyMotionArgs motionArgs = generateBasicMotionArgs();
mClassifier->notifyMotion(&motionArgs);
NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args));
ASSERT_EQ(motionArgs, args);
}
@@ -120,7 +112,7 @@
mClassifier->notifySwitch(&args);
NotifySwitchArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifySwitchWasCalled(&outArgs));
+ ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs));
ASSERT_EQ(args, outArgs);
}
@@ -133,7 +125,7 @@
mClassifier->notifyDeviceReset(&args);
NotifyDeviceResetArgs outArgs;
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyDeviceResetWasCalled(&outArgs));
+ ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs));
ASSERT_EQ(args, outArgs);
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ee4d3b8..2c64271 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,6 +16,7 @@
#include "../dispatcher/InputDispatcher.h"
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
@@ -73,6 +74,12 @@
return event;
}
+static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) {
+ ASSERT_EQ(expectedAction, receivedAction)
+ << "expected " << MotionEvent::actionToString(expectedAction) << ", got "
+ << MotionEvent::actionToString(receivedAction);
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -85,13 +92,29 @@
FakeInputDispatcherPolicy() {}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
- args.displayId);
+ assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& keyEvent = static_cast<const KeyEvent&>(event);
+ EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(keyEvent.getAction(), args.action);
+ });
}
- void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
- args.displayId);
+ void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
+ assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& motionEvent = static_cast<const MotionEvent&>(event);
+ EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(motionEvent.getAction(), args.action);
+ EXPECT_EQ(motionEvent.getX(0), point.x);
+ EXPECT_EQ(motionEvent.getY(0), point.y);
+ EXPECT_EQ(motionEvent.getRawX(0), point.x);
+ EXPECT_EQ(motionEvent.getRawY(0), point.y);
+ });
}
void assertFilterInputEventWasNotCalled() {
@@ -196,21 +219,21 @@
template <class T>
T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
- const std::chrono::time_point start = std::chrono::steady_clock::now();
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
-
// If there is an ANR, Dispatcher won't be idle because there are still events
// in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
// before checking if ANR was called.
// Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
// to provide it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait,
- [&storage]() REQUIRES(mLock) { return !storage.empty(); });
- const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- if (storage.empty()) {
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::optional<T> token =
+ getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+ if (!token.has_value()) {
ADD_FAILURE() << "Did not receive the ANR callback";
return {};
}
+
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
if (std::chrono::abs(timeout - waited) > 100ms) {
@@ -220,9 +243,24 @@
<< std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
<< "ms instead";
}
- T token = storage.front();
+ return *token;
+ }
+
+ template <class T>
+ std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+ std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock,
+ std::condition_variable& condition)
+ REQUIRES(mLock) {
+ condition.wait_for(lock, timeout,
+ [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+ if (storage.empty()) {
+ ADD_FAILURE() << "Did not receive the expected callback";
+ return std::nullopt;
+ }
+ T item = storage.front();
storage.pop();
- return token;
+ return std::make_optional(item);
}
void assertNotifyAnrWasNotCalled() {
@@ -280,6 +318,16 @@
mNotifyDropWindowWasCalled = false;
}
+ void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<sp<IBinder>> receivedToken =
+ getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
+ mNotifyInputChannelBroken);
+ ASSERT_TRUE(receivedToken.has_value());
+ ASSERT_EQ(token, *receivedToken);
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -298,6 +346,8 @@
std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
+ std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
+ std::condition_variable mNotifyInputChannelBroken;
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
@@ -338,7 +388,11 @@
mNotifyAnr.notify_all();
}
- void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override {
+ std::scoped_lock lock(mLock);
+ mBrokenInputChannels.push(connectionToken);
+ mNotifyInputChannelBroken.notify_all();
+ }
void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
@@ -418,26 +472,11 @@
mDropTargetWindowToken = token;
}
- void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
- int32_t displayId) {
+ void assertFilterInputEventWasCalledInternal(
+ const std::function<void(const InputEvent&)>& verify) {
std::scoped_lock lock(mLock);
ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), type);
-
- if (type == AINPUT_EVENT_TYPE_KEY) {
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
- EXPECT_EQ(keyEvent.getEventTime(), eventTime);
- EXPECT_EQ(keyEvent.getAction(), action);
- EXPECT_EQ(keyEvent.getDisplayId(), displayId);
- } else if (type == AINPUT_EVENT_TYPE_MOTION) {
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
- EXPECT_EQ(motionEvent.getEventTime(), eventTime);
- EXPECT_EQ(motionEvent.getAction(), action);
- EXPECT_EQ(motionEvent.getDisplayId(), displayId);
- } else {
- FAIL() << "Unknown type: " << type;
- }
-
+ verify(*mFilteredEvent);
mFilteredEvent = nullptr;
}
};
@@ -447,11 +486,11 @@
class InputDispatcherTest : public testing::Test {
protected:
sp<FakeInputDispatcherPolicy> mFakePolicy;
- sp<InputDispatcher> mDispatcher;
+ std::unique_ptr<InputDispatcher> mDispatcher;
void SetUp() override {
mFakePolicy = new FakeInputDispatcherPolicy();
- mDispatcher = new InputDispatcher(mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
// Start InputDispatcher thread
ASSERT_EQ(OK, mDispatcher->start());
@@ -460,7 +499,7 @@
void TearDown() override {
ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.clear();
- mDispatcher.clear();
+ mDispatcher.reset();
}
/**
@@ -535,8 +574,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
/*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -549,8 +588,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -562,8 +600,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -576,8 +613,7 @@
(1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -589,8 +625,7 @@
(~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE,
- ARBITRARY_TIME, ARBITRARY_TIME,
+ identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -601,8 +636,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 0, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -612,8 +647,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -625,8 +660,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -637,8 +672,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -651,8 +686,8 @@
event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME,
+ ARBITRARY_TIME,
/*pointerCount*/ 2, pointerProperties, pointerCoords);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
@@ -683,7 +718,11 @@
// --- InputDispatcherTest SetInputWindowTest ---
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ android::base::HwTimeoutMultiplier());
class FakeApplicationHandle : public InputApplicationHandle {
public:
@@ -796,7 +835,8 @@
}
case AINPUT_EVENT_TYPE_MOTION: {
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
+
if (expectedFlags.has_value()) {
EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
}
@@ -808,6 +848,9 @@
case AINPUT_EVENT_TYPE_CAPTURE: {
FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
}
+ case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+ FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
+ }
case AINPUT_EVENT_TYPE_DRAG: {
FAIL() << "Use 'consumeDragEvent' for DRAG events";
}
@@ -830,7 +873,6 @@
FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
- EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
}
void consumeCaptureEvent(bool hasCapture) {
@@ -865,6 +907,20 @@
EXPECT_EQ(y, dragEvent.getY());
}
+ void consumeTouchModeEvent(bool inTouchMode) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_TOUCH_MODE, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of TOUCH_MODE event";
+
+ ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+ const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+ EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
if (event == nullptr) {
@@ -886,6 +942,10 @@
const auto& captureEvent = static_cast<CaptureEvent&>(*event);
ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
<< (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
+ } else if (event->getType() == AINPUT_EVENT_TYPE_TOUCH_MODE) {
+ const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
+ ADD_FAILURE() << "Received touch mode event, inTouchMode = "
+ << (touchModeEvent.isInTouchMode() ? "true" : "false");
}
FAIL() << mName.c_str()
<< ": should not have received any events, so consume() should return NULL";
@@ -906,7 +966,7 @@
static const int32_t HEIGHT = 800;
FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, const std::string name,
+ const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt)
: mName(name) {
if (token == std::nullopt) {
@@ -944,7 +1004,7 @@
sp<FakeWindowHandle> clone(
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, int32_t displayId) {
+ const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId) {
sp<FakeWindowHandle> handle =
new FakeWindowHandle(inputApplicationHandle, dispatcher, mInfo.name + "(Mirror)",
displayId, mInfo.token);
@@ -967,16 +1027,24 @@
void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
- void setFrame(const Rect& frame) {
+ 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.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
+
+ const Rect logicalDisplayFrame = displayTransform.transform(frame);
+ ui::Transform translate;
+ translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
+ mInfo.transform = translate * displayTransform;
}
+ void setType(WindowInfo::Type type) { mInfo.type = type; }
+
+ void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
+
void addFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags |= flags; }
void setFlags(Flags<WindowInfo::Flag> flags) { mInfo.flags = flags; }
@@ -1074,6 +1142,12 @@
mInputReceiver->consumeDragEvent(isExiting, x, y);
}
+ void consumeTouchModeEvent(bool inTouchMode) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeTouchModeEvent(inTouchMode);
+ }
+
std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -1132,6 +1206,8 @@
mInfo.ownerUid = ownerUid;
}
+ void destroyReceiver() { mInputReceiver = nullptr; }
+
private:
const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1141,7 +1217,7 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
static InputEventInjectionResult injectKey(
- const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+ const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1163,7 +1239,7 @@
injectionTimeout, policyFlags);
}
-static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
}
@@ -1171,14 +1247,14 @@
// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
// has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(const sp<InputDispatcher>& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(
+ const std::unique_ptr<InputDispatcher>& dispatcher, int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId,
InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
/* allowKeyRepeat */ false);
}
-static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectKeyUp(const std::unique_ptr<InputDispatcher>& dispatcher,
int32_t displayId = ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
}
@@ -1280,9 +1356,8 @@
mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE,
mButtonState, MotionClassification::NONE, identityTransform,
/* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition,
- mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight,
- mEventTime, mEventTime, mPointers.size(), pointerProperties.data(),
- pointerCoords.data());
+ mRawYCursorPosition, identityTransform, mEventTime, mEventTime,
+ mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -1297,15 +1372,12 @@
int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
- uint32_t mDisplayOrientation{ui::Transform::ROT_0};
- int32_t mDisplayWidth{INVALID_DISPLAY_SIZE};
- int32_t mDisplayHeight{INVALID_DISPLAY_SIZE};
std::vector<PointerBuilder> mPointers;
};
static InputEventInjectionResult injectMotionEvent(
- const sp<InputDispatcher>& dispatcher, const MotionEvent& event,
+ const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
@@ -1314,8 +1386,8 @@
}
static InputEventInjectionResult injectMotionEvent(
- const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
- const PointF& position,
+ const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
+ int32_t displayId, const PointF& position,
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1335,13 +1407,13 @@
return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
}
-static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher,
- int32_t source, int32_t displayId,
- const PointF& location = {100, 200}) {
+static InputEventInjectionResult injectMotionDown(
+ const std::unique_ptr<InputDispatcher>& dispatcher, int32_t source, int32_t displayId,
+ const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
-static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher,
+static InputEventInjectionResult injectMotionUp(const std::unique_ptr<InputDispatcher>& dispatcher,
int32_t source, int32_t displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
@@ -1399,6 +1471,23 @@
return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
}
+/**
+ * When a window unexpectedly disposes of its input channel, policy should be notified about the
+ * broken channel.
+ */
+TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window that breaks its input channel",
+ ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Window closes its channel, but the window remains.
+ window->destroyReceiver();
+ mFakePolicy->assertNotifyInputChannelBrokenWasCalled(window->getInfo()->token);
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -1413,6 +1502,21 @@
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ // Inject a MotionEvent to an unknown display.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
/**
* Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues.
* To ensure that window receives only events that were directly inside of it, add
@@ -1480,6 +1584,244 @@
windowSecond->assertNoEvents();
}
+/**
+ * Two windows: A top window, and a wallpaper behind the window.
+ * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window
+ * gets ACTION_CANCEL.
+ * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true)
+ * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setHasWallpaper(true);
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ foregroundWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ foregroundWindow->consumeMotionMove();
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Now the foreground window goes away, but the wallpaper stays
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ foregroundWindow->consumeMotionCancel();
+ // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+}
+
+/**
+ * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
+ * with the following differences:
+ * After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to
+ * clean up the connection.
+ * This later may crash dispatcher during ACTION_CANCEL synthesis, if the dispatcher is not careful.
+ * Ensure that there's no crash in the dispatcher.
+ */
+TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setHasWallpaper(true);
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ foregroundWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ foregroundWindow->consumeMotionMove();
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Wallpaper closes its channel, but the window remains.
+ wallpaperWindow->destroyReceiver();
+ mFakePolicy->assertNotifyInputChannelBrokenWasCalled(wallpaperWindow->getInfo()->token);
+
+ // Now the foreground window goes away, but the wallpaper stays, even though its channel
+ // is no longer valid.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ foregroundWindow->consumeMotionCancel();
+}
+
+/**
+ * A single window that receives touch (on top), and a wallpaper window underneath it.
+ * The top window gets a multitouch gesture.
+ * Ensure that wallpaper gets the same gesture.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ window->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}});
+
+ // Touch down on top window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both top window and its wallpaper should receive the touch down
+ window->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the top window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(150)
+ .y(150))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionPointerDown(1 /* pointerIndex */);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+ window->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
+/**
+ * Two windows: a window on the left and window on the right.
+ * A third window, wallpaper, is behind both windows, and spans both top windows.
+ * The first touch down goes to the left window. A second pointer touches down on the right window.
+ * The touch is split, so both left and right windows should receive ACTION_DOWN.
+ * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by
+ * ACTION_POINTER_DOWN(1).
+ */
+TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+ leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ leftWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+ rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL);
+ rightWindow->setHasWallpaper(true);
+
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}});
+
+ // Touch down on left window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 100}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ leftWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Second finger down on the right window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(300)
+ .y(100))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ leftWindow->consumeMotionMove();
+ // Since the touch is split, right window gets ACTION_DOWN
+ rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT,
+ expectedWallpaperFlags);
+
+ // Now, leftWindow, which received the first finger, disappears.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}});
+ leftWindow->consumeMotionCancel();
+ // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // The pointer that's still down on the right window moves, and goes to the right window only.
+ // As far as the dispatcher's concerned though, both pointers are still present.
+ const MotionEvent secondFingerMoveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(100))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(310)
+ .y(110))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT));
+ rightWindow->consumeMotionMove();
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+ wallpaperWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowLeft =
@@ -1745,8 +2087,121 @@
0 /*expectedFlags*/);
}
-using TransferFunction =
- std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>;
+/**
+ * Ensure the correct coordinate spaces are used by InputDispatcher.
+ *
+ * InputDispatcher works in the display space, so its coordinate system is relative to the display
+ * panel. Windows get events in the window space, and get raw coordinates in the logical display
+ * space.
+ */
+class InputDispatcherDisplayProjectionTest : public InputDispatcherTest {
+public:
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mDisplayInfos.clear();
+ mWindowInfos.clear();
+ }
+
+ void addDisplayInfo(int displayId, const ui::Transform& transform) {
+ gui::DisplayInfo info;
+ info.displayId = displayId;
+ info.transform = transform;
+ mDisplayInfos.push_back(std::move(info));
+ mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ }
+
+ void addWindow(const sp<WindowInfoHandle>& windowHandle) {
+ mWindowInfos.push_back(*windowHandle->getInfo());
+ mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos);
+ }
+
+ // Set up a test scenario where the display has a scaled projection and there are two windows
+ // on the display.
+ std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() {
+ // The display has a projection that has a scale factor of 2 and 4 in the x and y directions
+ // respectively.
+ ui::Transform displayTransform;
+ displayTransform.set(2, 0, 0, 4);
+ addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+
+ // Add two windows to the display. Their frames are represented in the display space.
+ sp<FakeWindowHandle> firstWindow =
+ new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT);
+ firstWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform);
+ addWindow(firstWindow);
+
+ sp<FakeWindowHandle> secondWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second Window",
+ ADISPLAY_ID_DEFAULT);
+ secondWindow->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform);
+ addWindow(secondWindow);
+ return {std::move(firstWindow), std::move(secondWindow)};
+ }
+
+private:
+ std::vector<gui::DisplayInfo> mDisplayInfos;
+ std::vector<gui::WindowInfo> mWindowInfos;
+};
+
+TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+ // Send down to the first window. The point is represented in the display space. The point is
+ // selected so that if the hit test was done with the transform applied to it, then it would
+ // end up in the incorrect window.
+ NotifyMotionArgs downMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{75, 55}});
+ mDispatcher->notifyMotion(&downMotionArgs);
+
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+}
+
+// Ensure that when a MotionEvent is injected through the InputDispatcher::injectInputEvent() API,
+// the event should be treated as being in the logical display space.
+TEST_F(InputDispatcherDisplayProjectionTest, InjectionInLogicalDisplaySpace) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+ // Send down to the first window. The point is represented in the logical display space. The
+ // point is selected so that if the hit test was done in logical display space, then it would
+ // end up in the incorrect window.
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ PointF{75 * 2, 55 * 4});
+
+ firstWindow->consumeMotionDown();
+ secondWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinateSpace) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send down to the second window.
+ NotifyMotionArgs downMotionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}});
+ mDispatcher->notifyMotion(&downMotionArgs);
+
+ firstWindow->assertNoEvents();
+ const MotionEvent* event = secondWindow->consumeMotion();
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, event->getAction());
+
+ // Ensure that the events from the "getRaw" API are in logical display coordinates.
+ EXPECT_EQ(300, event->getRawX(0));
+ EXPECT_EQ(880, event->getRawY(0));
+
+ // Ensure that the x and y values are in the window's coordinate space.
+ // The left-top of the second window is at (100, 200) in display space, which is (200, 800) in
+ // the logical display space. This will be the origin of the window space.
+ EXPECT_EQ(100, event->getX(0));
+ EXPECT_EQ(80, event->getY(0));
+}
+
+using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher,
+ sp<IBinder>, sp<IBinder>)>;
class TransferTouchFixture : public InputDispatcherTest,
public ::testing::WithParamInterface<TransferFunction> {};
@@ -1859,12 +2314,12 @@
// for the case where there are multiple pointers split across several windows.
INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
::testing::Values(
- [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/,
- sp<IBinder> destChannelToken) {
+ [&](const std::unique_ptr<InputDispatcher>& dispatcher,
+ sp<IBinder> /*ignored*/, sp<IBinder> destChannelToken) {
return dispatcher->transferTouch(destChannelToken);
},
- [&](sp<InputDispatcher> dispatcher, sp<IBinder> from,
- sp<IBinder> to) {
+ [&](const std::unique_ptr<InputDispatcher>& dispatcher,
+ sp<IBinder> from, sp<IBinder> to) {
return dispatcher->transferTouchFocus(from, to,
false /*isDragAndDrop*/);
}));
@@ -2273,7 +2728,7 @@
class FakeMonitorReceiver {
public:
- FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+ FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
int32_t displayId, bool isGestureMonitor = false) {
base::Result<std::unique_ptr<InputChannel>> channel =
dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID);
@@ -2296,6 +2751,11 @@
expectedDisplayId, expectedFlags);
}
+ void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
+ }
+
void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
expectedDisplayId, expectedFlags);
@@ -2332,6 +2792,57 @@
std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
+/**
+ * Two entities that receive touch: A window, and a global monitor.
+ * The touch goes to the window, and then the window disappears.
+ * The monitor does not get cancel right away. But if more events come in, the touch gets canceled
+ * for the monitor, as well.
+ * 1. foregroundWindow
+ * 2. monitor <-- global monitor (doesn't observe z order, receives all events)
+ */
+TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT,
+ false /*isGestureMonitor*/);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both the foreground window and the global monitor should receive the touch down
+ window->consumeMotionDown();
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionMove();
+ monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+
+ // Now the foreground window goes away
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}});
+ window->consumeMotionCancel();
+ monitor.assertNoEvents(); // Global monitor does not get a cancel yet
+
+ // If more events come in, there will be no more foreground window to send them to. This will
+ // cause a cancel for the monitor, as well.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {120, 200}))
+ << "Injection should fail because the window was removed";
+ window->assertNoEvents();
+ // Global monitor now gets the cancel
+ monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+}
+
// Tests for gesture monitors
TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -2433,6 +2944,17 @@
ASSERT_EQ(ui::Transform(), event->getTransform());
}
+TEST_F(InputDispatcherTest, GestureMonitor_NoWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -2561,7 +3083,6 @@
SCOPED_TRACE("Check default value of touch mode");
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
setFocusedWindow(window);
-
window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
SCOPED_TRACE("Remove the window to trigger focus loss");
@@ -2571,6 +3092,7 @@
SCOPED_TRACE("Disable touch mode");
mDispatcher->setInTouchMode(false);
+ window->consumeTouchModeEvent(false);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
setFocusedWindow(window);
@@ -2583,6 +3105,7 @@
SCOPED_TRACE("Enable touch mode again");
mDispatcher->setInTouchMode(true);
+ window->consumeTouchModeEvent(true);
window->setFocusable(true);
mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
setFocusedWindow(window);
@@ -2622,8 +3145,8 @@
const VerifiedKeyEvent& verifiedKey = static_cast<const VerifiedKeyEvent&>(*verified);
ASSERT_EQ(keyArgs.action, verifiedKey.action);
- ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
+ ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
@@ -2637,7 +3160,14 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ ui::Transform transform;
+ transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = ADISPLAY_ID_DEFAULT;
+ displayInfo.transform = transform;
+
+ mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
@@ -2658,68 +3188,18 @@
const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified);
- EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX);
- EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY);
+ const vec2 rawXY =
+ MotionEvent::calculateTransformedXY(motionArgs.source, transform,
+ motionArgs.pointerCoords[0].getXYValue());
+ EXPECT_EQ(rawXY.x, verifiedMotion.rawX);
+ EXPECT_EQ(rawXY.y, verifiedMotion.rawY);
EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
- EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+ EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- const std::string name = window->getName();
-
- // Window gets transformed by offset values.
- window->setWindowOffset(500.0f, 500.0f);
-
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocusable(true);
-
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-
- // First, we set focused window so that focusedWindowHandle is not null.
- setFocusedWindow(window);
-
- // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
- window->consumeFocusEvent(true);
-
- constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
- for (const auto& [source, action] : nonTransformedSources) {
- const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
- mDispatcher->notifyMotion(&motionArgs);
-
- MotionEvent* event = window->consumeMotion();
- ASSERT_NE(event, nullptr);
-
- const MotionEvent& motionEvent = *event;
- EXPECT_EQ(action, motionEvent.getAction());
- EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
-
- float expectedX = motionArgs.pointerCoords[0].getX();
- float expectedY = motionArgs.pointerCoords[0].getY();
-
- // Ensure the axis values from the final motion event are not transformed.
- EXPECT_EQ(expectedX, motionEvent.getX(0))
- << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
- << motionEvent.getX(0);
- EXPECT_EQ(expectedY, motionEvent.getY(0))
- << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
- << motionEvent.getY(0);
- // Ensure the raw and transformed axis values for the motion event are the same.
- EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
- << "expected raw and transformed X-axis values to be equal";
- EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
- << "expected raw and transformed Y-axis values to be equal";
- }
-}
-
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
@@ -3015,7 +3495,7 @@
virtual void SetUp() override {
mFakePolicy = new FakeInputDispatcherPolicy();
mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
- mDispatcher = new InputDispatcher(mFakePolicy);
+ mDispatcher = std::make_unique<InputDispatcher>(mFakePolicy);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
ASSERT_EQ(OK, mDispatcher->start());
@@ -3318,7 +3798,8 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
+ const ui::Transform& transform = ui::Transform()) {
NotifyMotionArgs motionArgs;
motionArgs =
@@ -3329,7 +3810,8 @@
mDispatcher->notifyMotion(&motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
- mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
+ const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+ mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
}
@@ -3387,6 +3869,30 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
+// logical display coordinate space.
+TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) {
+ ui::Transform firstDisplayTransform;
+ firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});
+
+ std::vector<gui::DisplayInfo> displayInfos(2);
+ displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
+ displayInfos[0].transform = firstDisplayTransform;
+ displayInfos[1].displayId = SECOND_DISPLAY_ID;
+ displayInfos[1].transform = secondDisplayTransform;
+
+ mDispatcher->onWindowInfosChanged({}, displayInfos);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+
+ // Ensure the correct transforms are used for the displays.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+}
+
class InputFilterInjectionPolicyTest : public InputDispatcherTest {
protected:
virtual void SetUp() override {
@@ -3451,8 +3957,7 @@
DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE,
identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
- 0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime,
eventTime,
/*pointerCount*/ 1, pointerProperties, pointerCoords);
@@ -3659,7 +4164,7 @@
<< " event, got " << inputEventTypeToString(event->getType()) << " event";
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- EXPECT_EQ(expectedAction, motionEvent.getAction());
+ assertMotionAction(expectedAction, motionEvent.getAction());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
@@ -3674,7 +4179,7 @@
}
}
- void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+ void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints,
std::vector<PointF> expectedPoints) {
NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, touchedPoints);
@@ -5693,4 +6198,43 @@
window->assertNoEvents();
}
+class InputDispatcherTouchModeChangedTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFocusable(true);
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFocusable(true);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+
+ setFocusedWindow(mWindow);
+ mWindow->consumeFocusEvent(true);
+ }
+
+ void changeAndVerifyTouchMode(bool inTouchMode) {
+ mDispatcher->setInTouchMode(inTouchMode);
+ mWindow->consumeTouchModeEvent(inTouchMode);
+ mSecondWindow->consumeTouchModeEvent(inTouchMode);
+ }
+};
+
+TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) {
+ changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode);
+}
+
+TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) {
+ mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index b419d9a..336afc6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,7 +16,6 @@
#include <CursorInputMapper.h>
#include <InputDevice.h>
-#include <InputFlingerProperties.sysprop.h>
#include <InputMapper.h>
#include <InputReader.h>
#include <InputReaderBase.h>
@@ -205,7 +204,7 @@
std::condition_variable mDevicesChangedCondition;
InputReaderConfiguration mConfig;
- std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers;
+ std::shared_ptr<FakePointerController> mPointerController;
std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
bool mInputDevicesChanged GUARDED_BY(mLock){false};
std::vector<DisplayViewport> mViewports;
@@ -250,14 +249,35 @@
return mConfig.getDisplayViewportByPort(displayPort);
}
+ void addDisplayViewport(DisplayViewport viewport) {
+ mViewports.push_back(std::move(viewport));
+ mConfig.setDisplayViewports(mViewports);
+ }
+
void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- const DisplayViewport viewport =
- createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
- physicalPort, viewportType);
- mViewports.push_back(viewport);
- mConfig.setDisplayViewports(mViewports);
+ std::optional<uint8_t> physicalPort, ViewportType type) {
+ const bool isRotated =
+ (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+
+ addDisplayViewport(v);
}
bool updateViewport(const DisplayViewport& viewport) {
@@ -291,8 +311,8 @@
void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
- void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) {
- mPointerControllers.insert_or_assign(deviceId, std::move(controller));
+ void setPointerController(std::shared_ptr<FakePointerController> controller) {
+ mPointerController = std::move(controller);
}
const InputReaderConfiguration* getReaderConfiguration() const {
@@ -330,38 +350,13 @@
private:
uint32_t mNextPointerCaptureSequenceNumber = 0;
- DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, bool isActive,
- const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type) {
- bool isRotated = (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
- return v;
- }
-
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
*outConfig = mConfig;
}
- std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
- return mPointerControllers[deviceId];
+ std::shared_ptr<PointerControllerInterface> obtainPointerController(
+ int32_t /*deviceId*/) override {
+ return mPointerController;
}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
@@ -873,6 +868,24 @@
return false;
}
+ bool hasKeyCode(int32_t deviceId, int32_t keyCode) const override {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ return false;
+ }
+ for (size_t i = 0; i < device->keysByScanCode.size(); i++) {
+ if (keyCode == device->keysByScanCode.valueAt(i).keyCode) {
+ return true;
+ }
+ }
+ for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
+ if (keyCode == device->keysByUsageCode.valueAt(j).keyCode) {
+ return true;
+ }
+ }
+ return false;
+ }
+
bool hasLed(int32_t deviceId, int32_t led) const override {
Device* device = getDevice(deviceId);
return device && device->leds.indexOfKey(led) >= 0;
@@ -1172,7 +1185,7 @@
public:
InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
- const sp<InputListenerInterface>& listener)
+ InputListenerInterface& listener)
: InputReader(eventHub, policy, listener), mFakeContext(this) {}
virtual ~InstrumentedInputReader() {}
@@ -1485,7 +1498,7 @@
class InputReaderTest : public testing::Test {
protected:
- sp<TestInputListener> mFakeListener;
+ std::unique_ptr<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakeEventHub> mFakeEventHub;
std::unique_ptr<InstrumentedInputReader> mReader;
@@ -1493,14 +1506,14 @@
void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new TestInputListener();
+ mFakeListener = std::make_unique<TestInputListener>();
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- mFakeListener);
+ *mFakeListener);
}
void TearDown() override {
- mFakeListener.clear();
+ mFakeListener.reset();
mFakePolicy.clear();
}
@@ -2131,16 +2144,21 @@
// the tests to fail.
class InputReaderIntegrationTest : public testing::Test {
protected:
- sp<TestInputListener> mTestListener;
+ std::unique_ptr<TestInputListener> mTestListener;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<InputReaderInterface> mReader;
+ std::unique_ptr<InputReaderInterface> mReader;
+
+ std::shared_ptr<FakePointerController> mFakePointerController;
void SetUp() override {
mFakePolicy = new FakeInputReaderPolicy();
- mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
- 30ms /*eventDidNotHappenTimeout*/);
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ mTestListener = std::make_unique<TestInputListener>(2000ms /*eventHappenedTimeout*/,
+ 30ms /*eventDidNotHappenTimeout*/);
- mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
+ 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
@@ -2152,7 +2170,8 @@
void TearDown() override {
ASSERT_EQ(mReader->stop(), OK);
- mTestListener.clear();
+ mReader.reset();
+ mTestListener.reset();
mFakePolicy.clear();
}
};
@@ -2406,16 +2425,16 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<TestInputListener> mFakeListener;
+ std::unique_ptr<TestInputListener> mFakeListener;
std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new TestInputListener();
+ mFakeListener = std::make_unique<TestInputListener>();
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- mFakeListener);
+ *mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
identifier.location = DEVICE_LOCATION;
@@ -2427,7 +2446,7 @@
}
void TearDown() override {
- mFakeListener.clear();
+ mFakeListener.reset();
mFakePolicy.clear();
}
};
@@ -2675,36 +2694,29 @@
static const int32_t DEVICE_CONTROLLER_NUMBER;
static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<TestInputListener> mFakeListener;
+ std::unique_ptr<TestInputListener> mFakeListener;
std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
virtual void SetUp(Flags<InputDeviceClass> classes) {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new TestInputListener();
+ mFakeListener = std::make_unique<TestInputListener>();
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- mFakeListener);
+ *mFakeListener);
mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
}
void SetUp() override {
- // Ensure per_window_input_rotation is enabled.
- sysprop::InputFlingerProperties::per_window_input_rotation(true);
-
SetUp(DEVICE_CLASSES);
}
void TearDown() override {
- mFakeListener.clear();
+ mFakeListener.reset();
mFakePolicy.clear();
-
- sysprop::InputFlingerProperties::per_window_input_rotation(
- INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE);
}
void addConfigurationProperty(const char* key, const char* value) {
@@ -2816,8 +2828,6 @@
const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
Flags<InputDeviceClass>(0); // not needed for current tests
const int32_t InputMapperTest::EVENTHUB_ID = 1;
-const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- sysprop::InputFlingerProperties::per_window_input_rotation();
// --- SwitchInputMapperTest ---
@@ -3732,6 +3742,25 @@
mapper2.getMetaState());
}
+TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+ // Suppose we have two mappers. (DPAD + KEYBOARD)
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_DPAD,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ // Initialize metastate to AMETA_NUM_LOCK_ON.
+ ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+ mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+ mReader->toggleCapsLockState(DEVICE_ID);
+ ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+}
+
// --- KeyboardInputMapperTest_ExternalDevice ---
class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
@@ -3828,7 +3857,7 @@
InputMapperTest::SetUp();
mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
+ mFakePolicy->setPointerController(mFakePointerController);
}
void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
@@ -6254,6 +6283,172 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+// --- TouchDisplayProjectionTest ---
+
+class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
+public:
+ // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
+ // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
+ // rotated equivalent of the given un-rotated physical display bounds.
+ void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ uint32_t inverseRotationFlags;
+ auto width = DISPLAY_WIDTH;
+ auto height = DISPLAY_HEIGHT;
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ inverseRotationFlags = ui::Transform::ROT_270;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_180:
+ inverseRotationFlags = ui::Transform::ROT_180;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ inverseRotationFlags = ui::Transform::ROT_90;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_0:
+ inverseRotationFlags = ui::Transform::ROT_0;
+ break;
+ default:
+ FAIL() << "Invalid orientation: " << orientation;
+ }
+
+ const ui::Transform rotation(inverseRotationFlags, width, height);
+ const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
+
+ std::optional<DisplayViewport> internalViewport =
+ *mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ DisplayViewport& v = *internalViewport;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = 100;
+ v.logicalBottom = 100;
+
+ v.physicalLeft = rotatedPhysicalDisplay.left;
+ v.physicalTop = rotatedPhysicalDisplay.top;
+ v.physicalRight = rotatedPhysicalDisplay.right;
+ v.physicalBottom = rotatedPhysicalDisplay.bottom;
+
+ v.deviceWidth = width;
+ v.deviceHeight = height;
+
+ v.isActive = true;
+ v.uniqueId = UNIQUE_ID;
+ v.type = ViewportType::INTERNAL;
+ mFakePolicy->updateViewport(v);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ void assertReceivedMove(const Point& point) {
+ NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
+ 1, 0, 0, 0, 0, 0, 0, 0));
+ }
+};
+
+TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+ static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
+ {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches outside the physical display should be ignored, and should not generate any
+ // events. Ensure touches at the following points that lie outside of the physical display
+ // area do not generate any events.
+ for (const auto& point : kPointsOutsidePhysicalDisplay) {
+ processDown(mapper, toRawX(point.x), toRawY(point.y));
+ processSync(mapper);
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled())
+ << "Unexpected event generated for touch outside physical display at point: "
+ << point.x << ", " << point.y;
+ }
+ }
+}
+
+TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches that start outside the physical display should be ignored until it enters the
+ // physical display bounds, at which point it should generate a down event. Start a touch at
+ // the point (5, 100), which is outside the physical display bounds.
+ static const Point kOutsidePoint{5, 100};
+ processDown(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Move the touch into the physical display area. This should generate a pointer down.
+ processMove(mapper, toRawX(11), toRawY(21));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Move the touch inside the physical display area. This should generate a pointer move.
+ processMove(mapper, toRawX(69), toRawY(159));
+ processSync(mapper);
+ assertReceivedMove({69, 159});
+
+ // Move outside the physical display area. Since the pointer is already down, this should
+ // now continue generating events.
+ processMove(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ assertReceivedMove(kOutsidePoint);
+
+ // Release. This should generate a pointer up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], kOutsidePoint.x,
+ kOutsidePoint.y, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Ensure no more events were generated.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+}
+
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -7793,7 +7988,7 @@
fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
fakePointerController->setPosition(100, 200);
fakePointerController->setButtonState(0);
- mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerController(fakePointerController);
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
@@ -7946,8 +8141,7 @@
// Setup PointerController.
std::shared_ptr<FakePointerController> fakePointerController =
std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
- mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController);
+ mFakePolicy->setPointerController(fakePointerController);
// Setup policy for associated displays and show touches.
const uint8_t hdmi1 = 0;
@@ -8588,173 +8782,6 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-/**
- * Test touch should not work if outside of surface.
- */
-class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
-protected:
- void halfDisplayToCenterHorizontal(int32_t orientation) {
- std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
-
- // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
- internalViewport->orientation = orientation;
- if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_HEIGHT;
- internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
- internalViewport->physicalLeft = 0;
- internalViewport->physicalTop = DISPLAY_WIDTH / 4;
- internalViewport->physicalRight = DISPLAY_HEIGHT;
- internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
-
- internalViewport->deviceWidth = DISPLAY_HEIGHT;
- internalViewport->deviceHeight = DISPLAY_WIDTH;
- } else {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_WIDTH / 2;
- internalViewport->logicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
- internalViewport->physicalTop = 0;
- internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
- internalViewport->physicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->deviceWidth = DISPLAY_WIDTH;
- internalViewport->deviceHeight = DISPLAY_HEIGHT;
- }
-
- mFakePolicy->updateViewport(internalViewport.value());
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xOutside, int32_t yOutside,
- int32_t xInside, int32_t yInside, int32_t xExpected,
- int32_t yExpected) {
- // touch on outside area should not work.
- processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // touch on inside area should receive the event.
- NotifyMotionArgs args;
- processPosition(mapper, toRawX(xInside), toRawY(yInside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- // Reset.
- mapper.reset(ARBITRARY_TIME);
- }
-};
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Touch on center of normal display should work.
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
- processPosition(mapper, toRawX(x), toRawY(y));
- processSync(mapper);
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 0.0f));
- // Reset.
- mapper.reset(ARBITRARY_TIME);
-
- // Let physical display be different to device, and make surface and physical could be 1:1 in
- // all four orientations.
- for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
- halfDisplayToCenterHorizontal(orientation);
-
- const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
- }
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse x.
- constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
- constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected = y;
- const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- addConfigurationProperty("touch.orientationAware", "0");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- const int32_t x = 0;
- const int32_t y = 0;
-
- const int32_t xExpected = x;
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
- // expect x/y = swap x/y then reverse x.
- const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
- const int32_t yExpected90 = x;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected270 = y;
- const int32_t yExpected270 = DISPLAY_WIDTH - 1;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
-}
-
TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
// we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
std::shared_ptr<FakePointerController> fakePointerController =
@@ -8769,7 +8796,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
mFakePolicy->setPointerCapture(true);
- mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerController(fakePointerController);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
// captured touchpad should be a touchpad source
@@ -8919,7 +8946,7 @@
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerController(fakePointerController);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
// run uncaptured pointer tests - pushes out generic events
// FINGER 0 DOWN
@@ -8959,7 +8986,7 @@
prepareDisplay(DISPLAY_ORIENTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerController(fakePointerController);
mFakePolicy->setPointerCapture(false);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
@@ -8986,23 +9013,23 @@
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- sp<TestInputListener> mFakeListener;
+ std::unique_ptr<TestInputListener> mFakeListener;
std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
virtual void SetUp(Flags<InputDeviceClass> classes) {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
- mFakeListener = new TestInputListener();
+ mFakeListener = std::make_unique<TestInputListener>();
mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- mFakeListener);
+ *mFakeListener);
mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
}
void SetUp() override { SetUp(DEVICE_CLASSES); }
void TearDown() override {
- mFakeListener.clear();
+ mFakeListener.reset();
mFakePolicy.clear();
}
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 0a1dc4b..626cdfc 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -28,12 +28,10 @@
// --- TestInputListener ---
class TestInputListener : public InputListenerInterface {
-protected:
- virtual ~TestInputListener();
-
public:
TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms,
std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
+ virtual ~TestInputListener();
void assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs = nullptr);
diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h
new file mode 100644
index 0000000..c9e089e
--- /dev/null
+++ b/services/sensorservice/ISensorHalWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#ifndef ANDROID_ISENSOR_HAL_WRAPPER_H
+#define ANDROID_ISENSOR_HAL_WRAPPER_H
+
+#include <hardware/sensors.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+/**
+ * A wrapper for various types of HAL implementation, e.g. to distinguish HIDL and AIDL versions.
+ */
+class ISensorHalWrapper {
+public:
+ class ICallback : public ISensorsCallback {
+
+ void onDynamicSensorsConnected(
+ const std::vector<sensor_t> &dynamicSensorsAdded) = 0;
+
+ void onDynamicSensorsDisconnected(
+ const std::vector<int32_t> &dynamicSensorHandlesRemoved) = 0;
+ };
+
+ /**
+ * Connects to the underlying sensors HAL. This should also be used for any reconnections
+ * due to HAL resets.
+ */
+ virtual bool connect(ICallback *callback) = 0;
+
+ /**
+ * Polls for available sensor events. This could be using the traditional sensors
+ * polling or from a FMQ.
+ */
+ virtual ssize_t poll(sensors_event_t* buffer, size_t count) = 0;
+
+ /**
+ * The below functions directly mirrors the sensors HAL definitions.
+ */
+ virtual std::vector<sensor_t> getSensorsList() = 0;
+
+ virtual status_t setOperationMode(SensorService::Mode mode) = 0;
+
+ virtual status_t activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual status_t flush(int32_t sensorHandle) = 0;
+
+ virtual status_t injectSensorData(const sensors_event_t *event) = 0;
+
+ virtual status_t registerDirectChannel(const sensors_direct_mem_t *memory,
+ int32_t *channelHandle) = 0;
+
+ virtual void unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t *config) = 0;
+}
+
+} // namespace android
+
+#endif // ANDROID_ISENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index e4efde2..c67acbf 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -23,31 +23,32 @@
#include <android-base/logging.h>
#include <android/util/ProtoOutputStream.h>
+#include <cutils/atomic.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <sensors/convert.h>
-#include <cutils/atomic.h>
#include <utils/Errors.h>
#include <utils/Singleton.h>
-#include <cstddef>
#include <chrono>
#include <cinttypes>
+#include <cstddef>
#include <thread>
using namespace android::hardware::sensors;
using namespace android::hardware::sensors::V1_0;
using namespace android::hardware::sensors::V1_0::implementation;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
using android::hardware::sensors::V2_0::EventQueueFlagBits;
using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
using android::hardware::sensors::V2_1::ISensorsCallback;
-using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo;
-using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
+using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
+using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo;
+using android::hardware::sensors::V2_1::implementation::convertToSensor;
using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
-using android::hardware::hidl_vec;
-using android::hardware::Return;
using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
using android::util::ProtoOutputStream;
@@ -73,7 +74,7 @@
}
}
-template<typename EnumType>
+template <typename EnumType>
constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
return static_cast<typename std::underlying_type<EnumType>::type>(value);
}
@@ -81,14 +82,13 @@
// Used internally by the framework to wake the Event FMQ. These values must start after
// the last value of EventQueueFlagBits
enum EventQueueFlagBitsInternal : uint32_t {
- INTERNAL_WAKE = 1 << 16,
+ INTERNAL_WAKE = 1 << 16,
};
-} // anonymous namespace
+} // anonymous namespace
void SensorsHalDeathReceivier::serviceDied(
- uint64_t /* cookie */,
- const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
+ uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
ALOGW("Sensors HAL died, attempting to reconnect.");
SensorDevice::getInstance().prepareForReconnect();
}
@@ -98,29 +98,36 @@
using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
Return<void> onDynamicSensorsConnected_2_1(
- const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
- return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
+ const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+ std::vector<sensor_t> sensors;
+ for (const V2_1::SensorInfo& info : dynamicSensorsAdded) {
+ sensor_t sensor;
+ convertToSensor(info, &sensor);
+ sensors.push_back(sensor);
+ }
+
+ SensorDevice::getInstance().onDynamicSensorsConnected(sensors);
+ return Return<void>();
}
Return<void> onDynamicSensorsConnected(
- const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override {
- return SensorDevice::getInstance().onDynamicSensorsConnected(
- convertToNewSensorInfos(dynamicSensorsAdded));
+ const hidl_vec<V1_0::SensorInfo>& dynamicSensorsAdded) override {
+ return onDynamicSensorsConnected_2_1(convertToNewSensorInfos(dynamicSensorsAdded));
}
Return<void> onDynamicSensorsDisconnected(
- const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
- return SensorDevice::getInstance().onDynamicSensorsDisconnected(
- dynamicSensorHandlesRemoved);
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ SensorDevice::getInstance().onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved);
+ return Return<void>();
}
};
SensorDevice::SensorDevice()
- : mHidlTransportErrors(20),
- mRestartWaiter(new HidlServiceRegistrationWaiter()),
- mEventQueueFlag(nullptr),
- mWakeLockQueueFlag(nullptr),
- mReconnecting(false) {
+ : mHidlTransportErrors(20),
+ mRestartWaiter(new HidlServiceRegistrationWaiter()),
+ mEventQueueFlag(nullptr),
+ mWakeLockQueueFlag(nullptr),
+ mReconnecting(false) {
if (!connectHidlService()) {
return;
}
@@ -132,61 +139,59 @@
}
void SensorDevice::initializeSensorList() {
- checkReturn(mSensors->getSensorsList(
- [&](const auto &list) {
- const size_t count = list.size();
+ checkReturn(mSensors->getSensorsList([&](const auto& list) {
+ const size_t count = list.size();
- mActivationCount.setCapacity(count);
- Info model;
- for (size_t i=0 ; i < count; i++) {
- sensor_t sensor;
- convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
+ mActivationCount.setCapacity(count);
+ Info model;
+ for (size_t i = 0; i < count; i++) {
+ sensor_t sensor;
+ convertToSensor(list[i], &sensor);
- if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
- sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
+ if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
+ sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
- // Some sensors don't have a default resolution and will be left at 0.
- // Don't crash in this case since CTS will verify that devices don't go to
- // production with a resolution of 0.
- if (sensor.resolution != 0) {
- float quantizedRange = sensor.maxRange;
- SensorDeviceUtils::quantizeValue(
- &quantizedRange, sensor.resolution, /*factor=*/ 1);
- // Only rewrite maxRange if the requantization produced a "significant"
- // change, which is fairly arbitrarily defined as resolution / 8.
- // Smaller deltas are permitted, as they may simply be due to floating
- // point representation error, etc.
- if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
- ALOGW("%s's max range %.12f is not a multiple of the resolution "
- "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
- sensor.resolution, quantizedRange);
- sensor.maxRange = quantizedRange;
- }
- } else {
- // Don't crash here or the device will go into a crashloop.
- ALOGW("%s should have a non-zero resolution", sensor.name);
- }
+ // Some sensors don't have a default resolution and will be left at 0.
+ // Don't crash in this case since CTS will verify that devices don't go to
+ // production with a resolution of 0.
+ if (sensor.resolution != 0) {
+ float quantizedRange = sensor.maxRange;
+ SensorDeviceUtils::quantizeValue(&quantizedRange, sensor.resolution,
+ /*factor=*/1);
+ // Only rewrite maxRange if the requantization produced a "significant"
+ // change, which is fairly arbitrarily defined as resolution / 8.
+ // Smaller deltas are permitted, as they may simply be due to floating
+ // point representation error, etc.
+ if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+ ALOGW("%s's max range %.12f is not a multiple of the resolution "
+ "%.12f - updated to %.12f",
+ sensor.name, sensor.maxRange, sensor.resolution, quantizedRange);
+ sensor.maxRange = quantizedRange;
}
-
- // Sanity check and clamp power if it is 0 (or close)
- constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
- if (sensor.power < MIN_POWER_MA) {
- ALOGI("%s's reported power %f invalid, clamped to %f",
- sensor.name, sensor.power, MIN_POWER_MA);
- sensor.power = MIN_POWER_MA;
- }
- mSensorList.push_back(sensor);
-
- mActivationCount.add(list[i].sensorHandle, model);
-
- // Only disable all sensors on HAL 1.0 since HAL 2.0
- // handles this in its initialize method
- if (!mSensors->supportsMessageQueues()) {
- checkReturn(mSensors->activate(list[i].sensorHandle,
- 0 /* enabled */));
- }
+ } else {
+ // Don't crash here or the device will go into a crashloop.
+ ALOGW("%s should have a non-zero resolution", sensor.name);
}
- }));
+ }
+
+ // Check and clamp power if it is 0 (or close)
+ constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+ if (sensor.power < MIN_POWER_MA) {
+ ALOGI("%s's reported power %f invalid, clamped to %f", sensor.name, sensor.power,
+ MIN_POWER_MA);
+ sensor.power = MIN_POWER_MA;
+ }
+ mSensorList.push_back(sensor);
+
+ mActivationCount.add(list[i].sensorHandle, model);
+
+ // Only disable all sensors on HAL 1.0 since HAL 2.0
+ // handles this in its initialize method
+ if (!mSensors->supportsMessageQueues()) {
+ checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+ }
+ }
+ }));
}
SensorDevice::~SensorDevice() {
@@ -231,7 +236,7 @@
// Poke ISensor service. If it has lingering connection from previous generation of
// system server, it will kill itself. There is no intention to handle the poll result,
// which will be done since the size is 0.
- if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
+ if (mSensors->poll(0, [](auto, const auto&, const auto&) {}).isOk()) {
// ok to continue
connectionStatus = HalConnectionStatus::CONNECTED;
break;
@@ -278,23 +283,23 @@
SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() {
HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
- mWakeLockQueue = std::make_unique<WakeLockQueue>(
- SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
- true /* configureEventFlagWord */);
+ mWakeLockQueue =
+ std::make_unique<WakeLockQueue>(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
- hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
+ hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(),
+ &mEventQueueFlag);
hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
- hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
- &mWakeLockQueueFlag);
+ hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag);
- CHECK(mSensors != nullptr && mWakeLockQueue != nullptr &&
- mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr);
+ CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
+ mWakeLockQueueFlag != nullptr);
- status_t status = checkReturnAndGetStatus(mSensors->initialize(
- *mWakeLockQueue->getDesc(),
- new SensorsCallback()));
+ mCallback = new SensorsCallback();
+ status_t status =
+ checkReturnAndGetStatus(mSensors->initialize(*mWakeLockQueue->getDesc(), mCallback));
if (status != NO_ERROR) {
connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
@@ -338,8 +343,8 @@
mReconnecting = false;
}
-bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
- const Vector<sensor_t>& newSensorList) {
+bool SensorDevice::sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+ const std::vector<sensor_t>& newSensorList) {
bool didChange = false;
if (oldSensorList.size() != newSensorList.size()) {
@@ -375,19 +380,17 @@
bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) {
bool equivalent = true;
if (prevSensor.handle != newSensor.handle ||
- (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
- (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
- (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
- (prevSensor.version != newSensor.version) ||
- (prevSensor.type != newSensor.type) ||
- (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
- (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
- (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
- (prevSensor.minDelay != newSensor.minDelay) ||
- (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
- (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
- (prevSensor.maxDelay != newSensor.maxDelay) ||
- (prevSensor.flags != newSensor.flags)) {
+ (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
+ (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
+ (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
+ (prevSensor.version != newSensor.version) || (prevSensor.type != newSensor.type) ||
+ (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
+ (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
+ (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
+ (prevSensor.minDelay != newSensor.minDelay) ||
+ (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
+ (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
+ (prevSensor.maxDelay != newSensor.maxDelay) || (prevSensor.flags != newSensor.flags)) {
equivalent = false;
}
return equivalent;
@@ -405,7 +408,7 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& batchParams = info.batchParams[j];
status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */,
- batchParams.mTSample, batchParams.mTBatch);
+ batchParams.mTSample, batchParams.mTBatch);
if (res == NO_ERROR) {
activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */);
@@ -433,7 +436,7 @@
mSensorList.size(), mActivationCount.size(), mDisabledClients.size());
Mutex::Autolock _l(mLock);
- for (const auto & s : mSensorList) {
+ for (const auto& s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
if (info.numActiveClients() == 0) continue;
@@ -444,8 +447,9 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
result.appendFormat("%.1f%s%s", params.mTSample / 1e6f,
- isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
- (j < info.batchParams.size() - 1) ? ", " : "");
+ isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+ : "",
+ (j < info.batchParams.size() - 1) ? ", " : "");
}
result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f);
@@ -453,8 +457,9 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f,
- isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
- (j < info.batchParams.size() - 1) ? ", " : "");
+ isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+ : "",
+ (j < info.batchParams.size() - 1) ? ", " : "");
}
result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
}
@@ -472,29 +477,29 @@
void SensorDevice::dump(ProtoOutputStream* proto) const {
using namespace service::SensorDeviceProto;
if (mSensors == nullptr) {
- proto->write(INITIALIZED , false);
+ proto->write(INITIALIZED, false);
return;
}
- proto->write(INITIALIZED , true);
- proto->write(TOTAL_SENSORS , int(mSensorList.size()));
- proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+ proto->write(INITIALIZED, true);
+ proto->write(TOTAL_SENSORS, int(mSensorList.size()));
+ proto->write(ACTIVE_SENSORS, int(mActivationCount.size()));
Mutex::Autolock _l(mLock);
- for (const auto & s : mSensorList) {
+ for (const auto& s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
if (info.numActiveClients() == 0) continue;
uint64_t token = proto->start(SENSORS);
- proto->write(SensorProto::HANDLE , handle);
- proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+ proto->write(SensorProto::HANDLE, handle);
+ proto->write(SensorProto::ACTIVE_COUNT, int(info.batchParams.size()));
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
- proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
- proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+ proto->write(SensorProto::SAMPLING_PERIOD_MS, params.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_MS, params.mTBatch / 1e6f);
}
- proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
- proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+ proto->write(SensorProto::SAMPLING_PERIOD_SELECTED, info.bestBatchParams.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_SELECTED, info.bestBatchParams.mTBatch / 1e6f);
proto->end(token);
}
}
@@ -531,20 +536,19 @@
do {
auto ret = mSensors->poll(
- count,
- [&](auto result,
- const auto &events,
- const auto &dynamicSensorsAdded) {
+ count, [&](auto result, const auto& events, const auto& dynamicSensorsAdded) {
if (result == Result::OK) {
convertToSensorEventsAndQuantize(convertToNewEvents(events),
- convertToNewSensorInfos(dynamicSensorsAdded), buffer);
+ convertToNewSensorInfos(
+ dynamicSensorsAdded),
+ buffer);
err = (ssize_t)events.size();
} else {
err = statusFromResult(result);
}
});
- if (ret.isOk()) {
+ if (ret.isOk()) {
hidlTransportError = false;
} else {
hidlTransportError = true;
@@ -559,7 +563,7 @@
}
} while (hidlTransportError);
- if(numHidlTransportErrors > 0) {
+ if (numHidlTransportErrors > 0) {
ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
mHidlTransportErrors.add(errLog);
@@ -581,7 +585,8 @@
// events is not available, then read() would return no events, possibly introducing
// additional latency in delivering events to applications.
mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
- asBaseType(INTERNAL_WAKE), &eventFlagState);
+ asBaseType(INTERNAL_WAKE),
+ &eventFlagState);
availableEvents = mSensors->getEventQueue()->availableToRead();
if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
@@ -600,47 +605,39 @@
for (size_t i = 0; i < eventsToRead; i++) {
convertToSensorEvent(mEventBuffer[i], &buffer[i]);
android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i],
- getResolutionForSensor(buffer[i].sensor));
+ getResolutionForSensor(
+ buffer[i].sensor));
}
eventsRead = eventsToRead;
} else {
- ALOGW("Failed to read %zu events, currently %zu events available",
- eventsToRead, availableEvents);
+ ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead,
+ availableEvents);
}
}
return eventsRead;
}
-Return<void> SensorDevice::onDynamicSensorsConnected(
- const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded) {
std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
// Allocate a sensor_t structure for each dynamic sensor added and insert
// it into the dictionary of connected dynamic sensors keyed by handle.
for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
- const SensorInfo &info = dynamicSensorsAdded[i];
+ const sensor_t& sensor = dynamicSensorsAdded[i];
- auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+ auto it = mConnectedDynamicSensors.find(sensor.handle);
CHECK(it == mConnectedDynamicSensors.end());
- sensor_t *sensor = new sensor_t();
- convertToSensor(convertToOldSensorInfo(info), sensor);
-
- mConnectedDynamicSensors.insert(
- std::make_pair(sensor->handle, sensor));
+ mConnectedDynamicSensors.insert(std::make_pair(sensor.handle, sensor));
}
mDynamicSensorsCv.notify_all();
-
- return Return<void>();
}
-Return<void> SensorDevice::onDynamicSensorsDisconnected(
- const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) {
- (void) dynamicSensorHandlesRemoved;
+void SensorDevice::onDynamicSensorsDisconnected(
+ const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) {
// TODO: Currently dynamic sensors do not seem to be removed
- return Return<void>();
}
void SensorDevice::writeWakeLockHandled(uint32_t count) {
@@ -653,7 +650,7 @@
}
}
-void SensorDevice::autoDisable(void *ident, int handle) {
+void SensorDevice::autoDisable(void* ident, int handle) {
Mutex::Autolock _l(mLock);
ssize_t activationIndex = mActivationCount.indexOfKey(handle);
if (activationIndex < 0) {
@@ -687,15 +684,15 @@
Info& info(mActivationCount.editValueAt(activationIndex));
ALOGD_IF(DEBUG_CONNECTIONS,
- "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
- ident, handle, enabled, info.batchParams.size());
+ "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", ident,
+ handle, enabled, info.batchParams.size());
if (enabled) {
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
- ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
- ident, handle);
+ ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", ident,
+ handle);
return NO_ERROR;
}
@@ -714,7 +711,6 @@
// dictionary.
auto it = mConnectedDynamicSensors.find(handle);
if (it != mConnectedDynamicSensors.end()) {
- delete it->second;
mConnectedDynamicSensors.erase(it);
}
@@ -726,11 +722,10 @@
// Call batch for this sensor with the previously calculated best effort
// batch_rate and timeout. One of the apps has unregistered for sensor
// events, and the best effort batch parameters might have changed.
- ALOGD_IF(DEBUG_CONNECTIONS,
- "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle,
- info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
- checkReturn(mSensors->batch(
- handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64,
+ handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
+ checkReturn(mSensors->batch(handle, info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch));
}
} else {
// sensor wasn't enabled for this ident
@@ -767,12 +762,8 @@
return err;
}
-status_t SensorDevice::batch(
- void* ident,
- int handle,
- int flags,
- int64_t samplingPeriodNs,
- int64_t maxBatchReportLatencyNs) {
+status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
if (mSensors == nullptr) return NO_INIT;
if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
@@ -783,7 +774,8 @@
}
ALOGD_IF(DEBUG_CONNECTIONS,
- "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+ "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64
+ " timeout=%" PRId64,
ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
Mutex::Autolock _l(mLock);
@@ -807,25 +799,24 @@
info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
}
- status_t err = updateBatchParamsLocked(handle, info);
+ status_t err = updateBatchParamsLocked(handle, info);
if (err != NO_ERROR) {
- ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
- mSensors.get(), handle, info.bestBatchParams.mTSample,
- info.bestBatchParams.mTBatch, strerror(-err));
+ ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s", mSensors.get(),
+ handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch, strerror(-err));
info.removeBatchParamsForIdent(ident);
}
return err;
}
-status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) {
+status_t SensorDevice::updateBatchParamsLocked(int handle, Info& info) {
BatchParams prevBestBatchParams = info.bestBatchParams;
// Find the minimum of all timeouts and batch_rates for this sensor.
info.selectBatchParams();
ALOGD_IF(DEBUG_CONNECTIONS,
- "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
- " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
+ "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 " curr_timeout=%" PRId64
+ " min_timeout=%" PRId64,
prevBestBatchParams.mTSample, info.bestBatchParams.mTSample,
prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch);
@@ -834,8 +825,8 @@
if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
- err = checkReturnAndGetStatus(mSensors->batch(
- handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+ err = checkReturnAndGetStatus(mSensors->batch(handle, info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch));
}
return err;
@@ -866,8 +857,8 @@
return mDisabledClients.count(ident) > 0;
}
-std::vector<void *> SensorDevice::getDisabledClientsLocked() const {
- std::vector<void *> vec;
+std::vector<void*> SensorDevice::getDisabledClientsLocked() const {
+ std::vector<void*> vec;
for (const auto& it : mDisabledClients) {
vec.push_back(it.first);
}
@@ -896,7 +887,7 @@
addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE);
}
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
int handle = mActivationCount.keyAt(i);
Info& info = mActivationCount.editValueAt(i);
@@ -905,8 +896,7 @@
bool disable = info.numActiveClients() == 0 && info.isActive;
bool enable = info.numActiveClients() > 0 && !info.isActive;
- if ((enable || disable) &&
- doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+ if ((enable || disable) && doActivateHardwareLocked(handle, enable) == NO_ERROR) {
info.isActive = enable;
}
}
@@ -941,22 +931,21 @@
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
- for (void *client : getDisabledClientsLocked()) {
- removeDisabledReasonForIdentLocked(
- client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+ for (void* client : getDisabledClientsLocked()) {
+ removeDisabledReasonForIdentLocked(client,
+ DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
}
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
if (info.batchParams.isEmpty()) continue;
info.selectBatchParams();
const int sensor_handle = mActivationCount.keyAt(i);
ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
- sensor_handle);
- status_t err = checkReturnAndGetStatus(mSensors->batch(
- sensor_handle,
- info.bestBatchParams.mTSample,
- info.bestBatchParams.mTBatch));
+ sensor_handle);
+ status_t err = checkReturnAndGetStatus(mSensors->batch(sensor_handle,
+ info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch));
ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
if (err == NO_ERROR) {
@@ -973,38 +962,36 @@
void SensorDevice::disableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
// Check if this sensor has been activated previously and disable it.
if (info.batchParams.size() > 0) {
- const int sensor_handle = mActivationCount.keyAt(i);
- ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
- sensor_handle);
- checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
+ sensor_handle);
+ checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
- // Add all the connections that were registered for this sensor to the disabled
- // clients list.
- for (size_t j = 0; j < info.batchParams.size(); ++j) {
- addDisabledReasonForIdentLocked(
- info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
- ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
- }
+ // Add all the connections that were registered for this sensor to the disabled
+ // clients list.
+ for (size_t j = 0; j < info.batchParams.size(); ++j) {
+ addDisabledReasonForIdentLocked(info.batchParams.keyAt(j),
+ DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
+ }
- info.isActive = false;
+ info.isActive = false;
}
}
}
-status_t SensorDevice::injectSensorData(
- const sensors_event_t *injected_sensor_event) {
+status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) {
if (mSensors == nullptr) return NO_INIT;
ALOGD_IF(DEBUG_CONNECTIONS,
- "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
- injected_sensor_event->sensor,
- injected_sensor_event->timestamp, injected_sensor_event->data[0],
- injected_sensor_event->data[1], injected_sensor_event->data[2],
- injected_sensor_event->data[3], injected_sensor_event->data[4],
- injected_sensor_event->data[5]);
+ "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+ injected_sensor_event->sensor, injected_sensor_event->timestamp,
+ injected_sensor_event->data[0], injected_sensor_event->data[1],
+ injected_sensor_event->data[2], injected_sensor_event->data[3],
+ injected_sensor_event->data[4], injected_sensor_event->data[5]);
Event ev;
V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev);
@@ -1014,8 +1001,8 @@
status_t SensorDevice::setMode(uint32_t mode) {
if (mSensors == nullptr) return NO_INIT;
- return checkReturnAndGetStatus(mSensors->setOperationMode(
- static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+ return checkReturnAndGetStatus(
+ mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
}
int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
@@ -1041,21 +1028,20 @@
format = SharedMemFormat::SENSORS_EVENT;
SharedMemInfo mem = {
- .type = type,
- .format = format,
- .size = static_cast<uint32_t>(memory->size),
- .memoryHandle = memory->handle,
+ .type = type,
+ .format = format,
+ .size = static_cast<uint32_t>(memory->size),
+ .memoryHandle = memory->handle,
};
int32_t ret;
- checkReturn(mSensors->registerDirectChannel(mem,
- [&ret](auto result, auto channelHandle) {
- if (result == Result::OK) {
- ret = channelHandle;
- } else {
- ret = statusFromResult(result);
- }
- }));
+ checkReturn(mSensors->registerDirectChannel(mem, [&ret](auto result, auto channelHandle) {
+ if (result == Result::OK) {
+ ret = channelHandle;
+ } else {
+ ret = statusFromResult(result);
+ }
+ }));
return ret;
}
@@ -1065,13 +1051,13 @@
checkReturn(mSensors->unregisterDirectChannel(channelHandle));
}
-int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
- int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config) {
if (mSensors == nullptr) return NO_INIT;
Mutex::Autolock _l(mLock);
RateLevel rate;
- switch(config->rate_level) {
+ switch (config->rate_level) {
case SENSOR_DIRECT_RATE_STOP:
rate = RateLevel::STOP;
break;
@@ -1090,17 +1076,17 @@
int32_t ret;
checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
- [&ret, rate] (auto result, auto token) {
- if (rate == RateLevel::STOP) {
- ret = statusFromResult(result);
- } else {
- if (result == Result::OK) {
- ret = token;
- } else {
- ret = statusFromResult(result);
- }
- }
- }));
+ [&ret, rate](auto result, auto token) {
+ if (rate == RateLevel::STOP) {
+ ret = statusFromResult(result);
+ } else {
+ if (result == Result::OK) {
+ ret = token;
+ } else {
+ ret = statusFromResult(result);
+ }
+ }
+ }));
return ret;
}
@@ -1118,13 +1104,12 @@
return num;
}
-status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int,
- int64_t samplingPeriodNs,
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs) {
ssize_t index = batchParams.indexOfKey(ident);
if (index < 0) {
- ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64
- " timeout=%" PRId64 ") failed (%s)",
+ ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64
+ ") failed (%s)",
ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
return BAD_INDEX;
}
@@ -1168,12 +1153,11 @@
return mIsDirectReportSupported;
}
-void SensorDevice::convertToSensorEvent(
- const Event &src, sensors_event_t *dst) {
+void SensorDevice::convertToSensorEvent(const Event& src, sensors_event_t* dst) {
V2_1::implementation::convertToSensorEvent(src, dst);
if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) {
- const DynamicSensorInfo &dyn = src.u.dynamic;
+ const DynamicSensorInfo& dyn = src.u.dynamic;
dst->dynamic_sensor_meta.connected = dyn.connected;
dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
@@ -1184,56 +1168,53 @@
// marks it as oneway.
auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
if (it == mConnectedDynamicSensors.end()) {
- mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT,
- [&, dyn]{
- return mConnectedDynamicSensors.find(dyn.sensorHandle)
- != mConnectedDynamicSensors.end();
+ mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, [&, dyn] {
+ return mConnectedDynamicSensors.find(dyn.sensorHandle) !=
+ mConnectedDynamicSensors.end();
});
it = mConnectedDynamicSensors.find(dyn.sensorHandle);
CHECK(it != mConnectedDynamicSensors.end());
}
- dst->dynamic_sensor_meta.sensor = it->second;
+ dst->dynamic_sensor_meta.sensor = &it->second;
- memcpy(dst->dynamic_sensor_meta.uuid,
- dyn.uuid.data(),
+ memcpy(dst->dynamic_sensor_meta.uuid, dyn.uuid.data(),
sizeof(dst->dynamic_sensor_meta.uuid));
}
}
}
-void SensorDevice::convertToSensorEventsAndQuantize(
- const hidl_vec<Event> &src,
- const hidl_vec<SensorInfo> &dynamicSensorsAdded,
- sensors_event_t *dst) {
-
- if (dynamicSensorsAdded.size() > 0) {
- onDynamicSensorsConnected(dynamicSensorsAdded);
+void SensorDevice::convertToSensorEventsAndQuantize(const hidl_vec<Event>& src,
+ const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ sensors_event_t* dst) {
+ if (dynamicSensorsAdded.size() > 0 && mCallback != nullptr) {
+ mCallback->onDynamicSensorsConnected_2_1(dynamicSensorsAdded);
}
for (size_t i = 0; i < src.size(); ++i) {
V2_1::implementation::convertToSensorEvent(src[i], &dst[i]);
android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i],
- getResolutionForSensor(dst[i].sensor));
+ getResolutionForSensor(
+ dst[i].sensor));
}
}
float SensorDevice::getResolutionForSensor(int sensorHandle) {
for (size_t i = 0; i < mSensorList.size(); i++) {
- if (sensorHandle == mSensorList[i].handle) {
- return mSensorList[i].resolution;
- }
+ if (sensorHandle == mSensorList[i].handle) {
+ return mSensorList[i].resolution;
+ }
}
auto it = mConnectedDynamicSensors.find(sensorHandle);
if (it != mConnectedDynamicSensors.end()) {
- return it->second->resolution;
+ return it->second.resolution;
}
return 0;
}
-void SensorDevice::handleHidlDeath(const std::string & detail) {
+void SensorDevice::handleHidlDeath(const std::string& detail) {
if (!mSensors->supportsMessageQueues()) {
// restart is the only option at present.
LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index bc8d20f..314ddb8 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -17,14 +17,14 @@
#ifndef ANDROID_SENSOR_DEVICE_H
#define ANDROID_SENSOR_DEVICE_H
+#include "ISensorsWrapper.h"
#include "SensorDeviceUtils.h"
#include "SensorService.h"
#include "SensorServiceUtils.h"
-#include "ISensorsWrapper.h"
#include <fmq/MessageQueue.h>
-#include <sensor/SensorEventQueue.h>
#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
#include <stdint.h>
#include <sys/types.h>
#include <utils/KeyedVector.h>
@@ -32,9 +32,10 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <algorithm> //std::max std::min
#include <string>
#include <unordered_map>
-#include <algorithm> //std::max std::min
+#include <vector>
#include "RingBuffer.h"
@@ -42,18 +43,18 @@
namespace android {
+using Result = ::android::hardware::sensors::V1_0::Result;
+
// ---------------------------------------------------------------------------
class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient {
virtual void serviceDied(uint64_t cookie,
const wp<::android::hidl::base::V1_0::IBase>& service) override;
};
-class SensorDevice : public Singleton<SensorDevice>,
- public SensorServiceUtil::Dumpable {
+class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable {
public:
class HidlTransportErrorLog {
- public:
-
+ public:
HidlTransportErrorLog() {
mTs = 0;
mCount = 0;
@@ -66,7 +67,7 @@
String8 toString() const {
String8 result;
- struct tm *timeInfo = localtime(&mTs);
+ struct tm* timeInfo = localtime(&mTs);
result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
timeInfo->tm_sec, mCount);
return result;
@@ -74,7 +75,7 @@
private:
time_t mTs; // timestamp of the error
- int mCount; // number of transport errors observed
+ int mCount; // number of transport errors observed
};
~SensorDevice();
@@ -99,29 +100,24 @@
status_t setMode(uint32_t mode);
bool isDirectReportSupported() const;
- int32_t registerDirectChannel(const sensors_direct_mem_t *memory);
+ int32_t registerDirectChannel(const sensors_direct_mem_t* memory);
void unregisterDirectChannel(int32_t channelHandle);
- int32_t configureDirectChannel(int32_t sensorHandle,
- int32_t channelHandle, const struct sensors_direct_cfg_t *config);
+ int32_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config);
void disableAllSensors();
void enableAllSensors();
- void autoDisable(void *ident, int handle);
+ void autoDisable(void* ident, int handle);
- status_t injectSensorData(const sensors_event_t *event);
- void notifyConnectionDestroyed(void *ident);
+ status_t injectSensorData(const sensors_event_t* event);
+ void notifyConnectionDestroyed(void* ident);
- using Result = ::android::hardware::sensors::V1_0::Result;
- hardware::Return<void> onDynamicSensorsConnected(
- const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded);
- hardware::Return<void> onDynamicSensorsDisconnected(
- const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
+ void onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded);
+ void onDynamicSensorsDisconnected(const std::vector<int32_t>& dynamicSensorHandlesRemoved);
void setUidStateForConnection(void* ident, SensorService::UidState state);
- bool isReconnecting() const {
- return mReconnecting;
- }
+ bool isReconnecting() const { return mReconnecting; }
bool isSensorActive(int handle) const;
@@ -132,12 +128,14 @@
// Dumpable
virtual std::string dump() const override;
virtual void dump(util::ProtoOutputStream* proto) const override;
+
private:
friend class Singleton<SensorDevice>;
sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
- Vector<sensor_t> mSensorList;
- std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
+ sp<::android::hardware::sensors::V2_1::ISensorsCallback> mCallback;
+ std::vector<sensor_t> mSensorList;
+ std::unordered_map<int32_t, sensor_t> mConnectedDynamicSensors;
// A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic
// meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are
@@ -147,26 +145,26 @@
std::condition_variable mDynamicSensorsCv;
static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5};
- static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
- mutable Mutex mLock; // protect mActivationCount[].batchParams
+ static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
+ mutable Mutex mLock; // protect mActivationCount[].batchParams
// fixed-size array after construction
// Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from
// batch call. For continous mode clients, maxBatchReportLatency is set to zero.
struct BatchParams {
- nsecs_t mTSample, mTBatch;
- BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
- BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {}
- bool operator != (const BatchParams& other) {
- return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
- }
- // Merge another parameter with this one. The updated mTSample will be the min of the two.
- // The update mTBatch will be the min of original mTBatch and the apparent batch period
- // of the other. the apparent batch is the maximum of mTBatch and mTSample,
- void merge(const BatchParams &other) {
- mTSample = std::min(mTSample, other.mTSample);
- mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
- }
+ nsecs_t mTSample, mTBatch;
+ BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
+ BatchParams(nsecs_t tSample, nsecs_t tBatch) : mTSample(tSample), mTBatch(tBatch) {}
+ bool operator!=(const BatchParams& other) {
+ return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
+ }
+ // Merge another parameter with this one. The updated mTSample will be the min of the two.
+ // The update mTBatch will be the min of original mTBatch and the apparent batch period
+ // of the other. the apparent batch is the maximum of mTBatch and mTSample,
+ void merge(const BatchParams& other) {
+ mTSample = std::min(mTSample, other.mTSample);
+ mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
+ }
};
// Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in
@@ -224,7 +222,7 @@
static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT);
// Use this map to determine which client is activated or deactivated.
- std::unordered_map<void *, uint8_t> mDisabledClients;
+ std::unordered_map<void*, uint8_t> mDisabledClients;
void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
@@ -233,13 +231,13 @@
bool connectHidlService();
void initializeSensorList();
void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations);
- static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
- const Vector<sensor_t>& newSensorList);
+ static bool sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+ const std::vector<sensor_t>& newSensorList);
static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor);
enum HalConnectionStatus {
- CONNECTED, // Successfully connected to the HAL
- DOES_NOT_EXIST, // Could not find the HAL
+ CONNECTED, // Successfully connected to the HAL
+ DOES_NOT_EXIST, // Could not find the HAL
FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
UNKNOWN,
};
@@ -257,32 +255,32 @@
status_t updateBatchParamsLocked(int handle, Info& info);
status_t doActivateHardwareLocked(int handle, bool enable);
- void handleHidlDeath(const std::string &detail);
- template<typename T>
+ void handleHidlDeath(const std::string& detail);
+ template <typename T>
void checkReturn(const Return<T>& ret) {
if (!ret.isOk()) {
handleHidlDeath(ret.description());
}
}
+
status_t checkReturnAndGetStatus(const Return<Result>& ret);
- //TODO(b/67425500): remove waiter after bug is resolved.
+ // TODO(b/67425500): remove waiter after bug is resolved.
sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
bool isClientDisabled(void* ident) const;
bool isClientDisabledLocked(void* ident) const;
- std::vector<void *> getDisabledClientsLocked() const;
+ std::vector<void*> getDisabledClientsLocked() const;
bool clientHasNoAccessLocked(void* ident) const;
using Event = hardware::sensors::V2_1::Event;
using SensorInfo = hardware::sensors::V2_1::SensorInfo;
- void convertToSensorEvent(const Event &src, sensors_event_t *dst);
+ void convertToSensorEvent(const Event& src, sensors_event_t* dst);
- void convertToSensorEventsAndQuantize(
- const hardware::hidl_vec<Event> &src,
- const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
- sensors_event_t *dst);
+ void convertToSensorEventsAndQuantize(const hardware::hidl_vec<Event>& src,
+ const hardware::hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ sensors_event_t* dst);
float getResolutionForSensor(int sensorHandle);
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index af86d09..fae16f6 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -32,7 +32,6 @@
: mService(service), mUid(uid), mMem(*mem),
mHalChannelHandle(halChannelHandle),
mOpPackageName(opPackageName), mDestroyed(false) {
- mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -197,8 +196,8 @@
if (mService->isSensorInCappedSet(s.getType())) {
// Back up the rates that the app is allowed to have if the mic toggle is off
// This is used in the uncapRates() function.
- if (!mIsRateCappedBasedOnPermission ||
- requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
+ if ((requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) ||
+ !isRateCappedBasedOnPermission()) {
mMicRateBackup[handle] = requestedRateLevel;
} else {
mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index a3f348b..d39a073 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
+#include <optional>
#include <stdint.h>
#include <sys/types.h>
@@ -100,10 +101,19 @@
std::unordered_map<int, int> mActivatedBackup;
std::unordered_map<int, int> mMicRateBackup;
- std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mDestroyLock;
bool mDestroyed;
userid_t mUserId;
+
+ std::optional<bool> mIsRateCappedBasedOnPermission;
+
+ bool isRateCappedBasedOnPermission() {
+ if (!mIsRateCappedBasedOnPermission.has_value()) {
+ mIsRateCappedBasedOnPermission =
+ mService->isRateCappedBasedOnPermission(mOpPackageName);
+ }
+ return mIsRateCappedBasedOnPermission.value();
+ }
};
} // namepsace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 9ce8d9b..6948895 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -44,7 +44,6 @@
mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag),
mTargetSdk(kTargetSdkUnknown), mDestroyed(false) {
- mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName);
mUserId = multiuser_get_user_id(mUid);
mChannel = new BitTube(mService->mSocketBufferSize);
#if DEBUG_CONNECTIONS
@@ -706,8 +705,8 @@
err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,
reservedFlags, mOpPackageName);
if (err == OK && isSensorCapped) {
- if (!mIsRateCappedBasedOnPermission ||
- requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+ !isRateCappedBasedOnPermission()) {
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
@@ -745,8 +744,8 @@
}
status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName);
if (ret == OK && isSensorCapped) {
- if (!mIsRateCappedBasedOnPermission ||
- requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
+ if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
+ !isRateCappedBasedOnPermission()) {
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
@@ -867,7 +866,7 @@
} else if (numBytesRead == sizeof(uint32_t)) {
uint32_t numAcks = 0;
memcpy(&numAcks, buf, numBytesRead);
- // Sanity check to ensure there are no read errors in recv, numAcks is always
+ // Check to ensure there are no read errors in recv, numAcks is always
// within the range and not zero. If any of the above don't hold reset
// mWakeLockRefCount to zero.
if (numAcks > 0 && numAcks < mWakeLockRefCount) {
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 909053b..6a98a40 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -18,6 +18,7 @@
#define ANDROID_SENSOR_EVENT_CONNECTION_H
#include <atomic>
+#include <optional>
#include <stdint.h>
#include <sys/types.h>
#include <unordered_map>
@@ -148,7 +149,6 @@
sp<SensorService> const mService;
sp<BitTube> mChannel;
uid_t mUid;
- std::atomic_bool mIsRateCappedBasedOnPermission;
mutable Mutex mConnectionLock;
// Number of events from wake up sensors which are still pending and haven't been delivered to
// the corresponding application. It is incremented by one unit for each write to the socket.
@@ -201,6 +201,16 @@
// Mapping of sensor handles to its rate before being capped by the mic toggle.
std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
userid_t mUserId;
+
+ std::optional<bool> mIsRateCappedBasedOnPermission;
+
+ bool isRateCappedBasedOnPermission() {
+ if (!mIsRateCappedBasedOnPermission.has_value()) {
+ mIsRateCappedBasedOnPermission
+ = mService->isRateCappedBasedOnPermission(mOpPackageName);
+ }
+ return mIsRateCappedBasedOnPermission.value();
+ }
};
} // namepsace android
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index bdbae7b..9bc7b8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -164,7 +164,6 @@
sensor_t const* list;
ssize_t count = dev.getSensorList(&list);
if (count > 0) {
- ssize_t orientationIndex = -1;
bool hasGyro = false, hasAccel = false, hasMag = false;
uint32_t virtualSensorsNeeds =
(1<<SENSOR_TYPE_GRAVITY) |
@@ -183,9 +182,6 @@
case SENSOR_TYPE_MAGNETIC_FIELD:
hasMag = true;
break;
- case SENSOR_TYPE_ORIENTATION:
- orientationIndex = i;
- break;
case SENSOR_TYPE_GYROSCOPE:
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
hasGyro = true;
@@ -201,6 +197,8 @@
virtualSensorsNeeds &= ~(1<<list[i].type);
}
break;
+ default:
+ break;
}
if (useThisSensor) {
if (list[i].type == SENSOR_TYPE_PROXIMITY) {
@@ -1194,7 +1192,7 @@
// We have a dynamic sensor.
if (!sHmacGlobalKeyIsValid) {
- // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+ // Rather than risk exposing UUIDs, we slow down dynamic sensors.
ALOGW("HMAC key failure; dynamic sensor getId() will be wrong.");
return 0;
}
@@ -1220,7 +1218,7 @@
sHmacGlobalKey, sizeof(sHmacGlobalKey),
uuidAndApp, sizeof(uuidAndApp),
hash, &hashLen) == nullptr) {
- // Rather than risk exposing UUIDs, we cripple dynamic sensors.
+ // Rather than risk exposing UUIDs, we slow down dynamic sensors.
ALOGW("HMAC failure; dynamic sensor getId() will be wrong.");
return 0;
}
@@ -2182,10 +2180,10 @@
status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs,
const String16& opPackageName) {
uid_t uid = IPCThreadState::self()->getCallingUid();
- bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) {
return OK;
}
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (shouldCapBasedOnPermission) {
*requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
if (isPackageDebuggable(opPackageName)) {
@@ -2203,11 +2201,10 @@
status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel,
const String16& opPackageName) {
uid_t uid = IPCThreadState::self()->getCallingUid();
- bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
-
if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) {
return OK;
}
+ bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName);
if (shouldCapBasedOnPermission) {
*requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL;
if (isPackageDebuggable(opPackageName)) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b059e61..9b6d01a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -89,6 +89,52 @@
UID_STATE_IDLE,
};
+ enum Mode {
+ // The regular operating mode where any application can register/unregister/call flush on
+ // sensors.
+ NORMAL = 0,
+ // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
+ // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
+ // injected from the SensorService as if it were the real sensor data. This mode is primarily
+ // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
+ // Step Detector etc. Typically in this mode, there will be a client (a
+ // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
+ // unregister and register for any sensor that supports injection. Registering to sensors
+ // that do not support injection will give an error. TODO: Allow exactly one
+ // client to inject sensor data at a time.
+ DATA_INJECTION = 1,
+ // This mode is used only for testing sensors. Each sensor can be tested in isolation with
+ // the required sampling_rate and maxReportLatency parameters without having to think about
+ // the data rates requested by other applications. End user devices are always expected to be
+ // in NORMAL mode. When this mode is first activated, all active sensors from all connections
+ // are disabled. Calling flush() will return an error. In this mode, only the requests from
+ // selected apps whose package names are allowlisted are allowed (typically CTS apps). Only
+ // these apps can register/unregister/call flush() on sensors. If SensorService switches to
+ // NORMAL mode again, all sensors that were previously registered to are activated with the
+ // corresponding parameters if the application hasn't unregistered for sensors in the mean
+ // time. NOTE: Non allowlisted app whose sensors were previously deactivated may still
+ // receive events if a allowlisted app requests data from the same sensor.
+ RESTRICTED = 2
+
+ // State Transitions supported.
+ // RESTRICTED <--- NORMAL ---> DATA_INJECTION
+ // ---> <---
+
+ // Shell commands to switch modes in SensorService.
+ // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
+ // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
+ //
+ // $ adb shell dumpsys sensorservice restrict .cts.
+ //
+ // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
+ // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
+ //
+ // $ adb shell dumpsys sensorservice data_injection .xts.
+ //
+ // 3) Reset sensorservice back to NORMAL mode.
+ // $ adb shell dumpsys sensorservice enable
+ };
+
class ProximityActiveListener : public virtual RefBase {
public:
// Note that the callback is invoked from an async thread and can interact with the
@@ -276,52 +322,6 @@
const int64_t mToken;
};
- enum Mode {
- // The regular operating mode where any application can register/unregister/call flush on
- // sensors.
- NORMAL = 0,
- // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
- // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
- // injected from the SensorService as if it were the real sensor data. This mode is primarily
- // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
- // Step Detector etc. Typically in this mode, there will be a client (a
- // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
- // unregister and register for any sensor that supports injection. Registering to sensors
- // that do not support injection will give an error. TODO(aakella) : Allow exactly one
- // client to inject sensor data at a time.
- DATA_INJECTION = 1,
- // This mode is used only for testing sensors. Each sensor can be tested in isolation with
- // the required sampling_rate and maxReportLatency parameters without having to think about
- // the data rates requested by other applications. End user devices are always expected to be
- // in NORMAL mode. When this mode is first activated, all active sensors from all connections
- // are disabled. Calling flush() will return an error. In this mode, only the requests from
- // selected apps whose package names are whitelisted are allowed (typically CTS apps). Only
- // these apps can register/unregister/call flush() on sensors. If SensorService switches to
- // NORMAL mode again, all sensors that were previously registered to are activated with the
- // corresponding paramaters if the application hasn't unregistered for sensors in the mean
- // time. NOTE: Non whitelisted app whose sensors were previously deactivated may still
- // receive events if a whitelisted app requests data from the same sensor.
- RESTRICTED = 2
-
- // State Transitions supported.
- // RESTRICTED <--- NORMAL ---> DATA_INJECTION
- // ---> <---
-
- // Shell commands to switch modes in SensorService.
- // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
- // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
- //
- // $ adb shell dumpsys sensorservice restrict .cts.
- //
- // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
- // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
- //
- // $ adb shell dumpsys sensorservice data_injection .xts.
- //
- // 3) Reset sensorservice back to NORMAL mode.
- // $ adb shell dumpsys sensorservice enable
- };
-
static const char* WAKE_LOCK_NAME;
virtual ~SensorService();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index eeb3f3a..5560ed7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -39,15 +39,19 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.common@1.2",
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-cpp",
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"libEGL",
"libfmq",
@@ -66,13 +70,18 @@
"libinput",
"libutils",
"libSurfaceFlingerProp",
+ "server_configurable_flags",
],
static_libs: [
+ "libaidlcommonsupport",
"libcompositionengine",
"libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
+ "libscheduler",
"libserviceutils",
+ "libshaders",
+ "libtonemap",
"libtrace_proto",
],
header_libs: [
@@ -80,6 +89,7 @@
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
+ "android.hardware.graphics.composer3-command-buffer",
],
export_static_lib_headers: [
"libcompositionengine",
@@ -94,6 +104,7 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.3",
"libhidlbase",
"libtimestats",
@@ -101,7 +112,7 @@
// TODO (marissaw): this library is not used by surfaceflinger. This is here so
// the library compiled in a way that is accessible to system partition when running
// IMapper's VTS.
- required: ["libgralloctypes"]
+ required: ["libgralloctypes"],
}
cc_defaults {
@@ -134,6 +145,7 @@
filegroup {
name: "libsurfaceflinger_sources",
srcs: [
+ "BackgroundExecutor.cpp",
"BufferLayer.cpp",
"BufferLayerConsumer.cpp",
"BufferQueueLayer.cpp",
@@ -143,6 +155,8 @@
"EffectLayer.cpp",
"ContainerLayer.cpp",
"DisplayDevice.cpp",
+ "DisplayHardware/AidlComposerHal.cpp",
+ "DisplayHardware/HidlComposerHal.cpp",
"DisplayHardware/ComposerHal.cpp",
"DisplayHardware/DisplayIdentification.cpp",
"DisplayHardware/FramebufferSurface.cpp",
@@ -154,6 +168,7 @@
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
"EventLog/EventLog.cpp",
+ "FlagManager.cpp",
"FpsReporter.cpp",
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
@@ -181,14 +196,17 @@
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
"Scheduler/VsyncConfiguration.cpp",
+ "Scheduler/VsyncModulator.cpp",
+ "Scheduler/VsyncSchedule.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
"SurfaceInterceptor.cpp",
- "SurfaceTracing.cpp",
+ "Tracing/LayerTracing.cpp",
+ "Tracing/TransactionTracing.cpp",
+ "Tracing/TransactionProtoParser.cpp",
"TransactionCallbackInvoker.cpp",
"TunnelModeEnabledReporter.cpp",
],
@@ -247,7 +265,7 @@
"libSurfaceFlingerProp",
],
- logtags: ["EventLog/EventLogTags.logtags"],
+ logtags: ["EventLog/EventLogTags.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
new file mode 100644
index 0000000..de8e6b3
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BackgroundExecutor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BackgroundExecutor.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor);
+
+BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
+ mThread = std::thread([&]() {
+ bool done = false;
+ while (!done) {
+ std::vector<std::function<void()>> tasks;
+ {
+ std::unique_lock lock(mMutex);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ mWorkAvailableCv.wait(lock,
+ [&]() REQUIRES(mMutex) { return mDone || !mTasks.empty(); });
+ tasks = std::move(mTasks);
+ mTasks.clear();
+ done = mDone;
+ } // unlock mMutex
+
+ for (auto& task : tasks) {
+ task();
+ }
+ }
+ });
+}
+
+BackgroundExecutor::~BackgroundExecutor() {
+ {
+ std::scoped_lock lock(mMutex);
+ mDone = true;
+ mWorkAvailableCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void BackgroundExecutor::execute(std::function<void()> task) {
+ std::scoped_lock lock(mMutex);
+ mTasks.emplace_back(std::move(task));
+ mWorkAvailableCv.notify_all();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
new file mode 100644
index 0000000..6db7dda
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <utils/Singleton.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+// Executes tasks off the main thread.
+class BackgroundExecutor : public Singleton<BackgroundExecutor> {
+public:
+ BackgroundExecutor();
+ ~BackgroundExecutor();
+ void execute(std::function<void()>);
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mWorkAvailableCv GUARDED_BY(mMutex);
+ bool mDone GUARDED_BY(mMutex) = false;
+ std::vector<std::function<void()>> mTasks GUARDED_BY(mMutex);
+ std::thread mThread;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 8c97f12..e26c763 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -111,6 +111,10 @@
return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat());
}
+bool BufferLayer::canReceiveInput() const {
+ return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
+}
+
bool BufferLayer::isVisible() const {
return !isHiddenByPolicy() && getAlpha() > 0.0f &&
(mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
@@ -152,39 +156,10 @@
return result;
}
- if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) {
- // the texture has not been created yet, this Layer has
- // in fact never been drawn into. This happens frequently with
- // SurfaceView because the WindowManager can't know when the client
- // has drawn the first time.
-
- // If there is nothing under us, we paint the screen in black, otherwise
- // we just skip this update.
-
- // figure out if there is something below us
- Region under;
- bool finished = false;
- mFlinger->mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (finished || layer == static_cast<BufferLayer const*>(this)) {
- finished = true;
- return;
- }
-
- under.orSelf(layer->getScreenBounds());
- });
- // if not everything below us is covered, we plug the holes!
- Region holes(targetSettings.clip.subtract(under));
- if (!holes.isEmpty()) {
- targetSettings.clearRegion.orSelf(holes);
- }
-
- if (mSidebandStream != nullptr) {
- // For surfaceview of tv sideband, there is no activeBuffer
- // in bufferqueue, we need return LayerSettings.
- return result;
- } else {
- return std::nullopt;
- }
+ if (CC_UNLIKELY(mBufferInfo.mBuffer == 0) && mSidebandStream != nullptr) {
+ // For surfaceview of tv sideband, there is no activeBuffer
+ // in bufferqueue, we need return LayerSettings.
+ return result;
}
const bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
((isSecure() || isProtected()) && !targetSettings.isSecure);
@@ -316,15 +291,16 @@
// Sideband layers
auto* compositionState = editCompositionState();
- if (compositionState->sidebandStream.get()) {
- compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
return;
} else {
// Normal buffer layers
compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
compositionState->compositionType = mPotentialCursor
- ? Hwc2::IComposerClient::Composition::CURSOR
- : Hwc2::IComposerClient::Composition::DEVICE;
+ ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+ : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
}
compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
@@ -332,6 +308,7 @@
? 0
: mBufferInfo.mBufferSlot;
compositionState->acquireFence = mBufferInfo.mFence;
+ compositionState->sidebandStreamHasFrame = false;
}
bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
@@ -374,13 +351,13 @@
}
} // namespace
-bool BufferLayer::onPostComposition(const DisplayDevice* display,
+void BufferLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming& compositorTiming) {
// mFrameLatencyNeeded is true when a new frame was latched for the
// composition.
- if (!mBufferInfo.mFrameLatencyNeeded) return false;
+ if (!mBufferInfo.mFrameLatencyNeeded) return;
// Update mFrameEventHistory.
{
@@ -425,12 +402,13 @@
const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+ const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+ const auto gameMode = getGameMode();
+
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
- refreshRate, renderRate,
- frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate),
- getGameMode());
+ refreshRate, renderRate, vote, gameMode);
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -441,10 +419,7 @@
// timestamp instead.
const nsecs_t actualPresentTime = display->getRefreshTimestamp();
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
- refreshRate, renderRate,
- frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate),
- getGameMode());
+ refreshRate, renderRate, vote, gameMode);
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
mCurrentFrameNumber, actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -454,7 +429,6 @@
mFrameTracker.advanceFrame();
mBufferInfo.mFrameLatencyNeeded = false;
- return true;
}
void BufferLayer::gatherBufferInfo() {
@@ -514,7 +488,7 @@
// try again later
if (!fenceHasSignaled()) {
ATRACE_NAME("!fenceHasSignaled()");
- mFlinger->signalLayerUpdate();
+ mFlinger->onLayerUpdate();
return false;
}
@@ -591,15 +565,16 @@
// hardware.h, instead of using hard-coded values here.
#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-bool BufferLayer::getOpacityForFormat(uint32_t format) {
+bool BufferLayer::getOpacityForFormat(PixelFormat format) {
if (HARDWARE_IS_DEVICE_FORMAT(format)) {
return true;
}
switch (format) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_RGBA_FP16:
- case HAL_PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_R_8:
return false;
}
// in all other case, we have no blending (also for unknown formats)
@@ -648,14 +623,6 @@
return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
}
-uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const {
- if (hasFrameUpdate()) {
- return getFrameNumber(expectedPresentTime);
- } else {
- return mCurrentFrameNumber;
- }
-}
-
Rect BufferLayer::getBufferSize(const State& s) const {
// If we have a sideband stream, or we are scaling the buffer then return the layer size since
// we cannot determine the buffer size.
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 760c8b9..34d11ac 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -62,6 +62,7 @@
void useEmptyDamage() override;
bool isOpaque(const Layer::State& s) const override;
+ bool canReceiveInput() const override;
// isVisible - true if this layer is visible, false otherwise
bool isVisible() const override;
@@ -77,7 +78,7 @@
bool isHdrY410() const override;
- bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
+ void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
const CompositorTiming&) override;
@@ -152,7 +153,7 @@
bool onPreComposition(nsecs_t) override;
void preparePerFrameCompositionState() override;
- static bool getOpacityForFormat(uint32_t format);
+ static bool getOpacityForFormat(PixelFormat format);
// from graphics API
const uint32_t mTextureName;
@@ -164,15 +165,13 @@
void updateCloneBufferInfo() override;
uint64_t mPreviousFrameNumber = 0;
- uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override;
-
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
- bool getAutoRefresh() const { return mAutoRefresh; }
+ bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
// Returns true if the next buffer should be presented at the expected present time
@@ -183,14 +182,11 @@
// specific logic
virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
- std::atomic<bool> mAutoRefresh{false};
std::atomic<bool> mSidebandStreamChanged{false};
private:
virtual bool fenceHasSignaled() const = 0;
virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
- virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
-
// Latch sideband stream and returns true if the dirty region should be updated.
virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 96b2247..c79fa11 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -438,7 +438,7 @@
}
void BufferLayerConsumer::onSidebandStreamChanged() {
- FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
+ [[maybe_unused]] FrameAvailableListener* unsafeFrameAvailableListener = nullptr;
{
Mutex::Autolock lock(mFrameAvailableMutex);
unsafeFrameAvailableListener = mFrameAvailableListener.unsafe_get();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 99e470d..926aa1d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,7 +17,6 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
#undef LOG_TAG
#define LOG_TAG "BufferQueueLayer"
@@ -49,7 +48,9 @@
// Interface implementation for Layer
// -----------------------------------------------------------------------
-void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
+void BufferQueueLayer::onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) {
+ sp<Fence> releaseFence = new Fence(dup(futureRenderEngineResult.get().drawFence));
mConsumer->setReleaseFence(releaseFence);
// Prevent tracing the same release multiple times.
@@ -66,16 +67,6 @@
mConsumer->setTransformHint(mTransformHint);
}
-std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
- std::vector<OccupancyTracker::Segment> history;
- status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
- if (result != NO_ERROR) {
- ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result);
- return {};
- }
- return history;
-}
-
void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
if (!mConsumer->releasePendingBuffer()) {
return;
@@ -127,7 +118,7 @@
bool BufferQueueLayer::fenceHasSignaled() const {
Mutex::Autolock lock(mQueueItemLock);
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -153,54 +144,21 @@
}
bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
+ Mutex::Autolock lock(mQueueItemLock);
+
if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
return true;
}
- Mutex::Autolock lock(mQueueItemLock);
return mQueueItems[0].item.mTimestamp <= expectedPresentTime;
}
-uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
- Mutex::Autolock lock(mQueueItemLock);
- uint64_t frameNumber = mQueueItems[0].item.mFrameNumber;
-
- // The head of the queue will be dropped if there are signaled and timely frames behind it
- if (isRemovedFromCurrentState()) {
- expectedPresentTime = 0;
- }
-
- for (int i = 1; i < mQueueItems.size(); i++) {
- const bool fenceSignaled =
- mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
- if (!fenceSignaled) {
- break;
- }
-
- // We don't drop frames without explicit timestamps
- if (mQueueItems[i].item.mIsAutoTimestamp) {
- break;
- }
-
- const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp;
- if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC ||
- desiredPresent > expectedPresentTime) {
- break;
- }
-
- frameNumber = mQueueItems[i].item.mFrameNumber;
- }
-
- return frameNumber;
-}
-
bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
// We need to update the sideband stream if the layer has both a buffer and a sideband stream.
- const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+ editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
bool sidebandStreamChanged = true;
- if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) ||
- updateSidebandStream) {
+ if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
// mSidebandStreamChanged was changed to false
mSidebandStream = mConsumer->getSidebandStream();
auto* layerCompositionState = editCompositionState();
@@ -243,7 +201,7 @@
uint64_t lastSignaledFrameNumber = mLastFrameNumberReceived;
{
Mutex::Autolock lock(mQueueItemLock);
- for (int i = 0; i < mQueueItems.size(); i++) {
+ for (size_t i = 0; i < mQueueItems.size(); i++) {
bool fenceSignaled =
mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
if (!fenceSignaled) {
@@ -258,24 +216,26 @@
bool autoRefresh;
status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
&queuedBuffer, maxFrameNumberToAcquire);
- mAutoRefresh = autoRefresh;
+ mDrawingState.autoRefresh = autoRefresh;
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
// layer update so we check again at the next opportunity.
- mFlinger->signalLayerUpdate();
+ mFlinger->onLayerUpdate();
return BAD_VALUE;
} else if (updateResult == BufferLayerConsumer::BUFFER_REJECTED) {
// If the buffer has been rejected, remove it from the shadow queue
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
- mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
- if (mQueueItems[0].surfaceFrame) {
- addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+ if (mQueuedFrames > 0) {
+ mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
+ mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
+ if (mQueueItems[0].surfaceFrame) {
+ addSurfaceFrameDroppedForBuffer(mQueueItems[0].surfaceFrame);
+ }
+ mQueueItems.erase(mQueueItems.begin());
+ mQueuedFrames--;
}
- mQueueItems.erase(mQueueItems.begin());
- mQueuedFrames--;
}
return BAD_VALUE;
} else if (updateResult != NO_ERROR || mUpdateTexImageFailed) {
@@ -305,6 +265,7 @@
return BAD_VALUE;
}
+ bool more_frames_pending = false;
if (queuedBuffer) {
// Autolock scope
auto currentFrameNumber = mConsumer->getFrameNumber();
@@ -313,7 +274,7 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
- while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
+ while (mQueuedFrames > 0 && mQueueItems[0].item.mFrameNumber != currentFrameNumber) {
mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage);
mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber);
if (mQueueItems[0].surfaceFrame) {
@@ -334,12 +295,13 @@
latchTime);
}
mQueueItems.erase(mQueueItems.begin());
+ more_frames_pending = (mQueuedFrames.fetch_sub(1) > 1);
}
// Decrement the queued-frames count. Signal another event if we
// have more frames pending.
- if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) {
- mFlinger->signalLayerUpdate();
+ if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) {
+ mFlinger->onLayerUpdate();
}
return NO_ERROR;
@@ -410,8 +372,9 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- LayerHistory::LayerUpdateType::Buffer);
+
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
@@ -442,7 +405,7 @@
mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
item.mGraphicBuffer->getHeight(), item.mFrameNumber);
- mFlinger->signalLayerUpdate();
+ mFlinger->onLayerUpdate();
mConsumer->onBufferAvailable(item);
}
@@ -488,7 +451,7 @@
bool sidebandStreamChanged = false;
if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) {
// mSidebandStreamChanged was changed to true
- mFlinger->signalLayerUpdate();
+ mFlinger->onLayerUpdate();
}
}
@@ -561,7 +524,7 @@
}
sp<Layer> BufferQueueLayer::createClone() {
- LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+ LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
layer->setInitialValuesForClone(this);
@@ -625,4 +588,4 @@
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index b3b7948..c6e0727 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -42,9 +42,8 @@
// Implements Layer.
const char* getType() const override { return "BufferQueueLayer"; }
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-
- std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
+ void onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
@@ -63,6 +62,11 @@
status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
sp<IGraphicBufferProducer> getProducer() const;
+ void setSizeForTest(uint32_t w, uint32_t h) {
+ mDrawingState.active_legacy.w = w;
+ mDrawingState.active_legacy.h = h;
+ }
+
protected:
void gatherBufferInfo() override;
@@ -89,7 +93,6 @@
};
private:
- uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index df91904..2fac880 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
//#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "BufferStateLayer"
@@ -45,13 +40,13 @@
namespace {
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
- const sp<Fence>& releaseFence, uint32_t transformHint,
+ const sp<Fence>& releaseFence,
uint32_t currentMaxAcquiredBufferCount) {
if (!listener) {
return;
}
listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE, transformHint,
+ releaseFence ? releaseFence : Fence::NO_FENCE,
currentMaxAcquiredBufferCount);
}
} // namespace
@@ -69,77 +64,30 @@
if (mBufferInfo.mBuffer != nullptr) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
- mBufferInfo.mFence, mTransformHint,
+ mBufferInfo.mFence,
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
}
}
-status_t BufferStateLayer::addReleaseFence(const sp<CallbackHandle>& ch,
- const sp<Fence>& fence) {
- if (ch == nullptr) {
- return OK;
- }
- ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
- if (!ch->previousReleaseFence.get()) {
- ch->previousReleaseFence = fence;
- return OK;
- }
-
- // Below logic is lifted from ConsumerBase.cpp:
- // Check status of fences first because merging is expensive.
- // Merging an invalid fence with any other fence results in an
- // invalid fence.
- auto currentStatus = ch->previousReleaseFence->getStatus();
- if (currentStatus == Fence::Status::Invalid) {
- ALOGE("Existing fence has invalid state, layer: %s", mName.c_str());
- return BAD_VALUE;
- }
-
- auto incomingStatus = fence->getStatus();
- if (incomingStatus == Fence::Status::Invalid) {
- ALOGE("New fence has invalid state, layer: %s", mName.c_str());
- ch->previousReleaseFence = fence;
- return BAD_VALUE;
- }
-
- // If both fences are signaled or both are unsignaled, we need to merge
- // them to get an accurate timestamp.
- if (currentStatus == incomingStatus) {
- char fenceName[32] = {};
- snprintf(fenceName, 32, "%.28s", mName.c_str());
- sp<Fence> mergedFence = Fence::merge(
- fenceName, ch->previousReleaseFence, fence);
- if (!mergedFence.get()) {
- ALOGE("failed to merge release fences, layer: %s", mName.c_str());
- // synchronization is broken, the best we can do is hope fences
- // signal in order so the new fence will act like a union
- ch->previousReleaseFence = fence;
- return BAD_VALUE;
- }
- ch->previousReleaseFence = mergedFence;
- } else if (incomingStatus == Fence::Status::Unsignaled) {
- // If one fence has signaled and the other hasn't, the unsignaled
- // fence will approximately correspond with the correct timestamp.
- // There's a small race if both fences signal at about the same time
- // and their statuses are retrieved with unfortunate timing. However,
- // by this point, they will have both signaled and only the timestamp
- // will be slightly off; any dependencies after this point will
- // already have been met.
- ch->previousReleaseFence = fence;
- }
- // else if (currentStatus == Fence::Status::Unsignaled) is a no-op.
-
- return OK;
-}
-
// -----------------------------------------------------------------------
// Interface implementation for Layer
// -----------------------------------------------------------------------
-void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
- if (!releaseFence->isValid()) {
- return;
+void BufferStateLayer::onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) {
+ // If a layer has been displayed again we may need to clear
+ // the mLastClientComposition fence that we use for early release in setBuffer
+ // (as we now have a new fence which won't pass through the client composition path in some cases
+ // e.g. screenshot). We expect one call to onLayerDisplayed after receiving the GL comp fence
+ // from a single composition cycle, and want to clear on the second call
+ // (which we track with mLastClientCompositionDisplayed)
+ if (mLastClientCompositionDisplayed) {
+ mLastClientCompositionFence = nullptr;
+ mLastClientCompositionDisplayed = false;
+ } else if (mLastClientCompositionFence) {
+ mLastClientCompositionDisplayed = true;
}
+
// The previous release fence notifies the client that SurfaceFlinger is done with the previous
// buffer that was presented on this layer. The first transaction that came in this frame that
// replaced the previous buffer on this layer needs this release fence, because the fence will
@@ -164,17 +112,17 @@
break;
}
}
- auto status = addReleaseFence(ch, releaseFence);
- if (status != OK) {
- ALOGE("Failed to add release fence for layer %s", getName().c_str());
- }
-
- mPreviousReleaseFence = releaseFence;
// Prevent tracing the same release multiple times.
if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
mPreviousReleasedFrameNumber = mPreviousFrameNumber;
}
+
+ if (ch != nullptr) {
+ ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+ ch->previousReleaseFences.emplace_back(futureRenderEngineResult);
+ ch->name = mName;
+ }
}
void BufferStateLayer::onSurfaceFrameCreated(
@@ -219,12 +167,21 @@
JankData(surfaceFrame->getToken(), surfaceFrame->getJankType().value()));
}
- mFlinger->getTransactionCallbackInvoker().finalizePendingCallbackHandles(
+ mFlinger->getTransactionCallbackInvoker().addCallbackHandles(
mDrawingState.callbackHandles, jankData);
+ sp<Fence> releaseFence = Fence::NO_FENCE;
+ for (auto& handle : mDrawingState.callbackHandles) {
+ if (handle->releasePreviousBuffer &&
+ mDrawingState.releaseBufferEndpoint == handle->listener) {
+ releaseFence =
+ handle->previousReleaseFence ? handle->previousReleaseFence : Fence::NO_FENCE;
+ break;
+ }
+ }
+
mDrawingState.callbackHandles = {};
- const sp<Fence>& releaseFence(mPreviousReleaseFence);
std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
{
Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -411,15 +368,55 @@
return true;
}
-bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- const sp<Fence>& acquireFence, nsecs_t postTime,
+std::shared_ptr<renderengine::ExternalTexture> BufferStateLayer::getBufferFromBufferData(
+ const BufferData& bufferData) {
+ bool cacheIdChanged = bufferData.flags.test(BufferData::BufferDataChange::cachedBufferChanged);
+ bool bufferSizeExceedsLimit = false;
+ std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
+ if (cacheIdChanged && bufferData.buffer != nullptr) {
+ bufferSizeExceedsLimit =
+ mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+ bufferData.buffer->getHeight());
+ if (!bufferSizeExceedsLimit) {
+ ClientCache::getInstance().add(bufferData.cachedBuffer, bufferData.buffer);
+ buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+ }
+ } else if (cacheIdChanged) {
+ buffer = ClientCache::getInstance().get(bufferData.cachedBuffer);
+ } else if (bufferData.buffer != nullptr) {
+ bufferSizeExceedsLimit =
+ mFlinger->exceedsMaxRenderTargetSize(bufferData.buffer->getWidth(),
+ bufferData.buffer->getHeight());
+ if (!bufferSizeExceedsLimit) {
+ buffer = std::make_shared<
+ renderengine::ExternalTexture>(bufferData.buffer, mFlinger->getRenderEngine(),
+ renderengine::ExternalTexture::Usage::READABLE);
+ }
+ }
+ ALOGE_IF(bufferSizeExceedsLimit,
+ "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
+ "limit.",
+ getDebugName());
+ return buffer;
+}
+
+bool BufferStateLayer::setBuffer(const BufferData& bufferData, nsecs_t postTime,
nsecs_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& clientCacheId, uint64_t frameNumber,
- std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
- const sp<ITransactionCompletedListener>& releaseBufferListener,
- const sp<IBinder>& releaseBufferEndpoint) {
+ std::optional<nsecs_t> dequeueTime,
+ const FrameTimelineInfo& info) {
ATRACE_CALL();
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer =
+ getBufferFromBufferData(bufferData);
+ if (!buffer) {
+ return false;
+ }
+
+ const bool frameNumberChanged =
+ bufferData.flags.test(BufferData::BufferDataChange::frameNumberChanged);
+ const uint64_t frameNumber =
+ frameNumberChanged ? bufferData.frameNumber : mDrawingState.frameNumber + 1;
+
if (mDrawingState.buffer) {
mReleasePreviousBuffer = true;
if (mDrawingState.buffer != mBufferInfo.mBuffer ||
@@ -430,7 +427,7 @@
// call any release buffer callbacks if set.
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
- mDrawingState.acquireFence, mTransformHint,
+ mDrawingState.acquireFence,
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
mOwnerUid));
decrementPendingBufferCount();
@@ -439,13 +436,28 @@
addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
mDrawingState.bufferSurfaceFrameTX.reset();
}
+ } else if (mLastClientCompositionFence != nullptr) {
+ callReleaseBufferCallback(mDrawingState.releaseBufferListener,
+ mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
+ mLastClientCompositionFence,
+ mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
+ mOwnerUid));
+ mLastClientCompositionFence = nullptr;
}
}
mDrawingState.frameNumber = frameNumber;
- mDrawingState.releaseBufferListener = releaseBufferListener;
+ mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = buffer;
- mDrawingState.clientCacheId = clientCacheId;
+ mDrawingState.clientCacheId = bufferData.cachedBuffer;
+
+ mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
+ ? bufferData.acquireFence
+ : Fence::NO_FENCE;
+ mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(mDrawingState.acquireFence);
+ // The acquire fences of BufferStateLayers have already signaled before they are set
+ mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime();
+
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -464,10 +476,11 @@
return static_cast<nsecs_t>(0);
}();
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- LayerHistory::LayerUpdateType::Buffer);
- addFrameEvent(acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
+
+ addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -482,20 +495,7 @@
mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth();
mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight();
- mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint;
-
- return true;
-}
-
-bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) {
- mDrawingState.acquireFence = fence;
- mDrawingState.acquireFenceTime = std::make_unique<FenceTime>(fence);
-
- // The acquire fences of BufferStateLayers have already signaled before they are set
- mCallbackHandleAcquireTime = mDrawingState.acquireFenceTime->getSignalTime();
-
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
+ mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint;
return true;
}
@@ -544,7 +544,7 @@
setTransactionFlags(eTransactionNeeded);
if (!mSidebandStreamChanged.exchange(true)) {
// mSidebandStreamChanged was false
- mFlinger->signalLayerUpdate();
+ mFlinger->onLayerUpdate();
}
return true;
}
@@ -569,10 +569,6 @@
handle->acquireTime = mCallbackHandleAcquireTime;
handle->frameNumber = mDrawingState.frameNumber;
- // Notify the transaction completed thread that there is a pending latched callback
- // handle
- mFlinger->getTransactionCallbackInvoker().registerPendingCallbackHandle(handle);
-
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
@@ -596,7 +592,7 @@
return true;
}
-Rect BufferStateLayer::getBufferSize(const State& s) const {
+Rect BufferStateLayer::getBufferSize(const State& /*s*/) const {
// for buffer state layers we use the display frame size as the buffer size.
if (mBufferInfo.mBuffer == nullptr) {
@@ -618,7 +614,7 @@
}
}
- return Rect(0, 0, bufWidth, bufHeight);
+ return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
}
FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const {
@@ -635,7 +631,7 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -664,47 +660,15 @@
return BufferLayer::onPreComposition(refreshStartTime);
}
-uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
- return mDrawingState.frameNumber;
-}
-
-/**
- * This is the frameNumber used for deferred transaction signalling. We need to use this because
- * of cases where we defer a transaction for a surface to itself. In the BLAST world this
- * may not make a huge amount of sense (Why not just merge the Buffer transaction with the
- * deferred transaction?) but this is an important legacy use case, for example moving
- * a window at the same time it draws makes use of this kind of technique. So anyway
- * imagine we have something like this:
- *
- * Transaction { // containing
- * Buffer -> frameNumber = 2
- * DeferTransactionUntil -> frameNumber = 2
- * Random other stuff
- * }
- * Now imagine mFrameNumber returned mDrawingState.frameNumber (or mCurrentFrameNumber).
- * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
- * haven't swapped mDrawingState to mDrawingState yet we will think the sync point
- * is not ready. So we will return false from applyPendingState and not swap
- * current state to drawing state. But because we don't swap current state
- * to drawing state the number will never update and we will be stuck. This way
- * we can see we need to return the frame number for the buffer we are about
- * to apply.
- */
-uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const {
- return mDrawingState.frameNumber;
-}
-
void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
- if (!mAutoRefresh.exchange(autoRefresh)) {
- mFlinger->signalLayerUpdate();
- }
+ mDrawingState.autoRefresh = autoRefresh;
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
// We need to update the sideband stream if the layer has both a buffer and a sideband stream.
- const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get();
+ editCompositionState()->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
- if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) {
+ if (mSidebandStreamChanged.exchange(false)) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
mSidebandStream = s.sidebandStream;
@@ -770,7 +734,7 @@
std::deque<sp<CallbackHandle>> remainingHandles;
mFlinger->getTransactionCallbackInvoker()
- .finalizeOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
+ .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
mDrawingState.callbackHandles = remainingHandles;
mDrawingStateModified = false;
@@ -817,7 +781,7 @@
eraseBufferLocked(clientCacheId);
}
-uint32_t BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
+int BufferStateLayer::HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
std::lock_guard<std::mutex> lock(mMutex);
auto itr = mCachedBuffers.find(clientCacheId);
if (itr == mCachedBuffers.end()) {
@@ -828,7 +792,7 @@
return hwcCacheSlot;
}
-uint32_t BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
+int BufferStateLayer::HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId)
REQUIRES(mMutex) {
if (!clientCacheId.isValid()) {
ALOGE("invalid process, returning invalid slot");
@@ -837,17 +801,17 @@
ClientCache::getInstance().registerErasedRecipient(clientCacheId, wp<ErasedRecipient>(this));
- uint32_t hwcCacheSlot = getFreeHwcCacheSlot();
+ int hwcCacheSlot = getFreeHwcCacheSlot();
mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
return hwcCacheSlot;
}
-uint32_t BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
+int BufferStateLayer::HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
if (mFreeHwcCacheSlots.empty()) {
evictLeastRecentlyUsed();
}
- uint32_t hwcCacheSlot = mFreeHwcCacheSlots.top();
+ int hwcCacheSlot = mFreeHwcCacheSlots.top();
mFreeHwcCacheSlots.pop();
return hwcCacheSlot;
}
@@ -920,7 +884,7 @@
}
sp<Layer> BufferStateLayer::createClone() {
- LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+ LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
layer->mHwcSlotGenerator = mHwcSlotGenerator;
@@ -934,8 +898,8 @@
return false;
}
- uint32_t bufferWidth = s.buffer->getBuffer()->width;
- uint32_t bufferHeight = s.buffer->getBuffer()->height;
+ int32_t bufferWidth = s.buffer->getBuffer()->width;
+ int32_t bufferHeight = s.buffer->getBuffer()->height;
// Undo any transformations on the buffer and return the result.
if (s.bufferTransform & ui::Transform::ROT_90) {
@@ -994,6 +958,3 @@
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 0a0527c..eea700c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -39,7 +39,9 @@
// Implements Layer.
const char* getType() const override { return "BufferStateLayer"; }
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ void onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
+
void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
@@ -55,13 +57,9 @@
bool setTransform(uint32_t transform) override;
bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
bool setCrop(const Rect& crop) override;
- bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& buffer,
- const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber,
- std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info,
- const sp<ITransactionCompletedListener>& transactionListener,
- const sp<IBinder>& releaseBufferEndpoint) override;
- bool setAcquireFence(const sp<Fence>& fence) override;
+ bool setBuffer(const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
+ bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
+ const FrameTimelineInfo& info) override;
bool setDataspace(ui::Dataspace dataspace) override;
bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
bool setSurfaceDamageRegion(const Region& surfaceDamage) override;
@@ -105,7 +103,6 @@
protected:
void gatherBufferInfo() override;
- uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame);
ui::Transform getInputTransform() const override;
Rect getInputBounds() const override;
@@ -120,10 +117,6 @@
bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
nsecs_t requestedPresentTime);
- status_t addReleaseFence(const sp<CallbackHandle>& ch, const sp<Fence>& releaseFence);
-
- uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
-
bool latchSidebandStream(bool& recomputeVisibleRegions) override;
bool hasFrameUpdate() const override;
@@ -143,7 +136,9 @@
bool bufferNeedsFiltering() const override;
- sp<Fence> mPreviousReleaseFence;
+ std::shared_ptr<renderengine::ExternalTexture> getBufferFromBufferData(
+ const BufferData& bufferData);
+
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
uint64_t mPreviousReleasedFrameNumber = 0;
@@ -178,19 +173,19 @@
class HwcSlotGenerator : public ClientCache::ErasedRecipient {
public:
HwcSlotGenerator() {
- for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
mFreeHwcCacheSlots.push(i);
}
}
void bufferErased(const client_cache_t& clientCacheId);
- uint32_t getHwcCacheSlot(const client_cache_t& clientCacheId);
+ int getHwcCacheSlot(const client_cache_t& clientCacheId);
private:
friend class SlotGenerationTest;
- uint32_t addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- uint32_t getFreeHwcCacheSlot() REQUIRES(mMutex);
+ int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
+ int getFreeHwcCacheSlot() REQUIRES(mMutex);
void evictLeastRecentlyUsed() REQUIRES(mMutex);
void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
@@ -202,11 +197,10 @@
std::mutex mMutex;
- std::unordered_map<client_cache_t,
- std::pair<uint32_t /*HwcCacheSlot*/, uint32_t /*counter*/>,
+ std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
CachedBufferHash>
mCachedBuffers GUARDED_BY(mMutex);
- std::stack<uint32_t /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
+ std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
// The cache increments this counter value when a slot is updated or used.
// Used to track the least recently-used buffer
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index aac6c91..6d7b732 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <stdint.h>
#include <sys/types.h>
@@ -76,40 +72,34 @@
return lbc;
}
-status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parentHandle,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
- uint32_t* outTransformHint) {
+status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */,
+ PixelFormat /* format */, uint32_t flags,
+ const sp<IBinder>& parentHandle, LayerMetadata metadata,
+ sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */,
+ int32_t* outLayerId, uint32_t* outTransformHint) {
// We rely on createLayer to check permissions.
- return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- parentHandle, outLayerId, nullptr, outTransformHint);
+ LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata));
+ return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr,
+ outTransformHint);
}
-status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags,
- const sp<IGraphicBufferProducer>& parent,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
- uint32_t* outTransformHint) {
- if (mFlinger->authenticateSurfaceTexture(parent) == false) {
- ALOGE("failed to authenticate surface texture");
- return BAD_VALUE;
- }
-
- const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
- if (layer == nullptr) {
- ALOGE("failed to find parent layer");
- return BAD_VALUE;
- }
-
- return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- nullptr, outLayerId, layer, outTransformHint);
+status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */,
+ uint32_t /* h */, PixelFormat /* format */,
+ uint32_t /* flags */,
+ const sp<IGraphicBufferProducer>& /* parent */,
+ LayerMetadata /* metadata */, sp<IBinder>* /* handle */,
+ sp<IGraphicBufferProducer>* /* gbp */,
+ int32_t* /* outLayerId */,
+ uint32_t* /* outTransformHint */) {
+ // This api does not make sense with blast since SF no longer tracks IGBP. This api should be
+ // removed.
+ return BAD_VALUE;
}
status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
int32_t* outLayerId) {
- return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
+ LayerCreationArgs args(mFlinger.get(), this, "MirrorRoot", 0 /* flags */, LayerMetadata());
+ return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
@@ -132,6 +122,3 @@
// ---------------------------------------------------------------------------
}; // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index f310738..e7b8995 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -21,12 +21,12 @@
#include <cinttypes>
+#include <android-base/stringprintf.h>
+
#include "ClientCache.h"
namespace android {
-using base::StringAppendF;
-
ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {}
@@ -82,10 +82,13 @@
return false;
}
- status_t err = token->linkToDeath(mDeathRecipient);
- if (err != NO_ERROR) {
- ALOGE("failed to cache buffer: could not link to death");
- return false;
+ // Only call linkToDeath if not a local binder
+ if (token->localBinder() == nullptr) {
+ status_t err = token->linkToDeath(mDeathRecipient);
+ if (err != NO_ERROR) {
+ ALOGE("failed to cache buffer: could not link to death");
+ return false;
+ }
}
auto [itr, success] =
mBuffers.emplace(processToken,
@@ -212,16 +215,15 @@
void ClientCache::dump(std::string& result) {
std::lock_guard lock(mMutex);
- for (auto i : mBuffers) {
- const sp<IBinder>& cacheOwner = i.second.first;
- StringAppendF(&result," Cache owner: %p\n", cacheOwner.get());
- auto &buffers = i.second.second;
- for (auto& [id, clientCacheBuffer] : buffers) {
- StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id,
- (int)clientCacheBuffer.buffer->getBuffer()->getWidth(),
- (int)clientCacheBuffer.buffer->getBuffer()->getHeight());
+ for (const auto& [_, cache] : mBuffers) {
+ base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get());
+
+ for (const auto& [id, entry] : cache.second) {
+ const auto& buffer = entry.buffer->getBuffer();
+ base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(),
+ buffer->getHeight());
}
}
}
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index d738ccd..aefc014 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -20,6 +20,7 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.3",
"libbase",
@@ -37,13 +38,16 @@
static_libs: [
"libmath",
"librenderengine",
+ "libtonemap",
"libtrace_proto",
+ "libaidlcommonsupport",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
+ "android.hardware.graphics.composer3-command-buffer",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 554e2f4..93586ed 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -47,9 +47,6 @@
// All the layers that have queued updates.
Layers layersWithQueuedFrames;
- // If true, forces the entire display to be considered dirty and repainted
- bool repaintEverything{false};
-
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
@@ -88,8 +85,8 @@
// to prevent an early presentation of a frame.
std::shared_ptr<FenceTime> previousPresentFence;
- // The predicted next invalidation time
- std::optional<std::chrono::steady_clock::time_point> nextInvalidateTime;
+ // If set, a frame has been scheduled for that time.
+ std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 14eddb1..98c4af4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -36,18 +36,12 @@
struct DisplayCreationArgs {
DisplayId id;
- // Unset for virtual displays
- std::optional<ui::DisplayConnectionType> connectionType;
-
// Size of the display in pixels
- ui::Size pixels = ui::Size::INVALID;
+ ui::Size pixels = ui::kInvalidSize;
// True if this display should be considered secure
bool isSecure = false;
- // Gives the initial layer stack id to be used for the display
- uint32_t layerStackId = ~0u;
-
// Optional pointer to the power advisor interface, if one is needed for
// this display.
Hwc2::PowerAdvisor* powerAdvisor = nullptr;
@@ -69,11 +63,6 @@
return *this;
}
- DisplayCreationArgsBuilder& setConnectionType(ui::DisplayConnectionType connectionType) {
- mArgs.connectionType = connectionType;
- return *this;
- }
-
DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
mArgs.pixels = pixels;
return *this;
@@ -84,11 +73,6 @@
return *this;
}
- DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
- mArgs.layerStackId = layerStackId;
- return *this;
- }
-
DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
mArgs.powerAdvisor = powerAdvisor;
return *this;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 4502eee..c553fce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -16,6 +16,8 @@
#pragma once
+#include <cinttypes>
+
#include <ui/Size.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -47,13 +49,8 @@
// before composition takes place. The DisplaySurface can use the
// composition type to decide how to manage the flow of buffers between
// GPU and HWC for this frame.
- enum CompositionType {
- COMPOSITION_UNKNOWN = 0,
- COMPOSITION_GPU = 1,
- COMPOSITION_HWC = 2,
- COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
- };
- virtual status_t prepareFrame(CompositionType compositionType) = 0;
+ enum class CompositionType : uint8_t { Unknown = 0, Gpu = 0b1, Hwc = 0b10, Mixed = Gpu | Hwc };
+ virtual status_t prepareFrame(CompositionType) = 0;
// Inform the surface that GPU composition is complete for this frame, and
// the surface should make sure that HWComposer has the correct buffer for
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e51019a..e77e155 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -16,6 +16,7 @@
#pragma once
+#include <future>
#include <optional>
#include <ostream>
#include <unordered_set>
@@ -26,6 +27,7 @@
#pragma clang diagnostic ignored "-Wextra"
#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -115,10 +117,6 @@
// If set to true, the target buffer has protected content support.
const bool supportsProtectedContent;
- // Modified by each call to prepareClientComposition to indicate the
- // region of the target buffer that should be cleared.
- Region& clearRegion;
-
// Viewport of the target being rendered to. This is used to determine
// the shadow light position.
const Rect& viewport;
@@ -136,6 +134,9 @@
// Configure layer settings for using blurs
BlurSetting blurSetting;
+
+ // Requested white point of the layer in nits
+ const float whitePointNits;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
@@ -155,7 +156,7 @@
ClientCompositionTargetSettings&) = 0;
// Called after the layer is displayed to update the presentation fence
- virtual void onLayerDisplayed(const sp<Fence>&) = 0;
+ virtual void onLayerDisplayed(std::shared_future<renderengine::RenderEngineResult>) = 0;
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
@@ -165,6 +166,7 @@
// Whether the layer should be rendered with rounded corners.
virtual bool hasRoundedCorners() const = 0;
+ virtual void setWasClientComposed(const sp<Fence>&) {}
};
// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
@@ -177,11 +179,10 @@
static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
const LayerFE::ClientCompositionTargetSettings& rhs) {
- return lhs.clip.hasSameRects(rhs.clip) &&
- lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+ return lhs.clip.hasSameRects(rhs.clip) && lhs.needsFiltering == rhs.needsFiltering &&
+ lhs.isSecure == rhs.isSecure &&
lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
- lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
- lhs.dataspace == rhs.dataspace &&
+ lhs.viewport == rhs.viewport && lhs.dataspace == rhs.dataspace &&
lhs.realContentIsVisible == rhs.realContentIsVisible &&
lhs.clearContent == rhs.clearContent;
}
@@ -202,8 +203,6 @@
*os << "\n .needsFiltering = " << settings.needsFiltering;
*os << "\n .isSecure = " << settings.isSecure;
*os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
- *os << "\n .clearRegion = ";
- PrintTo(settings.clearRegion, os);
*os << "\n .viewport = ";
PrintTo(settings.viewport, os);
*os << "\n .dataspace = ";
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index a45be8a..8bf7f8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -22,6 +22,7 @@
#include <math/mat4.h>
#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -38,6 +39,8 @@
#include "DisplayHardware/Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -93,11 +96,9 @@
/*
* Visibility state
*/
- // the layer stack this layer belongs to
- std::optional<uint32_t> layerStackId;
- // If true, this layer should be only visible on the internal display
- bool internalOnly{false};
+ // The filter that determines which outputs include this layer
+ ui::LayerFilter outputFilter;
// If false, this layer should not be considered visible
bool isVisible{true};
@@ -157,16 +158,19 @@
*/
// The type of composition for this layer
- hal::Composition compositionType{hal::Composition::INVALID};
+ aidl::android::hardware::graphics::composer3::Composition compositionType{
+ aidl::android::hardware::graphics::composer3::Composition::INVALID};
// The buffer and related state
sp<GraphicBuffer> buffer;
int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
- sp<Fence> acquireFence;
+ sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
// The handle to use for a sideband stream for this layer
sp<NativeHandle> sidebandStream;
+ // If true, this sideband layer has a frame update
+ bool sidebandStreamHasFrame{false};
// The color for this layer
half4 color;
@@ -199,6 +203,9 @@
// The output-independent frame for the cursor
Rect cursorFrame;
+ // framerate of the layer as measured by LayerHistory
+ float fps;
+
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 0ef0b99..73770b7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -28,6 +28,7 @@
#include <renderengine/LayerSettings.h>
#include <ui/Fence.h>
#include <ui/GraphicTypes.h>
+#include <ui/LayerStack.h>
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -180,9 +181,8 @@
// output.
virtual ui::Transform::RotationFlags getTransformHint() const = 0;
- // Sets the layer stack filtering settings for this output. See
- // belongsInOutput for full details.
- virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
+ // Sets the filter for this output. See Output::includesLayer.
+ virtual void setLayerFilter(ui::LayerFilter) = 0;
// Sets the output color mode
virtual void setColorProfile(const ColorProfile&) = 0;
@@ -221,20 +221,12 @@
virtual OutputCompositionState& editState() = 0;
// Gets the dirty region in layer stack space.
- // If repaintEverything is true, this will be the full display bounds.
- virtual Region getDirtyRegion(bool repaintEverything) const = 0;
+ virtual Region getDirtyRegion() const = 0;
- // Tests whether a given layerStackId belongs in this output.
- // A layer belongs to the output if its layerStackId matches the of the output layerStackId,
- // unless the layer should display on the primary output only and this is not the primary output
-
- // A layer belongs to the output if its layerStackId matches. Additionally
- // if the layer should only show in the internal (primary) display only and
- // this output allows that.
- virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
-
- // Determines if a layer belongs to the output.
- virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
+ // Returns whether the output includes a layer, based on their respective filters.
+ // See Output::setLayerFilter.
+ virtual bool includesLayer(ui::LayerFilter) const = 0;
+ virtual bool includesLayer(const sp<LayerFE>&) const = 0;
// Returns a pointer to the output layer corresponding to the given layer on
// this output, or nullptr if the layer does not have one
@@ -294,7 +286,8 @@
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
+ bool supportsProtectedContent, ui::Dataspace outputDataspace,
+ std::vector<LayerFE*> &outLayerRef) = 0;
virtual void appendRegionFlashRequests(
const Region& flashRegion,
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index ead941d..a2824f5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -33,6 +33,8 @@
#include "LayerFE.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -112,7 +114,8 @@
virtual bool isHardwareCursor() const = 0;
// Applies a HWC device requested composition type change
- virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
+ virtual void applyDeviceCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition) = 0;
// Prepares to apply any HWC device layer requests
virtual void prepareForDeviceLayerRequests() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
index 58bb41a..a63145a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h
@@ -28,51 +28,36 @@
// Geometrical space to which content is projected.
// For example, this can be the layer space or the physical display space.
-struct ProjectionSpace {
+class ProjectionSpace {
+public:
ProjectionSpace() = default;
- ProjectionSpace(ui::Size size, Rect content)
- : bounds(std::move(size)), content(std::move(content)) {}
-
- // Bounds of this space. Always starts at (0,0).
- Rect bounds;
-
- // Rect onto which content is projected.
- Rect content;
-
- // The orientation of this space. This value is meaningful only in relation to the rotation
- // of another projection space and it's used to determine the rotating transformation when
- // mapping between the two.
- // As a convention when using this struct orientation = 0 for the "oriented*" projection
- // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
- // of the display space will become 90, while the orientation of the layer stack space will
- // remain the same.
- ui::Rotation orientation = ui::ROTATION_0;
+ ProjectionSpace(ui::Size size, Rect content) : mBounds(size), mContent(std::move(content)) {}
// Returns a transform which maps this.content into destination.content
// and also rotates according to this.orientation and destination.orientation
ui::Transform getTransform(const ProjectionSpace& destination) const {
- ui::Rotation rotation = destination.orientation - orientation;
+ ui::Rotation rotation = destination.getOrientation() - mOrientation;
// Compute a transformation which rotates the destination in a way it has the same
// orientation as us.
const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation);
ui::Transform inverseRotatingTransform;
- inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(),
- destination.bounds.height());
+ inverseRotatingTransform.set(inverseRotationFlags, destination.getBounds().width,
+ destination.getBounds().height);
// The destination content rotated so it has the same orientation as us.
- Rect orientedDestContent = inverseRotatingTransform.transform(destination.content);
+ Rect orientedDestContent = inverseRotatingTransform.transform(destination.getContent());
// Compute translation from the source content to (0, 0).
- const float sourceX = content.left;
- const float sourceY = content.top;
+ const float sourceX = mContent.left;
+ const float sourceY = mContent.top;
ui::Transform sourceTranslation;
sourceTranslation.set(-sourceX, -sourceY);
// Compute scaling transform which maps source content to destination content, assuming
// they are both at (0, 0).
ui::Transform scale;
- const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width();
- const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height();
+ const float scaleX = static_cast<float>(orientedDestContent.width()) / mContent.width();
+ const float scaleY = static_cast<float>(orientedDestContent.height()) / mContent.height();
scale.set(scaleX, 0, 0, scaleY);
// Compute translation from (0, 0) to the orientated destination content.
@@ -83,8 +68,8 @@
// Compute rotation transform.
const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation);
- auto orientedDestWidth = destination.bounds.width();
- auto orientedDestHeight = destination.bounds.height();
+ auto orientedDestWidth = destination.getBounds().width;
+ auto orientedDestHeight = destination.getBounds().height;
if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) {
std::swap(orientedDestWidth, orientedDestHeight);
}
@@ -98,9 +83,39 @@
}
bool operator==(const ProjectionSpace& other) const {
- return bounds == other.bounds && content == other.content &&
- orientation == other.orientation;
+ return mBounds == other.mBounds && mContent == other.mContent &&
+ mOrientation == other.mOrientation;
}
+
+ void setBounds(ui::Size newBounds) { mBounds = std::move(newBounds); }
+
+ void setContent(Rect newContent) { mContent = std::move(newContent); }
+
+ void setOrientation(ui::Rotation newOrientation) { mOrientation = newOrientation; }
+
+ Rect getBoundsAsRect() const { return Rect(mBounds.getWidth(), mBounds.getHeight()); }
+
+ const ui::Size& getBounds() const { return mBounds; }
+
+ const Rect& getContent() const { return mContent; }
+
+ ui::Rotation getOrientation() const { return mOrientation; }
+
+private:
+ // Bounds of this space. Always starts at (0,0).
+ ui::Size mBounds = ui::Size();
+
+ // Rect onto which content is projected.
+ Rect mContent = Rect();
+
+ // The orientation of this space. This value is meaningful only in relation to the rotation
+ // of another projection space and it's used to determine the rotating transformation when
+ // mapping between the two.
+ // As a convention when using this struct orientation = 0 for the "oriented*" projection
+ // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation
+ // of the display space will become 90, while the orientation of the layer stack space will
+ // remain the same.
+ ui::Rotation mOrientation = ui::ROTATION_0;
};
} // namespace compositionengine
@@ -108,8 +123,8 @@
inline std::string to_string(const android::compositionengine::ProjectionSpace& space) {
return android::base::
StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)",
- to_string(space.bounds).c_str(), to_string(space.content).c_str(),
- toCString(space.orientation));
+ to_string(space.getBoundsAsRect()).c_str(),
+ to_string(space.getContent()).c_str(), toCString(space.getOrientation()));
}
// Defining PrintTo helps with Google Tests.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index bb540ea..3571e11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -76,16 +76,15 @@
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
- virtual void applyClientTargetRequests(const ClientTargetProperty&);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&, float whitePointNits);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
private:
- bool mIsVirtual = false;
- bool mIsDisconnected = false;
DisplayId mId;
+ bool mIsDisconnected = false;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
index 6b9597b..7521324 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -22,6 +22,7 @@
#include <math/mat4.h>
#include <ui/FloatRect.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/StretchEffect.h>
@@ -52,13 +53,14 @@
dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
}
-void dumpVal(std::string& out, const char* name, const FloatRect& rect);
-void dumpVal(std::string& out, const char* name, const Rect& rect);
-void dumpVal(std::string& out, const char* name, const Region& region);
-void dumpVal(std::string& out, const char* name, const ui::Transform&);
-void dumpVal(std::string& out, const char* name, const ui::Size&);
+void dumpVal(std::string& out, const char* name, ui::LayerFilter);
+void dumpVal(std::string& out, const char* name, ui::Size);
-void dumpVal(std::string& out, const char* name, const mat4& tr);
+void dumpVal(std::string& out, const char* name, const FloatRect&);
+void dumpVal(std::string& out, const char* name, const Rect&);
+void dumpVal(std::string& out, const char* name, const Region&);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+void dumpVal(std::string& out, const char* name, const mat4&);
void dumpVal(std::string& out, const char* name, const StretchEffect&);
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index ddcc907..844876a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -23,6 +23,7 @@
#include <compositionengine/impl/planner/Planner.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/LayerSettings.h>
+
#include <memory>
#include <utility>
#include <vector>
@@ -45,7 +46,7 @@
void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect,
const Rect& orientedDisplaySpaceRect) override;
void setDisplaySize(const ui::Size&) override;
- void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+ void setLayerFilter(ui::LayerFilter) override;
ui::Transform::RotationFlags getTransformHint() const override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
@@ -64,9 +65,10 @@
compositionengine::RenderSurface* getRenderSurface() const override;
void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
- Region getDirtyRegion(bool repaintEverything) const override;
- bool belongsInOutput(std::optional<uint32_t>, bool) const override;
- bool belongsInOutput(const sp<LayerFE>&) const override;
+ Region getDirtyRegion() const override;
+
+ bool includesLayer(ui::LayerFilter) const override;
+ bool includesLayer(const sp<LayerFE>&) const override;
compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
@@ -111,8 +113,8 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion,
- ui::Dataspace outputDataspace) override;
+ bool supportsProtectedContent, ui::Dataspace outputDataspace,
+ std::vector<LayerFE*> &outLayerFEs) override;
void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
void dumpBase(std::string&) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index f34cb94..c5b6443 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -32,6 +32,7 @@
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
#include <compositionengine/ProjectionSpace.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Transform.h>
@@ -59,11 +60,8 @@
// If true, the current frame reused the buffer from a previous client composition
bool reusedClientComposition{false};
- // If true, this output displays layers that are internal-only
- bool layerStackInternal{false};
-
- // The layer stack to display on this display
- uint32_t layerStackId{~0u};
+ // The conditions for including a layer on this output
+ ui::LayerFilter layerFilter;
// The common space for all layers in the layer stack. layerStackSpace.content is the Rect
// which gets projected on the display. The orientation of this space is always ROTATION_0.
@@ -129,6 +127,9 @@
// SDR white point
float sdrWhitePointNits{-1.f};
+ // White point of the client target
+ float clientTargetWhitePointNits{-1.f};
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 244f8ab..0082dac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -27,6 +27,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android::compositionengine {
struct LayerFECompositionState;
@@ -50,7 +52,8 @@
HWC2::Layer* getHwcLayer() const override;
bool requiresClientComposition() const override;
bool isHardwareCursor() const override;
- void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
+ void applyDeviceCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition) override;
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
@@ -68,20 +71,24 @@
private:
Rect calculateInitialCrop() const;
- void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
- uint32_t z);
+ void writeOutputDependentGeometryStateToHWC(
+ HWC2::Layer*, aidl::android::hardware::graphics::composer3::Composition, uint32_t z);
void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
- void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
- bool skipLayer);
+ void writeOutputIndependentPerFrameStateToHWC(
+ HWC2::Layer*, const LayerFECompositionState&,
+ aidl::android::hardware::graphics::composer3::Composition compositionType,
+ bool skipLayer);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer);
- void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ void writeCompositionTypeToHWC(HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition,
bool isPeekingThrough, bool skipLayer);
- void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
- Hwc2::IComposerClient::Composition to) const;
+ void detectDisallowedCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition from,
+ aidl::android::hardware::graphics::composer3::Composition to) const;
bool isClientCompositionForced(bool isPeekingThrough) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 7564c54..49cb912 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -35,6 +35,8 @@
#include "DisplayHardware/ComposerHal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -123,8 +125,8 @@
std::shared_ptr<HWC2::Layer> hwcLayer;
// The most recently set HWC composition type for this layer
- Hwc2::IComposerClient::Composition hwcCompositionType{
- Hwc2::IComposerClient::Composition::INVALID};
+ aidl::android::hardware::graphics::composer3::Composition hwcCompositionType{
+ aidl::android::hardware::graphics::composer3::Composition::INVALID};
// The buffer cache for this layer. This is used to lower the
// cost of sending reused buffers to the HWC.
@@ -146,6 +148,10 @@
// Timestamp for when the layer is queued for client composition
nsecs_t clientCompositionTimestamp{0};
+
+ // White point of the layer, in nits.
+ static constexpr float kDefaultWhitePointNits = 200.f;
+ float whitePointNits = kDefaultWhitePointNits;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index a040fa9..92cc484 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -87,6 +87,13 @@
const bool mEnableHolePunch;
};
+ // Constants not yet backed by a sysprop
+ // CachedSets that contain no more than this many layers may be considered inactive on the basis
+ // of FPS.
+ static constexpr int kNumLayersFpsConsideration = 1;
+ // Frames/Second threshold below which these CachedSets may be considered inactive.
+ static constexpr float kFpsActiveThreshold = 1.f;
+
Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables);
void setDisplaySize(ui::Size size) {
@@ -119,29 +126,30 @@
std::chrono::steady_clock::time_point now);
// A Run is a sequence of CachedSets, which is a candidate for flattening into a single
- // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1
- // CachedSet
+ // CachedSet. Because it is wasteful to flatten 1 CachedSet, a run must contain more than
+ // 1 CachedSet or be used for a hole punch.
class Run {
public:
// A builder for a Run, to aid in construction
class Builder {
private:
std::vector<CachedSet>::const_iterator mStart;
- std::vector<size_t> mLengths;
+ int32_t mNumSets = 0;
const CachedSet* mHolePunchCandidate = nullptr;
const CachedSet* mBlurringLayer = nullptr;
+ bool mBuilt = false;
public:
// Initializes a Builder a CachedSet to start from.
// This start iterator must be an iterator for mLayers
void init(const std::vector<CachedSet>::const_iterator& start) {
mStart = start;
- mLengths.push_back(start->getLayerCount());
+ mNumSets = 1;
}
// Appends a new CachedSet to the end of the run
// The provided length must be the size of the next sequential CachedSet in layers
- void append(size_t length) { mLengths.push_back(length); }
+ void increment() { mNumSets++; }
// Sets the hole punch candidate for the Run.
void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
@@ -154,13 +162,36 @@
// Builds a Run instance, if a valid Run may be built.
std::optional<Run> validateAndBuild() {
- if (mLengths.size() <= 1) {
+ const bool built = mBuilt;
+ mBuilt = true;
+ if (mNumSets <= 0 || built) {
return std::nullopt;
}
+ const bool requiresHolePunch =
+ mHolePunchCandidate && mHolePunchCandidate->requiresHolePunch();
+
+ if (!requiresHolePunch) {
+ // If we don't require a hole punch, then treat solid color layers at the front
+ // to be "cheap", so remove them from the candidate cached set.
+ while (mNumSets > 1 && mStart->getLayerCount() == 1 &&
+ mStart->getFirstLayer().getBuffer() == nullptr) {
+ mStart++;
+ mNumSets--;
+ }
+
+ // Only allow for single cached sets if a hole punch is required. If we're here,
+ // then we don't require a hole punch, so don't build a run.
+ if (mNumSets <= 1) {
+ return std::nullopt;
+ }
+ }
+
return Run(mStart,
- std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
- [](size_t left, size_t right) { return left + right; }),
+ std::reduce(mStart, mStart + mNumSets, 0u,
+ [](size_t length, const CachedSet& set) {
+ return length + set.getLayerCount();
+ }),
mHolePunchCandidate, mBlurringLayer);
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5237527..14324de 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,8 @@
#include "DisplayHardware/Hal.h"
#include "math/HashCombine.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace std {
template <typename T>
struct hash<android::sp<T>> {
@@ -232,7 +234,7 @@
return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
}
int32_t getBackgroundBlurRadius() const { return mBackgroundBlurRadius.get(); }
- hardware::graphics::composer::hal::Composition getCompositionType() const {
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType() const {
return mCompositionType.get();
}
@@ -245,6 +247,7 @@
bool isProtected() const {
return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
}
+ float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; }
void dump(std::string& result) const;
std::optional<std::string> compare(const LayerState& other) const;
@@ -369,17 +372,18 @@
OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
- using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
- LayerStateField::CompositionType>;
- CompositionTypeState
- mCompositionType{[](auto layer) {
- return layer->getState().forceClientComposition
- ? hardware::graphics::composer::hal::Composition::CLIENT
- : layer->getLayerFE()
- .getCompositionState()
- ->compositionType;
- },
- CompositionTypeState::getHalToStrings()};
+ using CompositionTypeState =
+ OutputLayerState<aidl::android::hardware::graphics::composer3::Composition,
+ LayerStateField::CompositionType>;
+ CompositionTypeState mCompositionType{[](auto layer) {
+ return layer->getState().forceClientComposition
+ ? aidl::android::hardware::graphics::
+ composer3::Composition::CLIENT
+ : layer->getLayerFE()
+ .getCompositionState()
+ ->compositionType;
+ },
+ CompositionTypeState::getHalToStrings()};
OutputLayerState<void*, LayerStateField::SidebandStream>
mSidebandStream{[](auto layer) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index fe486d3..ef1560e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -109,7 +109,7 @@
static std::optional<Plan> fromString(const std::string&);
void reset() { mLayerTypes.clear(); }
- void addLayerType(hardware::graphics::composer::hal::Composition type) {
+ void addLayerType(aidl::android::hardware::graphics::composer3::Composition type) {
mLayerTypes.emplace_back(type);
}
@@ -125,7 +125,7 @@
}
private:
- std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition> mLayerTypes;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index d215bda..16aebef 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -39,7 +39,7 @@
std::vector<compositionengine::LayerFE::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
- MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
+ MOCK_METHOD1(onLayerDisplayed, void(std::shared_future<renderengine::RenderEngineResult>));
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 8fdf3ae..7b0d028 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -40,9 +40,12 @@
MOCK_METHOD1(setLayerCachingTexturePoolEnabled, void(bool));
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
- MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
+ MOCK_METHOD(void, setLayerFilter, (ui::LayerFilter));
+ MOCK_METHOD(bool, includesLayer, (ui::LayerFilter), (const));
+ MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&), (const));
+
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
MOCK_METHOD2(setDisplayBrightness, void(float, float));
@@ -62,9 +65,7 @@
MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
MOCK_METHOD0(editState, OutputCompositionState&());
- MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
- MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
- MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+ MOCK_METHOD(Region, getDirtyRegion, (), (const));
MOCK_CONST_METHOD1(getOutputLayerForLayer,
compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
@@ -114,7 +115,7 @@
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
MOCK_METHOD3(generateClientCompositionRequests,
- std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+ std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 358ed5a..a6cb811 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -46,7 +46,8 @@
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
MOCK_CONST_METHOD0(requiresClientComposition, bool());
MOCK_CONST_METHOD0(isHardwareCursor, bool());
- MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
+ MOCK_METHOD1(applyDeviceCompositionTypeChange,
+ void(aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 2f2c686..3571319 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -51,12 +51,9 @@
void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
mId = args.id;
- mIsVirtual = !args.connectionType;
mPowerAdvisor = args.powerAdvisor;
editState().isSecure = args.isSecure;
- editState().displaySpace.bounds = Rect(args.pixels);
- setLayerStackFilter(args.layerStackId,
- args.connectionType == ui::DisplayConnectionType::Internal);
+ editState().displaySpace.setBounds(args.pixels);
setName(args.name);
}
@@ -73,7 +70,7 @@
}
bool Display::isVirtual() const {
- return mIsVirtual;
+ return VirtualDisplayId::tryCast(mId).has_value();
}
std::optional<DisplayId> Display::getDisplayId() const {
@@ -117,8 +114,8 @@
return;
}
- if (mIsVirtual) {
- ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+ if (isVirtual()) {
+ ALOGW("%s: Invalid operation on virtual display", __func__);
return;
}
@@ -136,7 +133,7 @@
StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str());
out.append("\n ");
- dumpVal(out, "isVirtual", mIsVirtual);
+ dumpVal(out, "isVirtual", isVirtual());
dumpVal(out, "DisplayId", to_string(mId));
out.append("\n");
@@ -240,7 +237,8 @@
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
- applyClientTargetRequests(changes->clientTargetProperty);
+ applyClientTargetRequests(changes->clientTargetProperty,
+ changes->clientTargetWhitePointNits);
}
// Determine what type of composition we are doing from the final state
@@ -284,7 +282,8 @@
if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) {
layer->applyDeviceCompositionTypeChange(
- static_cast<Hwc2::IComposerClient::Composition>(it->second));
+ static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+ it->second));
}
}
}
@@ -312,12 +311,14 @@
}
}
-void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty,
+ float whitePointNits) {
if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
return;
}
editState().dataspace = clientTargetProperty.dataspace;
+ editState().clientTargetWhitePointNits = whitePointNits;
getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
}
@@ -364,8 +365,7 @@
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
// 2) There is work to be done (the dirty region isn't empty)
- if (GpuVirtualDisplayId::tryCast(mId) &&
- getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+ if (GpuVirtualDisplayId::tryCast(mId) && getDirtyRegion().isEmpty()) {
ALOGV("Skipping display composition");
return;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
index 5565396..01c368d 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -63,6 +63,18 @@
dumpVal(out, name, valueName.c_str(), value);
}
+void dumpVal(std::string& out, const char* name, ui::LayerFilter filter) {
+ out.append(name);
+ out.append("={");
+ dumpVal(out, "layerStack", filter.layerStack.id);
+ dumpVal(out, "toInternalDisplay", filter.toInternalDisplay);
+ out.push_back('}');
+}
+
+void dumpVal(std::string& out, const char* name, ui::Size size) {
+ StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
+}
+
void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
}
@@ -80,10 +92,6 @@
out.append(" ");
}
-void dumpVal(std::string& out, const char* name, const ui::Size& size) {
- StringAppendF(&out, "%s=[%d %d] ", name, size.width, size.height);
-}
-
void dumpVal(std::string& out, const char* name, const mat4& tr) {
StringAppendF(&out,
"%s=["
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 95ae5e5..d503153 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -27,6 +27,7 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/impl/planner/Planner.h>
+#include <ftl/future.h>
#include <thread>
@@ -156,38 +157,40 @@
const Rect& orientedDisplaySpaceRect) {
auto& outputState = editState();
- outputState.displaySpace.orientation = orientation;
- LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT,
+ outputState.displaySpace.setOrientation(orientation);
+ LOG_FATAL_IF(outputState.displaySpace.getBoundsAsRect() == Rect::INVALID_RECT,
"The display bounds are unknown.");
// Compute orientedDisplaySpace
- ui::Size orientedSize = outputState.displaySpace.bounds.getSize();
+ ui::Size orientedSize = outputState.displaySpace.getBounds();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
}
- outputState.orientedDisplaySpace.bounds = Rect(orientedSize);
- outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect;
+ outputState.orientedDisplaySpace.setBounds(orientedSize);
+ outputState.orientedDisplaySpace.setContent(orientedDisplaySpaceRect);
// Compute displaySpace.content
const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation);
ui::Transform rotation;
if (transformOrientationFlags != ui::Transform::ROT_INVALID) {
- const auto displaySize = outputState.displaySpace.bounds;
+ const auto displaySize = outputState.displaySpace.getBoundsAsRect();
rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height());
}
- outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect);
+ outputState.displaySpace.setContent(rotation.transform(orientedDisplaySpaceRect));
// Compute framebufferSpace
- outputState.framebufferSpace.orientation = orientation;
- LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT,
+ outputState.framebufferSpace.setOrientation(orientation);
+ LOG_FATAL_IF(outputState.framebufferSpace.getBoundsAsRect() == Rect::INVALID_RECT,
"The framebuffer bounds are unknown.");
- const auto scale =
- getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds);
- outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y);
+ const auto scale = getScale(outputState.displaySpace.getBoundsAsRect(),
+ outputState.framebufferSpace.getBoundsAsRect());
+ outputState.framebufferSpace.setContent(
+ outputState.displaySpace.getContent().scale(scale.x, scale.y));
// Compute layerStackSpace
- outputState.layerStackSpace.content = layerStackSpaceRect;
- outputState.layerStackSpace.bounds = layerStackSpaceRect;
+ outputState.layerStackSpace.setContent(layerStackSpaceRect);
+ outputState.layerStackSpace.setBounds(
+ ui::Size(layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight()));
outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace);
outputState.needsFiltering = outputState.transform.needsBilinearFiltering();
@@ -200,21 +203,21 @@
auto& state = editState();
// Update framebuffer space
- const Rect newBounds(size);
- state.framebufferSpace.bounds = newBounds;
+ const ui::Size newBounds(size);
+ state.framebufferSpace.setBounds(newBounds);
// Update display space
- state.displaySpace.bounds = newBounds;
+ state.displaySpace.setBounds(newBounds);
state.transform = state.layerStackSpace.getTransform(state.displaySpace);
// Update oriented display space
- const auto orientation = state.displaySpace.orientation;
+ const auto orientation = state.displaySpace.getOrientation();
ui::Size orientedSize = size;
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(orientedSize.width, orientedSize.height);
}
- const Rect newOrientedBounds(orientedSize);
- state.orientedDisplaySpace.bounds = newOrientedBounds;
+ const ui::Size newOrientedBounds(orientedSize);
+ state.orientedDisplaySpace.setBounds(newOrientedBounds);
if (mPlanner) {
mPlanner->setDisplaySize(size);
@@ -227,11 +230,8 @@
return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
}
-void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
- auto& outputState = editState();
- outputState.layerStackId = layerStackId;
- outputState.layerStackInternal = isInternal;
-
+void Output::setLayerFilter(ui::LayerFilter filter) {
+ editState().layerFilter = filter;
dirtyEntireOutput();
}
@@ -352,7 +352,7 @@
void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
mRenderSurface = std::move(surface);
const auto size = mRenderSurface->getSize();
- editState().framebufferSpace.bounds = Rect(size);
+ editState().framebufferSpace.setBounds(size);
if (mPlanner) {
mPlanner->setDisplaySize(size);
}
@@ -371,26 +371,18 @@
mRenderSurface = std::move(surface);
}
-Region Output::getDirtyRegion(bool repaintEverything) const {
+Region Output::getDirtyRegion() const {
const auto& outputState = getState();
- Region dirty(outputState.layerStackSpace.content);
- if (!repaintEverything) {
- dirty.andSelf(outputState.dirtyRegion);
- }
- return dirty;
+ return outputState.dirtyRegion.intersect(outputState.layerStackSpace.getContent());
}
-bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
- // The layerStackId's must match, and also the layer must not be internal
- // only when not on an internal output.
- const auto& outputState = getState();
- return layerStackId && (*layerStackId == outputState.layerStackId) &&
- (!internalOnly || outputState.layerStackInternal);
+bool Output::includesLayer(ui::LayerFilter filter) const {
+ return getState().layerFilter.includes(filter);
}
-bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+bool Output::includesLayer(const sp<LayerFE>& layerFE) const {
const auto* layerFEState = layerFE->getCompositionState();
- return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
+ return layerFEState && includesLayer(layerFEState->outputFilter);
}
std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
@@ -461,7 +453,7 @@
// Compute the resulting coverage for this output, and store it for later
const ui::Transform& tr = outputState.transform;
- Region undefinedRegion{outputState.displaySpace.bounds};
+ Region undefinedRegion{outputState.displaySpace.getBoundsAsRect()};
undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
outputState.undefinedRegion = undefinedRegion;
@@ -496,8 +488,8 @@
layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
}
- // Only consider the layers on the given layer stack
- if (!belongsInOutput(layerFE)) {
+ // Only consider the layers on this output
+ if (!includesLayer(layerFE)) {
return;
}
@@ -658,7 +650,7 @@
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
- drawRegion.andSelf(outputState.displaySpace.bounds);
+ drawRegion.andSelf(outputState.displaySpace.getBoundsAsRect());
if (drawRegion.isEmpty()) {
return;
}
@@ -676,7 +668,7 @@
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
- visibleNonShadowRegion.intersect(outputState.layerStackSpace.content));
+ visibleNonShadowRegion.intersect(outputState.layerStackSpace.getContent()));
outputLayerState.shadowRegion = shadowRegion;
}
@@ -911,7 +903,7 @@
void Output::beginFrame() {
auto& outputState = editState();
- const bool dirty = !getDirtyRegion(false).isEmpty();
+ const bool dirty = !getDirtyRegion().isEmpty();
const bool empty = getOutputLayerCount() == 0;
const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers;
@@ -963,14 +955,9 @@
}
if (getState().isEnabled) {
- // transform the dirty region into this screen's coordinate space
- const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
- if (!dirtyRegion.isEmpty()) {
- base::unique_fd readyFence;
- // redraw the whole screen
+ if (const auto dirtyRegion = getDirtyRegion(); !dirtyRegion.isEmpty()) {
static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
-
- mRenderSurface->queueBuffer(std::move(readyFence));
+ mRenderSurface->queueBuffer(base::unique_fd());
}
}
@@ -1049,19 +1036,18 @@
}
}
- base::unique_fd readyFence;
if (!hasClientComposition) {
setExpensiveRenderingExpected(false);
- return readyFence;
+ return base::unique_fd();
}
ALOGV("hasClientComposition");
renderengine::DisplaySettings clientCompositionDisplay;
- clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content;
- clientCompositionDisplay.clip = outputState.layerStackSpace.content;
+ clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.getContent();
+ clientCompositionDisplay.clip = outputState.layerStackSpace.getContent();
clientCompositionDisplay.orientation =
- ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+ ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
? outputState.dataspace
: ui::Dataspace::UNKNOWN;
@@ -1071,21 +1057,19 @@
clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
? outputState.displayBrightnessNits
: mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
+ clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
// Compute the global color transform matrix.
if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
}
- // Note: Updated by generateClientCompositionRequests
- clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
-
// Generate the client composition requests for the layers on this output.
+ std::vector<LayerFE*> clientCompositionLayersFE;
std::vector<LayerFE::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
- clientCompositionDisplay.clearRegion,
- clientCompositionDisplay.outputDataspace);
+ clientCompositionDisplay.outputDataspace,
+ clientCompositionLayersFE);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
// Check if the client composition requests were rendered into the provided graphic buffer. If
@@ -1096,7 +1080,7 @@
clientCompositionLayers)) {
outputCompositionState.reusedClientComposition = true;
setExpensiveRenderingExpected(false);
- return readyFence;
+ return base::unique_fd();
}
mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay,
clientCompositionLayers);
@@ -1114,12 +1098,12 @@
setExpensiveRenderingExpected(true);
}
- std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
- clientCompositionLayerPointers.reserve(clientCompositionLayers.size());
+ std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
+ clientRenderEngineLayers.reserve(clientCompositionLayers.size());
std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
- std::back_inserter(clientCompositionLayerPointers),
- [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings* {
- return &settings;
+ std::back_inserter(clientRenderEngineLayers),
+ [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings {
+ return settings;
});
const nsecs_t renderEngineStart = systemTime();
@@ -1129,10 +1113,12 @@
// 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.layerStackInternal;
- status_t status =
- renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, tex,
- useFramebufferCache, std::move(fd), &readyFence);
+ const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay;
+ auto [status, drawFence] =
+ renderEngine
+ .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex,
+ useFramebufferCache, std::move(fd))
+ .get();
if (status != NO_ERROR && mClientCompositionRequestCache) {
// If rendering was not successful, remove the request from the cache.
@@ -1140,27 +1126,32 @@
}
auto& timeStats = getCompositionEngine().getTimeStats();
- if (readyFence.get() < 0) {
+ if (drawFence.get() < 0) {
timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
} else {
timeStats.recordRenderEngineDuration(renderEngineStart,
std::make_shared<FenceTime>(
- new Fence(dup(readyFence.get()))));
+ new Fence(dup(drawFence.get()))));
}
- return readyFence;
+ if (clientCompositionLayersFE.size() > 0) {
+ sp<Fence> clientCompFence = new Fence(dup(drawFence.get()));
+ for (auto clientComposedLayer : clientCompositionLayersFE) {
+ clientComposedLayer->setWasClientComposed(clientCompFence);
+ }
+ }
+
+ return std::move(drawFence);
}
std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
+ bool supportsProtectedContent, ui::Dataspace outputDataspace, std::vector<LayerFE*>& outLayerFEs) {
std::vector<LayerFE::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
const auto& outputState = getState();
- const Region viewportRegion(outputState.layerStackSpace.content);
+ const Region viewportRegion(outputState.layerStackSpace.getContent());
bool firstLayer = true;
- // Used when a layer clears part of the buffer.
- Region stubRegion;
bool disableBlurs = false;
sp<GraphicBuffer> previousOverrideBuffer = nullptr;
@@ -1222,18 +1213,19 @@
outputState.needsFiltering,
.isSecure = outputState.isSecure,
.supportsProtectedContent = supportsProtectedContent,
- .clearRegion = clientComposition ? clearRegion : stubRegion,
- .viewport = outputState.layerStackSpace.content,
+ .viewport = outputState.layerStackSpace.getContent(),
.dataspace = outputDataspace,
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
- .blurSetting = blurSetting};
+ .blurSetting = blurSetting,
+ .whitePointNits = layerState.whitePointNits};
results = layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
layer->editState().clientCompositionTimestamp = systemTime();
}
}
+ outLayerFEs.push_back(&layerFE);
clientCompositionLayers.insert(clientCompositionLayers.end(),
std::make_move_iterator(results.begin()),
std::make_move_iterator(results.end()));
@@ -1305,8 +1297,10 @@
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
-
- layer->getLayerFE().onLayerDisplayed(releaseFence);
+ layer->getLayerFE().onLayerDisplayed(
+ ftl::yield<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd(releaseFence->dup())})
+ .share());
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1314,7 +1308,9 @@
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (auto layer = weakLayer.promote(); layer != nullptr) {
- layer->onLayerDisplayed(frame.presentFence);
+ layer->onLayerDisplayed(ftl::yield<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd(frame.presentFence->dup())})
+ .share());
}
}
@@ -1324,13 +1320,13 @@
void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) {
if (mPlanner) {
- mPlanner->renderCachedSets(getState(), refreshArgs.nextInvalidateTime);
+ mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime);
}
}
void Output::dirtyEntireOutput() {
auto& outputState = editState();
- outputState.dirtyRegion.set(outputState.displaySpace.bounds);
+ outputState.dirtyRegion.set(outputState.displaySpace.getBoundsAsRect());
}
void Output::chooseCompositionStrategy() {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index ee30ad8..acc9216 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -28,9 +28,7 @@
dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
dumpVal(out, "flipClientTarget", flipClientTarget);
dumpVal(out, "reusedClientComposition", reusedClientComposition);
-
- dumpVal(out, "layerStack", layerStackId);
- dumpVal(out, "layerStackInternal", layerStackInternal);
+ dumpVal(out, "layerFilter", layerFilter);
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 56e9d27..e6bcec8 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,9 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cstdint>
+#include "system/graphics-base-v1.0.h"
+
+#include <ui/DataspaceUtils.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -34,6 +37,8 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
OutputLayer::~OutputLayer() = default;
@@ -79,7 +84,7 @@
FloatRect activeCropFloat =
reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
- const Rect& viewport = getOutput().getState().layerStackSpace.content;
+ const Rect& viewport = getOutput().getState().layerStackSpace.getContent();
const ui::Transform& layerTransform = layerState.geomLayerTransform;
const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
// Transform to screen space.
@@ -136,7 +141,7 @@
* buffer
*/
uint32_t invTransformOrient =
- ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
+ ui::Transform::toRotationFlags(outputState.displaySpace.getOrientation());
// calculate the inverse transform
if (invTransformOrient & HAL_TRANSFORM_ROT_90) {
invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -192,7 +197,7 @@
Rect activeCrop = layerState.geomCrop;
if (!activeCrop.isEmpty() && bufferSize.isValid()) {
activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) {
+ if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
activeCrop.clear();
}
activeCrop = inverseLayerTransform.transform(activeCrop, true);
@@ -228,7 +233,7 @@
geomLayerBounds.bottom += outset;
}
Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.layerStackSpace.content, &frame)) {
+ if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
frame.clear();
}
const ui::Transform displayTransform{outputState.transform};
@@ -317,6 +322,14 @@
? outputState.targetDataspace
: layerFEState->dataspace;
+ // For hdr content, treat the white point as the display brightness - HDR content should not be
+ // boosted or dimmed.
+ if (isHdrDataspace(state.dataspace)) {
+ state.whitePointNits = getOutput().getState().displayBrightnessNits;
+ } else {
+ state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+ }
+
// These are evaluated every frame as they can potentially change at any
// time.
if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -347,6 +360,10 @@
auto requestedCompositionType = outputIndependentState->compositionType;
+ if (requestedCompositionType == Composition::SOLID_COLOR && state.overrideInfo.buffer) {
+ requestedCompositionType = Composition::DEVICE;
+ }
+
// TODO(b/181172795): We now update geometry for all flattened layers. We should update it
// only when the geometry actually changes
const bool isOverridden =
@@ -359,20 +376,22 @@
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
- writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer);
+ writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState,
+ requestedCompositionType, skipLayer);
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
skipLayer);
- // Always set the layer color after setting the composition type.
- writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+ if (requestedCompositionType == Composition::SOLID_COLOR) {
+ writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+ }
editState().hwc->stateOverridden = isOverridden;
editState().hwc->layerSkipped = skipLayer;
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType,
+ Composition requestedCompositionType,
uint32_t z) {
const auto& outputDependentState = getState();
@@ -381,12 +400,7 @@
if (outputDependentState.overrideInfo.buffer != nullptr) {
displayFrame = outputDependentState.overrideInfo.displayFrame;
- sourceCrop =
- FloatRect(0.f, 0.f,
- static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
- ->getWidth()),
- static_cast<float>(outputDependentState.overrideInfo.buffer->getBuffer()
- ->getHeight()));
+ sourceCrop = displayFrame.toFloatRect();
}
ALOGV("Writing display frame [%d, %d, %d, %d]", displayFrame.left, displayFrame.top,
@@ -411,7 +425,7 @@
}
// Solid-color layers and overridden buffers should always use an identity transform.
- const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+ const auto bufferTransform = (requestedCompositionType != Composition::SOLID_COLOR &&
getState().overrideInfo.buffer == nullptr)
? outputDependentState.bufferTransform
: static_cast<hal::Transform>(0);
@@ -478,11 +492,21 @@
ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
+
+ // Don't dim cached layers
+ const auto whitePointNits = outputDependentState.overrideInfo.buffer
+ ? getOutput().getState().displayBrightnessNits
+ : outputDependentState.whitePointNits;
+
+ if (auto error = hwcLayer->setWhitePointNits(whitePointNits); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set white point %f: %s (%d)", getLayerFE().getDebugName(),
+ whitePointNits, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
}
void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
- bool skipLayer) {
+ Composition compositionType, bool skipLayer) {
switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
case hal::Error::NONE:
break;
@@ -506,19 +530,19 @@
}
// Content-specific per-frame state
- switch (outputIndependentState.compositionType) {
- case hal::Composition::SOLID_COLOR:
+ switch (compositionType) {
+ case Composition::SOLID_COLOR:
// For compatibility, should be written AFTER the composition type.
break;
- case hal::Composition::SIDEBAND:
+ case Composition::SIDEBAND:
writeSidebandStateToHWC(hwcLayer, outputIndependentState);
break;
- case hal::Composition::CURSOR:
- case hal::Composition::DEVICE:
+ case Composition::CURSOR:
+ case Composition::DEVICE:
writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
break;
- case hal::Composition::INVALID:
- case hal::Composition::CLIENT:
+ case Composition::INVALID:
+ case Composition::CLIENT:
// Ignored
break;
}
@@ -526,10 +550,6 @@
void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
- if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) {
- return;
- }
-
hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
@@ -588,13 +608,13 @@
}
void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType,
+ Composition requestedCompositionType,
bool isPeekingThrough, bool skipLayer) {
auto& outputDependentState = editState();
if (isClientCompositionForced(isPeekingThrough)) {
// If we are forcing client composition, we need to tell the HWC
- requestedCompositionType = hal::Composition::CLIENT;
+ requestedCompositionType = Composition::CLIENT;
}
// Set the requested composition type with the HWC whenever it changes
@@ -607,7 +627,7 @@
if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
- toString(requestedCompositionType).c_str(), to_string(error).c_str(),
+ to_string(requestedCompositionType).c_str(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
}
@@ -628,7 +648,7 @@
const auto& outputState = getOutput().getState();
Rect frame = layerFEState->cursorFrame;
- frame.intersect(outputState.layerStackSpace.content, &frame);
+ frame.intersect(outputState.layerStackSpace.getContent(), &frame);
Rect position = outputState.transform.transform(frame);
if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
@@ -646,38 +666,37 @@
bool OutputLayer::requiresClientComposition() const {
const auto& state = getState();
- return !state.hwc || state.hwc->hwcCompositionType == hal::Composition::CLIENT;
+ return !state.hwc || state.hwc->hwcCompositionType == Composition::CLIENT;
}
bool OutputLayer::isHardwareCursor() const {
const auto& state = getState();
- return state.hwc && state.hwc->hwcCompositionType == hal::Composition::CURSOR;
+ return state.hwc && state.hwc->hwcCompositionType == Composition::CURSOR;
}
-void OutputLayer::detectDisallowedCompositionTypeChange(hal::Composition from,
- hal::Composition to) const {
+void OutputLayer::detectDisallowedCompositionTypeChange(Composition from, Composition to) const {
bool result = false;
switch (from) {
- case hal::Composition::INVALID:
- case hal::Composition::CLIENT:
+ case Composition::INVALID:
+ case Composition::CLIENT:
result = false;
break;
- case hal::Composition::DEVICE:
- case hal::Composition::SOLID_COLOR:
- result = (to == hal::Composition::CLIENT);
+ case Composition::DEVICE:
+ case Composition::SOLID_COLOR:
+ result = (to == Composition::CLIENT);
break;
- case hal::Composition::CURSOR:
- case hal::Composition::SIDEBAND:
- result = (to == hal::Composition::CLIENT || to == hal::Composition::DEVICE);
+ case Composition::CURSOR:
+ case Composition::SIDEBAND:
+ result = (to == Composition::CLIENT || to == Composition::DEVICE);
break;
}
if (!result) {
ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
- getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
- toString(to).c_str(), static_cast<int>(to));
+ getLayerFE().getDebugName(), to_string(from).c_str(), static_cast<int>(from),
+ to_string(to).c_str(), static_cast<int>(to));
}
}
@@ -686,7 +705,7 @@
(!isPeekingThrough && getLayerFE().hasRoundedCorners());
}
-void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
+void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
auto& hwcState = *state.hwc;
@@ -737,7 +756,7 @@
// framebuffer space of the override buffer to layer space.
const ProjectionSpace& layerSpace = getOutput().getState().layerStackSpace;
const ui::Transform transform = getState().overrideInfo.displaySpace.getTransform(layerSpace);
- const Rect boundaries = transform.transform(getState().overrideInfo.displayFrame);
+ const Rect boundaries = transform.transform(getState().overrideInfo.displaySpace.getContent());
LayerFE::LayerSettings settings;
settings.geometry = renderengine::Geometry{
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index ef50870..a19d23f 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -127,19 +127,18 @@
}
void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) {
- DisplaySurface::CompositionType compositionType;
- if (usesClientComposition && usesDeviceComposition) {
- compositionType = DisplaySurface::COMPOSITION_MIXED;
- } else if (usesClientComposition) {
- compositionType = DisplaySurface::COMPOSITION_GPU;
- } else if (usesDeviceComposition) {
- compositionType = DisplaySurface::COMPOSITION_HWC;
- } else {
+ const auto compositionType = [=] {
+ using CompositionType = DisplaySurface::CompositionType;
+
+ if (usesClientComposition && usesDeviceComposition) return CompositionType::Mixed;
+ if (usesClientComposition) return CompositionType::Gpu;
+ if (usesDeviceComposition) return CompositionType::Hwc;
+
// Nothing to do -- when turning the screen off we get a frame like
// this. Call it a HWC frame since we won't be doing any GPU work but
// will do a prepare/set cycle.
- compositionType = DisplaySurface::COMPOSITION_HWC;
- }
+ return CompositionType::Hwc;
+ }();
if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) {
ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result,
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index c1cd5ab..2203f22 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -25,6 +25,7 @@
#include <math/HashCombine.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
#include <utils/Trace.h>
#include <utils/Trace.h>
@@ -159,30 +160,30 @@
void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool,
const OutputCompositionState& outputState) {
ATRACE_CALL();
- const Rect& viewport = outputState.layerStackSpace.content;
+ const Rect& viewport = outputState.layerStackSpace.getContent();
const ui::Dataspace& outputDataspace = outputState.dataspace;
const ui::Transform::RotationFlags orientation =
- ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation);
+ ui::Transform::toRotationFlags(outputState.framebufferSpace.getOrientation());
renderengine::DisplaySettings displaySettings{
- .physicalDisplay = outputState.framebufferSpace.content,
+ .physicalDisplay = outputState.framebufferSpace.getContent(),
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
+ .targetLuminanceNits = outputState.displayBrightnessNits,
};
- Region clearRegion = Region::INVALID_REGION;
LayerFE::ClientCompositionTargetSettings targetSettings{
.clip = Region(viewport),
.needsFiltering = false,
.isSecure = outputState.isSecure,
.supportsProtectedContent = false,
- .clearRegion = clearRegion,
.viewport = viewport,
.dataspace = outputDataspace,
.realContentIsVisible = true,
.clearContent = false,
.blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ .whitePointNits = outputState.displayBrightnessNits,
};
std::vector<renderengine::LayerSettings> layerSettings;
@@ -195,11 +196,6 @@
clientCompositionList.cend());
}
- std::vector<const renderengine::LayerSettings*> layerSettingsPointers;
- std::transform(layerSettings.cbegin(), layerSettings.cend(),
- std::back_inserter(layerSettingsPointers),
- [](const renderengine::LayerSettings& settings) { return &settings; });
-
renderengine::LayerSettings blurLayerSettings;
if (mBlurLayer) {
auto blurSettings = targetSettings;
@@ -214,7 +210,7 @@
blurLayerSettings.name = std::string("blur layer");
// Clear out the shadow settings
blurLayerSettings.shadow = {};
- layerSettingsPointers.push_back(&blurLayerSettings);
+ layerSettings.push_back(blurLayerSettings);
}
renderengine::LayerSettings holePunchSettings;
@@ -232,7 +228,7 @@
holePunchSettings.disableBlending = true;
holePunchSettings.alpha = 0.0f;
holePunchSettings.name = std::string("hole punch layer");
- layerSettingsPointers.push_back(&holePunchSettings);
+ layerSettings.push_back(holePunchSettings);
// Add a solid background as the first layer in case there is no opaque
// buffer behind the punch hole
@@ -241,7 +237,7 @@
holePunchBackgroundSettings.geometry.boundaries = holePunchSettings.geometry.boundaries;
holePunchBackgroundSettings.geometry.positionTransform =
holePunchSettings.geometry.positionTransform;
- layerSettingsPointers.insert(layerSettingsPointers.begin(), &holePunchBackgroundSettings);
+ layerSettings.emplace(layerSettings.begin(), holePunchBackgroundSettings);
}
if (sDebugHighlighLayers) {
@@ -259,7 +255,7 @@
.alpha = half(0.05f),
};
- layerSettingsPointers.emplace_back(&highlight);
+ layerSettings.emplace_back(highlight);
}
auto texture = texturePool.borrowTexture();
@@ -274,17 +270,17 @@
bufferFence.reset(texture->getReadyFence()->dup());
}
- base::unique_fd drawFence;
- status_t result =
- renderEngine.drawLayers(displaySettings, layerSettingsPointers, texture->get(), false,
- std::move(bufferFence), &drawFence);
+ auto [status, drawFence] = renderEngine
+ .drawLayers(displaySettings, layerSettings, texture->get(),
+ false, std::move(bufferFence))
+ .get();
- if (result == NO_ERROR) {
+ if (status == NO_ERROR) {
mDrawFence = new Fence(drawFence.release());
mOutputSpace = outputState.framebufferSpace;
mTexture = texture;
mTexture->setReadyFence(mDrawFence);
- mOutputSpace.orientation = outputState.framebufferSpace.orientation;
+ mOutputSpace.setOrientation(outputState.framebufferSpace.getOrientation());
mOutputDataspace = outputDataspace;
mOrientation = orientation;
mSkipCount = 0;
@@ -398,20 +394,29 @@
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
- base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
- base::StringAppendF(&result, " Protected [%s]",
+ if (auto* buffer = mLayers[0].getBuffer().get()) {
+ base::StringAppendF(&result, " Buffer %p", buffer);
+ base::StringAppendF(&result, " Format %s",
+ decodePixelFormat(buffer->getPixelFormat()).c_str());
+ }
+ base::StringAppendF(&result, " Protected [%s]\n",
mLayers[0].getState()->isProtected() ? "true" : "false");
} else {
- result.append(" Cached set of:");
+ result.append(" Cached set of:\n");
for (const Layer& layer : mLayers) {
- base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
- base::StringAppendF(&result, "\n Protected [%s]",
+ base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
+ if (auto* buffer = layer.getBuffer().get()) {
+ base::StringAppendF(&result, " Buffer %p", buffer);
+ base::StringAppendF(&result, " Format[%s]",
+ decodePixelFormat(buffer->getPixelFormat()).c_str());
+ }
+ base::StringAppendF(&result, " Protected [%s]\n",
layer.getState()->isProtected() ? "true" : "false");
}
}
- base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
- base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+ base::StringAppendF(&result, " Creation cost: %zd\n", getCreationCost());
+ base::StringAppendF(&result, " Display cost: %zd\n", getDisplayCost());
}
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index ad5e931..0918510 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -211,7 +211,8 @@
displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
layer->getDisplayFrame().height());
- hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ hasClientComposition |= layer->getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT;
}
if (hasClientComposition) {
@@ -315,7 +316,7 @@
state.overrideInfo = {
.buffer = mNewCachedSet->getBuffer(),
.acquireFence = mNewCachedSet->getDrawFence(),
- .displayFrame = mNewCachedSet->getTextureBounds(),
+ .displayFrame = mNewCachedSet->getBounds(),
.dataspace = mNewCachedSet->getOutputDataspace(),
.displaySpace = mNewCachedSet->getOutputSpace(),
.damageRegion = Region::INVALID_REGION,
@@ -355,7 +356,7 @@
state.overrideInfo = {
.buffer = currentLayerIter->getBuffer(),
.acquireFence = currentLayerIter->getDrawFence(),
- .displayFrame = currentLayerIter->getTextureBounds(),
+ .displayFrame = currentLayerIter->getBounds(),
.dataspace = currentLayerIter->getOutputDataspace(),
.displaySpace = currentLayerIter->getOutputSpace(),
.damageRegion = Region(),
@@ -409,25 +410,29 @@
bool runHasFirstLayer = false;
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
- const bool layerIsInactive =
- now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
+ bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
+ // Layers should also be considered inactive whenever their framerate is lower than 1fps.
+ if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
+ auto layerFps = currentSet->getFirstLayer().getState()->getFps();
+ if (layerFps > 0 && layerFps <= kFpsActiveThreshold) {
+ ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f",
+ currentSet->getFirstLayer().getName().c_str(), layerFps);
+ layerIsInactive = true;
+ }
+ }
+
if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
!currentSet->hasUnsupportedDataspace()) {
if (isPartOfRun) {
- builder.append(currentSet->getLayerCount());
+ builder.increment();
} else {
- // Runs can't start with a non-buffer layer
- if (currentSet->getFirstLayer().getBuffer() == nullptr) {
- ALOGV("[%s] Skipping initial non-buffer layer", __func__);
- } else {
- builder.init(currentSet);
- if (firstLayer) {
- runHasFirstLayer = true;
- }
- isPartOfRun = true;
+ builder.init(currentSet);
+ if (firstLayer) {
+ runHasFirstLayer = true;
}
+ isPartOfRun = true;
}
} else if (isPartOfRun) {
builder.setHolePunchCandidate(&(*currentSet));
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 936dba3..c79ca0d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -93,11 +93,7 @@
void LayerState::dump(std::string& result) const {
for (const StateInterface* field : getNonUniqueFields()) {
- if (auto viewOpt = flag_name(field->getField()); viewOpt) {
- base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
- } else {
- result.append("<UNKNOWN FIELD>:\n");
- }
+ base::StringAppendF(&result, " %16s: ", ftl::flag_string(field->getField()).c_str());
bool first = true;
for (const std::string& line : field->toStrings()) {
@@ -126,11 +122,7 @@
continue;
}
- if (auto viewOpt = flag_name(thisField->getField()); viewOpt) {
- base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str());
- } else {
- result.append("<UNKNOWN FIELD>:\n");
- }
+ base::StringAppendF(&result, " %16s: ", ftl::flag_string(thisField->getField()).c_str());
const auto& thisStrings = thisField->toStrings();
const auto& otherStrings = otherField->toStrings();
@@ -168,7 +160,8 @@
lhs.mColorTransform == rhs.mColorTransform &&
lhs.mCompositionType == rhs.mCompositionType &&
lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
- (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ (lhs.mCompositionType.get() !=
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR ||
lhs.mSolidColor == rhs.mSolidColor);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index f5b1cee..74d2701 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -193,7 +193,7 @@
finalPlan.addLayerType(
forcedOrRequestedClient
- ? hardware::graphics::composer::hal::Composition::CLIENT
+ ? aidl::android::hardware::graphics::composer3::Composition::CLIENT
: layer->getLayerFE().getCompositionState()->compositionType);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 8226ef7..074673e 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -39,8 +39,10 @@
// Skip layers where both are client-composited, since that doesn't change the
// composition plan
- if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
- other[i]->getCompositionType() == hal::Composition::CLIENT) {
+ if (mLayers[i].getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT &&
+ other[i]->getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT) {
continue;
}
@@ -89,22 +91,28 @@
for (char c : string) {
switch (c) {
case 'C':
- plan.addLayerType(hal::Composition::CLIENT);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT);
continue;
case 'U':
- plan.addLayerType(hal::Composition::CURSOR);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR);
continue;
case 'D':
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE);
continue;
case 'I':
- plan.addLayerType(hal::Composition::INVALID);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::INVALID);
continue;
case 'B':
- plan.addLayerType(hal::Composition::SIDEBAND);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND);
continue;
case 'S':
- plan.addLayerType(hal::Composition::SOLID_COLOR);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR);
continue;
default:
return std::nullopt;
@@ -117,22 +125,22 @@
std::string result;
for (auto type : plan.mLayerTypes) {
switch (type) {
- case hal::Composition::CLIENT:
+ case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
result.append("C");
break;
- case hal::Composition::CURSOR:
+ case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
result.append("U");
break;
- case hal::Composition::DEVICE:
+ case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
result.append("D");
break;
- case hal::Composition::INVALID:
+ case aidl::android::hardware::graphics::composer3::Composition::INVALID:
result.append("I");
break;
- case hal::Composition::SIDEBAND:
+ case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
result.append("B");
break;
- case hal::Composition::SOLID_COLOR:
+ case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
result.append("S");
break;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index c037cc6..3bdb2c0 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -38,6 +38,10 @@
#include "MockHWComposer.h"
#include "MockPowerAdvisor.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
namespace {
@@ -60,8 +64,7 @@
constexpr HalVirtualDisplayId HAL_VIRTUAL_DISPLAY_ID{456u};
constexpr GpuVirtualDisplayId GPU_VIRTUAL_DISPLAY_ID{789u};
-const ui::Size DEFAULT_RESOLUTION{1920, 1080};
-constexpr uint32_t DEFAULT_LAYER_STACK = 42;
+constexpr ui::Size DEFAULT_RESOLUTION{1920, 1080};
struct Layer {
Layer() {
@@ -161,13 +164,11 @@
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
}
- DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+ DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() {
return DisplayCreationArgsBuilder()
.setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(ui::DisplayConnectionType::Internal)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(true)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.build();
}
@@ -177,7 +178,6 @@
.setId(GPU_VIRTUAL_DISPLAY_ID)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.build();
}
@@ -193,14 +193,13 @@
using Display = DisplayTestCommon::PartialMockDisplay;
std::shared_ptr<Display> mDisplay =
createPartialMockDisplay<Display>(mCompositionEngine,
- getDisplayCreationArgsForPhysicalHWCDisplay());
+ getDisplayCreationArgsForPhysicalDisplay());
};
struct FullDisplayImplTestCommon : public DisplayTestCommon {
using Display = DisplayTestCommon::FullImplDisplay;
std::shared_ptr<Display> mDisplay =
- createDisplay<Display>(mCompositionEngine,
- getDisplayCreationArgsForPhysicalHWCDisplay());
+ createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
};
struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
@@ -218,8 +217,7 @@
LayerNoHWC2Layer mLayer3;
StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
std::shared_ptr<Display> mDisplay =
- createDisplay<Display>(mCompositionEngine,
- getDisplayCreationArgsForPhysicalHWCDisplay());
+ createDisplay<Display>(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
};
/*
@@ -232,7 +230,7 @@
TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
auto display =
- impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+ impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalDisplay());
EXPECT_TRUE(display->isSecure());
EXPECT_FALSE(display->isVirtual());
EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
@@ -252,13 +250,11 @@
using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
-TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+TEST_F(DisplaySetConfigurationTest, configuresPhysicalDisplay) {
mDisplay->setConfiguration(DisplayCreationArgsBuilder()
.setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(ui::DisplayConnectionType::Internal)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(true)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.setName(getDisplayNameFromCurrentTest())
.build());
@@ -266,28 +262,11 @@
EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
EXPECT_TRUE(mDisplay->isSecure());
EXPECT_FALSE(mDisplay->isVirtual());
- EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
- EXPECT_TRUE(mDisplay->getState().layerStackInternal);
EXPECT_FALSE(mDisplay->isValid());
-}
-TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
- mDisplay->setConfiguration(DisplayCreationArgsBuilder()
- .setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(ui::DisplayConnectionType::External)
- .setPixels(DEFAULT_RESOLUTION)
- .setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
- .setPowerAdvisor(&mPowerAdvisor)
- .setName(getDisplayNameFromCurrentTest())
- .build());
-
- EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
- EXPECT_FALSE(mDisplay->isSecure());
- EXPECT_FALSE(mDisplay->isVirtual());
- EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
- EXPECT_FALSE(mDisplay->getState().layerStackInternal);
- EXPECT_FALSE(mDisplay->isValid());
+ const auto& filter = mDisplay->getState().layerFilter;
+ EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+ EXPECT_FALSE(filter.toInternalDisplay);
}
TEST_F(DisplaySetConfigurationTest, configuresHalVirtualDisplay) {
@@ -295,7 +274,6 @@
.setId(HAL_VIRTUAL_DISPLAY_ID)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.setName(getDisplayNameFromCurrentTest())
.build());
@@ -303,9 +281,11 @@
EXPECT_EQ(HAL_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
- EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
- EXPECT_FALSE(mDisplay->getState().layerStackInternal);
EXPECT_FALSE(mDisplay->isValid());
+
+ const auto& filter = mDisplay->getState().layerFilter;
+ EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+ EXPECT_FALSE(filter.toInternalDisplay);
}
TEST_F(DisplaySetConfigurationTest, configuresGpuVirtualDisplay) {
@@ -313,7 +293,6 @@
.setId(GPU_VIRTUAL_DISPLAY_ID)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(false)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.setName(getDisplayNameFromCurrentTest())
.build());
@@ -321,9 +300,11 @@
EXPECT_EQ(GPU_VIRTUAL_DISPLAY_ID, mDisplay->getId());
EXPECT_FALSE(mDisplay->isSecure());
EXPECT_TRUE(mDisplay->isVirtual());
- EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
- EXPECT_FALSE(mDisplay->getState().layerStackInternal);
EXPECT_FALSE(mDisplay->isValid());
+
+ const auto& filter = mDisplay->getState().layerFilter;
+ EXPECT_EQ(ui::INVALID_LAYER_STACK, filter.layerStack);
+ EXPECT_FALSE(filter.toInternalDisplay);
}
/*
@@ -615,10 +596,11 @@
TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
android::HWComposer::DeviceRequestedChanges changes{
- {{nullptr, hal::Composition::CLIENT}},
+ {{nullptr, Composition::CLIENT}},
hal::DisplayRequest::FLIP_CLIENT_TARGET,
{{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
{hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+ -1.f,
};
// Since two calls are made to anyLayersRequireClientComposition with different return
@@ -722,17 +704,15 @@
}
TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
- EXPECT_CALL(*mLayer1.outputLayer,
- applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
+ EXPECT_CALL(*mLayer1.outputLayer, applyDeviceCompositionTypeChange(Composition::CLIENT))
.Times(1);
- EXPECT_CALL(*mLayer2.outputLayer,
- applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
+ EXPECT_CALL(*mLayer2.outputLayer, applyDeviceCompositionTypeChange(Composition::DEVICE))
.Times(1);
mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
- {&mLayer1.hwc2Layer, hal::Composition::CLIENT},
- {&mLayer2.hwc2Layer, hal::Composition::DEVICE},
- {&hwc2LayerUnknown, hal::Composition::SOLID_COLOR},
+ {&mLayer1.hwc2Layer, Composition::CLIENT},
+ {&mLayer2.hwc2Layer, Composition::DEVICE},
+ {&hwc2LayerUnknown, Composition::SOLID_COLOR},
});
}
@@ -811,15 +791,18 @@
.dataspace = hal::Dataspace::STANDARD_BT470M,
};
+ static constexpr float kWhitePointNits = 800.f;
+
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
- mDisplay->applyClientTargetRequests(clientTargetProperty);
+ mDisplay->applyClientTargetRequests(clientTargetProperty, kWhitePointNits);
auto& state = mDisplay->getState();
EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+ EXPECT_EQ(kWhitePointNits, state.clientTargetWhitePointNits);
}
/*
@@ -899,13 +882,10 @@
mDisplay->editState().isEnabled = true;
mDisplay->editState().usesClientComposition = false;
- mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- CompositionRefreshArgs refreshArgs;
- refreshArgs.repaintEverything = false;
-
- mDisplay->finishFrame(refreshArgs);
+ mDisplay->finishFrame({});
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -920,13 +900,10 @@
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
- gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- CompositionRefreshArgs refreshArgs;
- refreshArgs.repaintEverything = false;
-
- gpuDisplay->finishFrame(refreshArgs);
+ gpuDisplay->finishFrame({});
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
@@ -941,34 +918,9 @@
gpuDisplay->editState().isEnabled = true;
gpuDisplay->editState().usesClientComposition = false;
- gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
+ gpuDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
gpuDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
-
- CompositionRefreshArgs refreshArgs;
- refreshArgs.repaintEverything = false;
-
- gpuDisplay->finishFrame(refreshArgs);
-}
-
-TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
- auto args = getDisplayCreationArgsForGpuVirtualDisplay();
- std::shared_ptr<impl::Display> gpuDisplay = impl::createDisplay(mCompositionEngine, args);
-
- mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
- gpuDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
-
- // We expect a single call to queueBuffer when composition is not skipped.
- EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
-
- gpuDisplay->editState().isEnabled = true;
- gpuDisplay->editState().usesClientComposition = false;
- gpuDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1);
- gpuDisplay->editState().dirtyRegion = Region::INVALID_REGION;
-
- CompositionRefreshArgs refreshArgs;
- refreshArgs.repaintEverything = true;
-
- gpuDisplay->finishFrame(refreshArgs);
+ gpuDisplay->finishFrame({});
}
/*
@@ -998,10 +950,8 @@
Display>(mCompositionEngine,
DisplayCreationArgsBuilder()
.setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(ui::DisplayConnectionType::Internal)
.setPixels(DEFAULT_RESOLUTION)
.setIsSecure(true)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&mPowerAdvisor)
.build());
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 9518659..ff68053 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -32,6 +32,8 @@
#include <ui/GraphicTypes.h>
#include "DisplayHardware/HWC2.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -57,7 +59,8 @@
MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
MOCK_METHOD1(setColor, Error(hal::Color));
- MOCK_METHOD1(setCompositionType, Error(hal::Composition));
+ MOCK_METHOD1(setCompositionType,
+ Error(aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
@@ -71,6 +74,7 @@
MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
MOCK_METHOD3(setLayerGenericMetadata,
Error(const std::string&, bool, const std::vector<uint8_t>&));
+ MOCK_METHOD1(setWhitePointNits, Error(float));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index ada5adf..224dad1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -49,6 +49,7 @@
MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
MOCK_METHOD5(getDeviceCompositionChanges,
status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
@@ -110,11 +111,15 @@
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
- MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
- MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
- MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
- MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId));
- MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId));
+
+ MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
+ MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override));
+ MOCK_METHOD(bool, isHeadless, (), (const, override));
+
+ MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId),
+ (const, override));
+ MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId),
+ (const, override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index fc8cb50..db4151b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,11 +29,21 @@
PowerAdvisor();
~PowerAdvisor() override;
- MOCK_METHOD0(init, void());
- MOCK_METHOD0(onBootFinished, void());
- MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
- MOCK_METHOD0(isUsingExpensiveRendering, bool());
- MOCK_METHOD0(notifyDisplayUpdateImminent, void());
+ MOCK_METHOD(void, init, (), (override));
+ MOCK_METHOD(void, onBootFinished, (), (override));
+ MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
+ (override));
+ MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(bool, usePowerHintSession, (), (override));
+ MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
+ MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+ (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
+ (override));
+ MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c8c6012..ad7976f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -30,6 +30,10 @@
#include "MockHWComposer.h"
#include "RegionMatcher.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
namespace {
@@ -158,7 +162,7 @@
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferTransform = TR_IDENT;
- mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
}
FloatRect calculateOutputSourceCrop() {
@@ -229,7 +233,7 @@
mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
@@ -243,7 +247,7 @@
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
- mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
EXPECT_THAT(calculateOutputSourceCrop(), expected);
@@ -265,7 +269,7 @@
mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
- mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
mOutputState.transform = ui::Transform{TR_IDENT};
}
@@ -313,7 +317,7 @@
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
- mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540};
+ mOutputState.layerStackSpace.setContent(Rect{0, 0, 960, 540});
const Rect expected{0, 0, 960, 540};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -399,7 +403,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
@@ -511,7 +515,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
- mOutputState.displaySpace.orientation = toRotation(entry.display);
+ mOutputState.displaySpace.setOrientation(toRotation(entry.display));
mOutputState.transform = ui::Transform{entry.display};
const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
@@ -652,6 +656,22 @@
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+ mOutputState.sdrWhitePointNits = 200.f;
+ mOutputState.displayBrightnessNits = 800.f;
+
+ mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+ EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+
+ mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+}
+
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
mOutputLayer.editState().forceClientComposition = false;
@@ -728,6 +748,8 @@
static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
+ static constexpr float kWhitePointNits = 200.f;
+ static constexpr float kDisplayBrightnessNits = 400.f;
static const half4 kColor;
static const Rect kDisplayFrame;
@@ -758,6 +780,7 @@
outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
outputLayerState.dataspace = kDataspace;
+ outputLayerState.whitePointNits = kWhitePointNits;
mLayerFEState.blendMode = kBlendMode;
mLayerFEState.alpha = kAlpha;
@@ -770,6 +793,8 @@
mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
+ mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+
EXPECT_CALL(mOutput, getDisplayColorProfile())
.WillRepeatedly(Return(&mDisplayColorProfile));
EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
@@ -818,9 +843,11 @@
void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
ui::Dataspace dataspace = kDataspace,
const Region& visibleRegion = kOutputSpaceVisibleRegion,
- const Region& surfaceDamage = kSurfaceDamage) {
+ const Region& surfaceDamage = kSurfaceDamage,
+ float whitePointNits = kWhitePointNits) {
EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setWhitePointNits(whitePointNits)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
@@ -828,7 +855,7 @@
EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
}
- void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+ void expectSetCompositionTypeCall(Composition compositionType) {
EXPECT_CALL(*mHwcLayer, setCompositionType(compositionType)).WillOnce(Return(kError));
}
@@ -876,7 +903,7 @@
84.f / 255.f};
const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
-const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{0.f, 0.f, 4.f, 5.f};
+const FloatRect OutputLayerWriteStateToHWCTest::kOverrideSourceCrop{1002, 1003, 1004, 20044};
const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
Rect{1005, 1006, 1007, 1008}};
const Region OutputLayerWriteStateToHWCTest::kOverrideVisibleRegion{Rect{1006, 1007, 1008, 1009}};
@@ -942,7 +969,7 @@
// This test simulates a scenario where displayInstallOrientation is set to
// ROT_90. This only has an effect on the transform; orientation stays 0 (see
// DisplayDevice::setProjection).
- mOutputState.displaySpace.orientation = ui::ROTATION_0;
+ mOutputState.displaySpace.setOrientation(ui::ROTATION_0);
mOutputState.transform = ui::Transform{TR_ROT_90};
// Buffers are pre-rotated based on the transform hint (ROT_90); their
// geomBufferTransform is set to the inverse transform.
@@ -952,7 +979,7 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -961,7 +988,7 @@
// check this in this test only by setting up an testing::InSeqeuence
// instance before setting up the two expectations.
InSequence s;
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+ expectSetCompositionTypeCall(Composition::SOLID_COLOR);
expectSetColorCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
@@ -969,11 +996,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ mLayerFEState.compositionType = Composition::SIDEBAND;
expectPerFrameCommonCalls();
expectSetSidebandHandleCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+ expectSetCompositionTypeCall(Composition::SIDEBAND);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -982,11 +1009,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+ mLayerFEState.compositionType = Composition::CURSOR;
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+ expectSetCompositionTypeCall(Composition::CURSOR);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -995,11 +1022,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1008,10 +1035,9 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
- (*mOutputLayer.editState().hwc).hwcCompositionType =
- Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ (*mOutputLayer.editState().hwc).hwcCompositionType = Composition::SOLID_COLOR;
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
expectSetColorCall();
@@ -1024,11 +1050,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
expectSetColorCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1037,25 +1063,25 @@
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
mOutputLayer.editState().forceClientComposition = true;
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
expectSetColorCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeGenericLayerMetadataInState();
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
expectGenericLayerMetadataCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1064,12 +1090,12 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeGenericLayerMetadataInState();
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1078,15 +1104,31 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeOverrideInfo();
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) {
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kSkipAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
@@ -1094,15 +1136,31 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeOverrideInfo();
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kOverrideAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
+ expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1110,14 +1168,14 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().hwc->stateOverridden = true;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1125,16 +1183,16 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().hwc->stateOverridden = true;
mOutputLayer.editState().hwc->layerSkipped = true;
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1142,17 +1200,17 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().forceClientComposition = true;
mOutputLayer.editState().hwc->stateOverridden = true;
mOutputLayer.editState().hwc->layerSkipped = true;
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1198,7 +1256,7 @@
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
@@ -1210,14 +1268,13 @@
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
true);
- EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
- mOutputLayer.getState().hwc->hwcCompositionType);
+ EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType);
}
/*
@@ -1237,7 +1294,7 @@
mLayerFEState.cursorFrame = kDefaultCursorFrame;
- mOutputState.layerStackSpace.content = kDefaultDisplayViewport;
+ mOutputState.layerStackSpace.setContent(kDefaultDisplayViewport);
mOutputState.transform = ui::Transform{kDefaultTransform};
}
@@ -1316,14 +1373,14 @@
TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
EXPECT_TRUE(mOutputLayer.requiresClientComposition());
}
TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
EXPECT_FALSE(mOutputLayer.requiresClientComposition());
}
@@ -1340,14 +1397,14 @@
TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CURSOR;
EXPECT_TRUE(mOutputLayer.isHardwareCursor());
}
TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
EXPECT_FALSE(mOutputLayer.isHardwareCursor());
}
@@ -1358,13 +1415,12 @@
TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
- mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT);
+ mOutputLayer.applyDeviceCompositionTypeChange(Composition::CLIENT);
ASSERT_TRUE(mOutputLayer.getState().hwc);
- EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT,
- mOutputLayer.getState().hwc->hwcCompositionType);
+ EXPECT_EQ(Composition::CLIENT, mOutputLayer.getState().hwc->hwcCompositionType);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09f5a5e..6d96260 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -24,6 +24,7 @@
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
+#include <ftl/future.h>
#include <gtest/gtest.h>
#include <renderengine/mock/RenderEngine.h>
#include <ui/Rect.h>
@@ -35,6 +36,7 @@
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
#include "RegionMatcher.h"
+#include "TestUtils.h"
#include "renderengine/ExternalTexture.h"
namespace android::compositionengine {
@@ -142,7 +144,8 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput->editState().displaySpace.bounds = kDefaultDisplaySize;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight()));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
}
@@ -288,8 +291,10 @@
TEST_F(OutputTest, setProjectionWorks) {
const Rect displayRect{0, 0, 1000, 2000};
- mOutput->editState().displaySpace.bounds = displayRect;
- mOutput->editState().framebufferSpace.bounds = displayRect;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+ mOutput->editState().framebufferSpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
const ui::Rotation orientation = ui::ROTATION_90;
const Rect frame{50, 60, 100, 100};
@@ -297,28 +302,29 @@
mOutput->setProjection(orientation, viewport, frame);
- EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
- EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
- EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
const auto state = mOutput->getState();
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(viewport, state.layerStackSpace.content);
- EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(frame, state.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
- EXPECT_EQ(orientation, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+ EXPECT_EQ(orientation, state.displaySpace.getOrientation());
- EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content);
- EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+ EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.getContent());
+ EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
}
@@ -326,8 +332,10 @@
TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
const Rect displayRect{0, 0, 1000, 2000};
const Rect framebufferRect{0, 0, 500, 1000};
- mOutput->editState().displaySpace.bounds = displayRect;
- mOutput->editState().framebufferSpace.bounds = framebufferRect;
+ mOutput->editState().displaySpace.setBounds(
+ ui::Size(displayRect.getWidth(), displayRect.getHeight()));
+ mOutput->editState().framebufferSpace.setBounds(
+ ui::Size(framebufferRect.getWidth(), framebufferRect.getHeight()));
const ui::Rotation orientation = ui::ROTATION_90;
const Rect frame{50, 60, 100, 100};
@@ -335,28 +343,29 @@
mOutput->setProjection(orientation, viewport, frame);
- EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation);
- EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content);
- EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content);
+ EXPECT_EQ(orientation, mOutput->getState().displaySpace.getOrientation());
+ EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.getContent());
+ EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.getContent());
const auto state = mOutput->getState();
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(viewport, state.layerStackSpace.content);
- EXPECT_EQ(viewport, state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(viewport, state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 20, 20), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(frame, state.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(frame, state.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content);
- EXPECT_EQ(orientation, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.getContent());
+ EXPECT_EQ(orientation, state.displaySpace.getOrientation());
- EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds);
- EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content);
- EXPECT_EQ(orientation, state.framebufferSpace.orientation);
+ EXPECT_EQ(framebufferRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.getContent());
+ EXPECT_EQ(orientation, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
}
/*
@@ -364,16 +373,16 @@
*/
TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) {
- mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000);
- mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000);
- mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900);
- mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000);
- mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800);
- mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000);
- mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90;
- mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800);
- mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000);
- mOutput->editState().displaySpace.orientation = ui::ROTATION_90;
+ mOutput->editState().layerStackSpace.setContent(Rect(0, 0, 2000, 1000));
+ mOutput->editState().layerStackSpace.setBounds(ui::Size(2000, 1000));
+ mOutput->editState().orientedDisplaySpace.setContent(Rect(0, 0, 1800, 900));
+ mOutput->editState().orientedDisplaySpace.setBounds(ui::Size(2000, 1000));
+ mOutput->editState().framebufferSpace.setContent(Rect(0, 0, 900, 1800));
+ mOutput->editState().framebufferSpace.setBounds(ui::Size(1000, 2000));
+ mOutput->editState().framebufferSpace.setOrientation(ui::ROTATION_90);
+ mOutput->editState().displaySpace.setContent(Rect(0, 0, 900, 1800));
+ mOutput->editState().displaySpace.setBounds(ui::Size(1000, 2000));
+ mOutput->editState().displaySpace.setOrientation(ui::ROTATION_90);
const ui::Size newDisplaySize{500, 1000};
@@ -384,36 +393,38 @@
const auto state = mOutput->getState();
const Rect displayRect(newDisplaySize);
- EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content);
- EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.getOrientation());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getContent());
+ EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.getBoundsAsRect());
- EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation);
- EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds);
+ EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.getOrientation());
+ EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.getBoundsAsRect());
- EXPECT_EQ(displayRect, state.displaySpace.bounds);
- EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation);
+ EXPECT_EQ(displayRect, state.displaySpace.getBoundsAsRect());
+ EXPECT_EQ(ui::ROTATION_90, state.displaySpace.getOrientation());
- EXPECT_EQ(displayRect, state.framebufferSpace.bounds);
- EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation);
+ EXPECT_EQ(displayRect, state.framebufferSpace.getBoundsAsRect());
+ EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.getOrientation());
- EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+ EXPECT_EQ(state.displaySpace.getContent(),
+ state.transform.transform(state.layerStackSpace.getContent()));
EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect)));
}
/*
- * Output::setLayerStackFilter()
+ * Output::setLayerFilter()
*/
-TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
- const uint32_t layerStack = 123u;
- mOutput->setLayerStackFilter(layerStack, true);
+TEST_F(OutputTest, setLayerFilterSetsFilterAndDirtiesEntireOutput) {
+ constexpr ui::LayerFilter kFilter{ui::LayerStack{123u}, true};
+ mOutput->setLayerFilter(kFilter);
- EXPECT_TRUE(mOutput->getState().layerStackInternal);
- EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
+ const auto& state = mOutput->getState();
+ EXPECT_EQ(kFilter.layerStack, state.layerFilter.layerStack);
+ EXPECT_TRUE(state.layerFilter.toInternalDisplay);
- EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+ EXPECT_THAT(state.dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
/*
@@ -560,133 +571,107 @@
mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
- EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds);
+ EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.getBoundsAsRect());
}
/*
* Output::getDirtyRegion()
*/
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
+TEST_F(OutputTest, getDirtyRegion) {
const Rect viewport{100, 200};
- mOutput->editState().layerStackSpace.content = viewport;
+ mOutput->editState().layerStackSpace.setContent(viewport);
mOutput->editState().dirtyRegion.set(50, 300);
- {
- Region result = mOutput->getDirtyRegion(true);
-
- EXPECT_THAT(result, RegionEq(Region(viewport)));
- }
-}
-
-TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
- const Rect viewport{100, 200};
- mOutput->editState().layerStackSpace.content = viewport;
- mOutput->editState().dirtyRegion.set(50, 300);
-
- {
- Region result = mOutput->getDirtyRegion(false);
-
- // The dirtyRegion should be clipped to the display bounds.
- EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
- }
+ // The dirty region should be clipped to the display bounds.
+ EXPECT_THAT(mOutput->getDirtyRegion(), RegionEq(Region(Rect(50, 200))));
}
/*
- * Output::belongsInOutput()
+ * Output::includesLayer()
*/
-TEST_F(OutputTest, belongsInOutputFiltersAsExpected) {
- const uint32_t layerStack1 = 123u;
- const uint32_t layerStack2 = 456u;
+TEST_F(OutputTest, layerFiltering) {
+ const ui::LayerStack layerStack1{123u};
+ const ui::LayerStack layerStack2{456u};
- // If the output accepts layerStack1 and internal-only layers....
- mOutput->setLayerStackFilter(layerStack1, true);
+ // If the output is associated to layerStack1 and to an internal display...
+ mOutput->setLayerFilter({layerStack1, true});
- // A layer with no layerStack does not belong to it, internal-only or not.
- EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
- EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true));
+ // It excludes layers with no layer stack, internal-only or not.
+ EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, false}));
+ EXPECT_FALSE(mOutput->includesLayer({ui::INVALID_LAYER_STACK, true}));
- // Any layer with layerStack1 belongs to it, internal-only or not.
- EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
- EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true));
- EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
- EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+ // It includes layers on layerStack1, internal-only or not.
+ EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+ EXPECT_TRUE(mOutput->includesLayer({layerStack1, true}));
+ EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+ EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
- // If the output accepts layerStack21 but not internal-only layers...
- mOutput->setLayerStackFilter(layerStack1, false);
+ // If the output is associated to layerStack1 but not to an internal display...
+ mOutput->setLayerFilter({layerStack1, false});
- // Only non-internal layers with layerStack1 belong to it.
- EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
- EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
- EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
- EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
+ // It includes layers on layerStack1, unless they are internal-only.
+ EXPECT_TRUE(mOutput->includesLayer({layerStack1, false}));
+ EXPECT_FALSE(mOutput->includesLayer({layerStack1, true}));
+ EXPECT_FALSE(mOutput->includesLayer({layerStack2, true}));
+ EXPECT_FALSE(mOutput->includesLayer({layerStack2, false}));
}
-TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+TEST_F(OutputTest, layerFilteringWithoutCompositionState) {
NonInjectedLayer layer;
sp<LayerFE> layerFE(layer.layerFE);
- // If the layer has no composition state, it does not belong to any output.
+ // Layers without composition state are excluded.
EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
}
-TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+TEST_F(OutputTest, layerFilteringWithCompositionState) {
NonInjectedLayer layer;
sp<LayerFE> layerFE(layer.layerFE);
- const uint32_t layerStack1 = 123u;
- const uint32_t layerStack2 = 456u;
+ const ui::LayerStack layerStack1{123u};
+ const ui::LayerStack layerStack2{456u};
- // If the output accepts layerStack1 and internal-only layers....
- mOutput->setLayerStackFilter(layerStack1, true);
+ // If the output is associated to layerStack1 and to an internal display...
+ mOutput->setLayerFilter({layerStack1, true});
- // A layer with no layerStack does not belong to it, internal-only or not.
- layer.layerFEState.layerStackId = std::nullopt;
- layer.layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ // It excludes layers with no layer stack, internal-only or not.
+ layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, false};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = std::nullopt;
- layer.layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {ui::INVALID_LAYER_STACK, true};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- // Any layer with layerStack1 belongs to it, internal-only or not.
- layer.layerFEState.layerStackId = layerStack1;
- layer.layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+ // It includes layers on layerStack1, internal-only or not.
+ layer.layerFEState.outputFilter = {layerStack1, false};
+ EXPECT_TRUE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack1;
- layer.layerFEState.internalOnly = true;
- EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack1, true};
+ EXPECT_TRUE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack2;
- layer.layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack2, true};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack2;
- layer.layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack2, false};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- // If the output accepts layerStack1 but not internal-only layers...
- mOutput->setLayerStackFilter(layerStack1, false);
+ // If the output is associated to layerStack1 but not to an internal display...
+ mOutput->setLayerFilter({layerStack1, false});
- // Only non-internal layers with layerStack1 belong to it.
- layer.layerFEState.layerStackId = layerStack1;
- layer.layerFEState.internalOnly = false;
- EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+ // It includes layers on layerStack1, unless they are internal-only.
+ layer.layerFEState.outputFilter = {layerStack1, false};
+ EXPECT_TRUE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack1;
- layer.layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack1, true};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack2;
- layer.layerFEState.internalOnly = true;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack2, true};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
- layer.layerFEState.layerStackId = layerStack2;
- layer.layerFEState.internalOnly = false;
- EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+ layer.layerFEState.outputFilter = {layerStack2, false};
+ EXPECT_FALSE(mOutput->includesLayer(layerFE));
}
/*
@@ -1079,7 +1064,8 @@
OutputRebuildLayerStacksTest() {
mOutput.mState.isEnabled = true;
mOutput.mState.transform = kIdentityTransform;
- mOutput.mState.displaySpace.bounds = kOutputBounds;
+ mOutput.mState.displaySpace.setBounds(
+ ui::Size(kOutputBounds.getWidth(), kOutputBounds.getHeight()));
mRefreshArgs.updatingOutputGeometryThisFrame = true;
@@ -1268,21 +1254,22 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+ MOCK_METHOD(bool, includesLayer, (const sp<compositionengine::LayerFE>&),
+ (const, override));
MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
MOCK_METHOD2(ensureOutputLayer,
compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
};
OutputEnsureOutputLayerIfVisibleTest() {
- EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+ EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE)))
.WillRepeatedly(Return(true));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
- mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300);
+ mOutput.mState.displaySpace.setBounds(ui::Size(200, 300));
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 200, 300));
mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
mLayer.layerFEState.isVisible = true;
@@ -1326,8 +1313,8 @@
const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
Region(Rect(0, 0, 200, 100));
-TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
- EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) {
+ EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
EXPECT_CALL(*mLayer.layerFE,
prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
@@ -1337,8 +1324,8 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest,
- skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
- EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+ skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerIncluded) {
+ EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
ensureOutputLayerIfVisible();
}
@@ -1362,7 +1349,7 @@
}
TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
- mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0);
+ mOutput.mState.displaySpace.setBounds(ui::Size(0, 0));
ensureOutputLayerIfVisible();
}
@@ -1559,7 +1546,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
@@ -1585,7 +1572,7 @@
mLayer.layerFEState.contentDirty = true;
mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
- mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200);
+ mOutput.mState.layerStackSpace.setContent(Rect(0, 0, 300, 200));
mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
@@ -2530,7 +2517,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_METHOD(Region, getDirtyRegion, (), (const));
};
OutputBeginFrameTest() {
@@ -2542,8 +2529,7 @@
struct IfGetDirtyRegionExpectationState
: public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
[[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
- EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
- .WillOnce(Return(dirtyRegion));
+ EXPECT_CALL(getInstance()->mOutput, getDirtyRegion()).WillOnce(Return(dirtyRegion));
return nextState<AndIfGetOutputLayerCountExpectationState>();
}
};
@@ -2683,7 +2669,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_METHOD(Region, getDirtyRegion, (), (const));
MOCK_METHOD2(composeSurfaces,
std::optional<base::unique_fd>(
const Region&, const compositionengine::CompositionRefreshArgs&));
@@ -2711,7 +2697,6 @@
TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
- mRefreshArgs.repaintEverything = true;
mOutput.mState.isEnabled = true;
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2719,7 +2704,6 @@
TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
- mRefreshArgs.repaintEverything = true;
mOutput.mState.isEnabled = false;
InSequence seq;
@@ -2729,13 +2713,12 @@
mOutput.devOptRepaintFlash(mRefreshArgs);
}
-TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfEnabled) {
mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
- mRefreshArgs.repaintEverything = true;
mOutput.mState.isEnabled = true;
InSequence seq;
- EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+ EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, prepareFrame());
@@ -2744,11 +2727,10 @@
TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
- mRefreshArgs.repaintEverything = false;
mOutput.mState.isEnabled = true;
InSequence seq;
- EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+ EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
EXPECT_CALL(mOutput, postFramebuffer());
@@ -2903,12 +2885,24 @@
// are passed. This happens to work with the current implementation, but
// would not survive certain calls like Fence::merge() which would return a
// new instance.
- EXPECT_CALL(*mLayer1.layerFE,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
- EXPECT_CALL(*mLayer2.layerFE,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
- EXPECT_CALL(*mLayer3.layerFE,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+ base::unique_fd layer1FD(layer1Fence->dup());
+ base::unique_fd layer2FD(layer2Fence->dup());
+ base::unique_fd layer3FD(layer3Fence->dup());
+ EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(_))
+ .WillOnce([&layer1FD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layer1FD, futureRenderEngineResult.get().drawFence);
+ });
+ EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(_))
+ .WillOnce([&layer2FD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layer2FD, futureRenderEngineResult.get().drawFence);
+ });
+ EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(_))
+ .WillOnce([&layer3FD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layer3FD, futureRenderEngineResult.get().drawFence);
+ });
mOutput.postFramebuffer();
}
@@ -2934,9 +2928,9 @@
// Fence::merge is called, and since none of the fences are actually valid,
// Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
// This is the best we can do without creating a real kernel fence object.
- EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
- EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
- EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(*mLayer1.layerFE, onLayerDisplayed).WillOnce(Return());
+ EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
+ EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
mOutput.postFramebuffer();
}
@@ -2968,12 +2962,22 @@
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
// Each released layer should be given the presentFence.
- EXPECT_CALL(*releasedLayer1,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
- EXPECT_CALL(*releasedLayer2,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
- EXPECT_CALL(*releasedLayer3,
- onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+ base::unique_fd layerFD(presentFence.get()->dup());
+ EXPECT_CALL(*releasedLayer1, onLayerDisplayed(_))
+ .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+ });
+ EXPECT_CALL(*releasedLayer2, onLayerDisplayed(_))
+ .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+ });
+ EXPECT_CALL(*releasedLayer3, onLayerDisplayed(_))
+ .WillOnce([&layerFD](std::shared_future<renderengine::RenderEngineResult>
+ futureRenderEngineResult) {
+ EXPECT_EQ(layerFD, futureRenderEngineResult.get().drawFence);
+ });
mOutput.postFramebuffer();
@@ -2993,7 +2997,7 @@
// mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD3(generateClientCompositionRequests,
- std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+ std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3005,11 +3009,11 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
- mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame;
- mOutput.mState.layerStackSpace.content = kDefaultOutputViewport;
- mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip;
- mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation;
+ mOutput.mState.orientedDisplaySpace.setContent(kDefaultOutputFrame);
+ mOutput.mState.layerStackSpace.setContent(kDefaultOutputViewport);
+ mOutput.mState.framebufferSpace.setContent(kDefaultOutputDestinationClip);
+ mOutput.mState.displaySpace.setContent(kDefaultOutputDestinationClip);
+ mOutput.mState.displaySpace.setOrientation(kDefaultOutputOrientation);
mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags};
mOutput.mState.dataspace = kDefaultOutputDataspace;
mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
@@ -3019,6 +3023,7 @@
mOutput.mState.usesDeviceComposition = false;
mOutput.mState.reusedClientComposition = false;
mOutput.mState.flipClientTarget = false;
+ mOutput.mState.clientTargetWhitePointNits = kClientTargetLuminanceNits;
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -3053,6 +3058,7 @@
static constexpr float kDefaultMaxLuminance = 0.9f;
static constexpr float kDefaultAvgLuminance = 0.7f;
static constexpr float kDefaultMinLuminance = 0.1f;
+ static constexpr float kClientTargetLuminanceNits = 200.f;
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
@@ -3142,15 +3148,20 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _, _))
- .WillRepeatedly(Return(NO_ERROR));
-
+ EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _))
+ .WillRepeatedly([&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ });
verify().execute().expectAFenceWasReturned();
}
@@ -3165,7 +3176,7 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(
@@ -3175,8 +3186,14 @@
}));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
- .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ .WillRepeatedly([&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ });
verify().execute().expectAFenceWasReturned();
}
@@ -3188,14 +3205,13 @@
r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
- const constexpr uint32_t kInternalLayerStack = 1234;
- mOutput.setLayerStackFilter(kInternalLayerStack, true);
+ mOutput.setLayerFilter({ui::LayerStack{1234u}, true});
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(
@@ -3205,8 +3221,14 @@
}));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
- .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, true, _))
+ .WillRepeatedly([&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ });
verify().execute().expectAFenceWasReturned();
}
@@ -3223,15 +3245,18 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
.Times(2)
- .WillOnce(Return(NO_ERROR));
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
verify().execute().expectAFenceWasReturned();
EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3252,14 +3277,15 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
- .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
verify().execute().expectAFenceWasReturned();
@@ -3281,7 +3307,7 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
@@ -3293,8 +3319,14 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
.WillOnce(Return(mOutputBuffer))
.WillOnce(Return(otherOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
- .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ .WillRepeatedly([&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ });
verify().execute().expectAFenceWasReturned();
EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3316,17 +3348,19 @@
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, false, _, _))
- .WillOnce(Return(NO_ERROR));
- EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, false, _, _))
- .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
verify().execute().expectAFenceWasReturned();
EXPECT_FALSE(mOutput.mState.reusedClientComposition);
@@ -3339,7 +3373,7 @@
OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
@@ -3375,8 +3409,9 @@
struct ExpectDisplaySettingsState
: public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
- EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _, _))
- .WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _))
+ .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()}))));
return nextState<ExecuteState>();
}
};
@@ -3391,7 +3426,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3402,7 +3438,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3411,10 +3448,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3423,10 +3460,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat, Region::INVALID_REGION,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3438,7 +3475,8 @@
.andIfSkipColorTransform(true)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- Region::INVALID_REGION, kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3474,8 +3512,15 @@
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _))
- .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ .WillRepeatedly(
+ [&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ return futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
+ });
}
Layer mLayer1;
@@ -3527,7 +3572,9 @@
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, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3590,14 +3637,16 @@
TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
mOutput.mState.dataspace = kExpensiveOutputDataspace;
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kExpensiveOutputDataspace, _))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
// For this test, we also check the call order of key functions.
InSequence seq;
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3613,9 +3662,11 @@
EXPECT_CALL(mLayer.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
+ .WillOnce(Return(ByMove(futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()}))));
EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
.WillRepeatedly(Return(&mLayer.outputLayer));
@@ -3652,11 +3703,11 @@
struct GenerateClientCompositionRequestsTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
- std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion,
- ui::Dataspace dataspace) override {
+ std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper(
+ bool supportsProtectedContent, ui::Dataspace dataspace) {
+ std::vector<LayerFE*> ignore;
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
- clearRegion, dataspace);
+ dataspace, ignore);
}
};
@@ -3683,6 +3734,8 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
}
+ static constexpr float kLayerWhitePointNits = 200.f;
+
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
StrictMock<OutputPartialMock> mOutput;
@@ -3691,12 +3744,12 @@
struct GenerateClientCompositionRequestsTest_ThreeLayers
: public GenerateClientCompositionRequestsTest {
GenerateClientCompositionRequestsTest_ThreeLayers() {
- mOutput.mState.orientedDisplaySpace.content = kDisplayFrame;
- mOutput.mState.layerStackSpace.content = kDisplayViewport;
- mOutput.mState.displaySpace.content = kDisplayDestinationClip;
+ mOutput.mState.orientedDisplaySpace.setContent(kDisplayFrame);
+ mOutput.mState.layerStackSpace.setContent(kDisplayViewport);
+ mOutput.mState.displaySpace.setContent(kDisplayDestinationClip);
mOutput.mState.transform =
ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)};
- mOutput.mState.displaySpace.orientation = kDisplayOrientation;
+ mOutput.mState.displaySpace.setOrientation(kDisplayOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = false;
@@ -3722,6 +3775,7 @@
static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+ static constexpr float kLayerWhitePointNits = 200.f;
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
@@ -3740,11 +3794,9 @@
EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
@@ -3752,11 +3804,9 @@
mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
@@ -3771,16 +3821,13 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>(
{mShadowSettings, mLayers[2].mLayerSettings})));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(3u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mShadowSettings, requests[1]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
// Check that a timestamp was set for the layers that generated requests
EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3811,16 +3858,13 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>(
{mShadowSettings, mLayers[2].mLayerSettings})));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(3u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mShadowSettings, requests[1]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
-
// Check that a timestamp was set for the layers that generated requests
EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
@@ -3844,13 +3888,10 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3870,13 +3911,10 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(_))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
- Region accumClearRegion(Rect(10, 11, 12, 13));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
-
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
@@ -3897,32 +3935,30 @@
mLayers[0].mLayerFEState.isOpaque = true;
mLayers[1].mLayerFEState.isOpaque = true;
mLayers[2].mLayerFEState.isOpaque = true;
- Region accumClearRegion(Rect(10, 11, 12, 13));
- Region stubRegion;
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* needs filtering */
- false, /* secure */
- false, /* supports protected content */
- stubRegion, /* clear region */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
kDisplayViewport,
kDisplayDataspace,
false /* realContentIsVisible */,
true /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3936,16 +3972,14 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
// The second layer is expected to be rendered as alpha=0 black with no blending
EXPECT_EQ(mBlackoutSettings, requests[0]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
-
- EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -3954,43 +3988,41 @@
mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(Rect(10, 10, 20, 20)),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4001,8 +4033,8 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
- mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace));
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4010,43 +4042,41 @@
mOutput.mState.needsFiltering = false;
EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4057,8 +4087,8 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
- mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace));
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4066,44 +4096,41 @@
mOutput.mState.needsFiltering = true;
EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
-
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
true, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4114,51 +4141,49 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
- mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace));
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
wholeOutputSecurityUsedToGenerateRequests) {
mOutput.mState.isSecure = true;
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
true, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4169,49 +4194,47 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
static_cast<void>(
- mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace));
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
protectedContentSupportUsedToGenerateRequests) {
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
false, /* secure */
true, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4221,8 +4244,7 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
- static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
- accumClearRegion,
+ static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
kDisplayDataspace));
}
@@ -4343,11 +4365,11 @@
const ui::Rotation kPortraitOrientation = ui::ROTATION_90;
constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.mState.orientedDisplaySpace.content = kPortraitFrame;
- mOutput.mState.layerStackSpace.content = kPortraitViewport;
- mOutput.mState.displaySpace.content = kPortraitDestinationClip;
+ mOutput.mState.orientedDisplaySpace.setContent(kPortraitFrame);
+ mOutput.mState.layerStackSpace.setContent(kPortraitViewport);
+ mOutput.mState.displaySpace.setContent(kPortraitDestinationClip);
mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)};
- mOutput.mState.displaySpace.orientation = kPortraitOrientation;
+ mOutput.mState.displaySpace.setOrientation(kPortraitOrientation);
mOutput.mState.needsFiltering = false;
mOutput.mState.isSecure = true;
@@ -4370,19 +4392,17 @@
EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
.WillRepeatedly(Return(&rightLayer.mOutputLayer));
- Region accumClearRegion(Rect(10, 11, 12, 13));
-
compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
Region(Rect(0, 0, 1000, 1000)),
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
- accumClearRegion,
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4395,12 +4415,12 @@
false, /* needs filtering */
true, /* secure */
true, /* supports protected content */
- accumClearRegion,
kPortraitViewport,
kOutputDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4409,8 +4429,8 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
constexpr bool supportsProtectedContent = true;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
- accumClearRegion, kOutputDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -4423,18 +4443,17 @@
const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
- Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
false /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mShadowSettings;
@@ -4448,8 +4467,8 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mShadowSettings, requests[0]);
@@ -4469,18 +4488,17 @@
mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
- Region accumClearRegion(Rect(10, 11, 12, 13));
compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
false, /* needs filtering */
false, /* secure */
false, /* supports protected content */
- accumClearRegion,
kDisplayViewport,
kDisplayDataspace,
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -4489,8 +4507,8 @@
.WillOnce(Return(std::vector<LayerFE::LayerSettings>(
{mShadowSettings, mLayers[2].mLayerSettings})));
- auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
- accumClearRegion, kDisplayDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(mShadowSettings, requests[0]);
diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
index 704f5a8..19b3928 100644
--- a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp
@@ -53,40 +53,40 @@
TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) {
ProjectionSpace space;
- space.content = Rect(100, 200);
- space.bounds = Rect(100, 200);
+ space.setContent(Rect(100, 200));
+ space.setBounds(ui::Size(100, 200));
const ui::Transform identity;
for (int rotation = 0; rotation <= 3; rotation++) {
- space.orientation = ui::Rotation(rotation);
+ space.setOrientation(ui::Rotation(rotation));
EXPECT_EQ(space.getTransform(space), identity);
}
}
TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) {
ProjectionSpace source;
- source.content = Rect(10, 10, 20, 20);
- source.bounds = Rect(100, 200);
+ source.setContent(Rect(10, 10, 20, 20));
+ source.setBounds(ui::Size(100, 200));
ProjectionSpace dest;
- dest.content = Rect(10, 20, 30, 20);
- dest.bounds = source.bounds;
+ dest.setContent(Rect(10, 20, 30, 20));
+ dest.setBounds(source.getBounds());
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content);
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
}
TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) {
ProjectionSpace source;
- source.content = Rect(0, 0, 20, 20);
- source.bounds = Rect(100, 200);
+ source.setContent(Rect(0, 0, 20, 20));
+ source.setBounds(ui::Size(100, 200));
ProjectionSpace dest;
- dest.content = Rect(0, 0, 40, 30);
- dest.bounds = source.bounds;
+ dest.setContent(Rect(0, 0, 40, 30));
+ dest.setBounds(source.getBounds());
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content);
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent());
}
TEST(ProjectionSpaceTest, getSideStripTest) {
@@ -99,7 +99,7 @@
void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) {
const auto transform = source.getTransform(dest);
- EXPECT_EQ(transform.transform(source.content), dest.content)
+ EXPECT_EQ(transform.transform(source.getContent()), dest.getContent())
<< "Source content doesn't map to dest content when projecting " << to_string(source)
<< " onto " << to_string(dest);
@@ -113,8 +113,8 @@
// | | | *
// +-----+ +-------*
// source(ROTATION_0) dest (ROTATION_90)
- const auto sourceStrip = getSideStrip(source.content, source.orientation);
- const auto destStrip = getSideStrip(dest.content, dest.orientation);
+ const auto sourceStrip = getSideStrip(source.getContent(), source.getOrientation());
+ const auto destStrip = getSideStrip(dest.getContent(), dest.getOrientation());
ASSERT_NE(sourceStrip, Rect::INVALID_RECT);
ASSERT_NE(destStrip, Rect::INVALID_RECT);
const auto mappedStrip = transform.transform(sourceStrip);
@@ -126,16 +126,16 @@
TEST(ProjectionSpaceTest, getTransformWithOrienations) {
ProjectionSpace source;
- source.bounds = Rect(12, 13, 678, 789);
- source.content = Rect(40, 50, 234, 343);
+ source.setBounds(ui::Size(666, 776));
+ source.setContent(Rect(40, 50, 234, 343));
ProjectionSpace dest;
- dest.bounds = Rect(17, 18, 879, 564);
- dest.content = Rect(43, 52, 432, 213);
+ dest.setBounds(ui::Size(862, 546));
+ dest.setContent(Rect(43, 52, 432, 213));
for (int sourceRot = 0; sourceRot <= 3; sourceRot++) {
- source.orientation = ui::Rotation(sourceRot);
+ source.setOrientation(ui::Rotation(sourceRot));
for (int destRot = 0; destRot <= 3; destRot++) {
- dest.orientation = ui::Rotation(destRot);
+ dest.setOrientation(ui::Rotation(destRot));
testTransform(source, dest);
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 5090bb2..7c8e41b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -35,7 +35,7 @@
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
-constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u);
+constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(123u);
const std::string DEFAULT_DISPLAY_NAME = "Mock Display";
using testing::_;
@@ -201,28 +201,28 @@
*/
TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Mixed))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, true);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Gpu))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, false);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(false, true);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(false, false);
diff --git a/services/surfaceflinger/CompositionEngine/tests/TestUtils.h b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
new file mode 100644
index 0000000..c80fde6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TestUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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 <future>
+
+namespace android::compositionengine {
+namespace {
+
+template <class T>
+std::future<T> futureOf(T obj) {
+ std::promise<T> resultPromise;
+ std::future<T> resultFuture = resultPromise.get_future();
+ resultPromise.set_value(std::move(obj));
+ return resultFuture;
+}
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index ec81322..d5a117a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -27,6 +27,8 @@
#include <utils/Errors.h>
#include <memory>
+#include "tests/TestUtils.h"
+
namespace android::compositionengine {
using namespace std::chrono_literals;
@@ -53,13 +55,21 @@
}
MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") {
- *result_listener << "ClientCompositionTargetSettings' SecureSettings aren't equal \n";
+ *result_listener << "ClientCompositionTargetSettings' isSecure bits aren't equal \n";
*result_listener << "expected " << expectedSecureSetting << "\n";
*result_listener << "actual " << arg.isSecure << "\n";
return expectedSecureSetting == arg.isSecure;
}
+MATCHER_P(ClientCompositionTargetSettingsWhitePointEq, expectedWhitePoint, "") {
+ *result_listener << "ClientCompositionTargetSettings' white points aren't equal \n";
+ *result_listener << "expected " << expectedWhitePoint << "\n";
+ *result_listener << "actual " << arg.whitePointNits << "\n";
+
+ return expectedWhitePoint == arg.whitePointNits;
+}
+
static const ui::Size kOutputSize = ui::Size(1, 1);
class CachedSetTest : public testing::Test {
@@ -121,7 +131,7 @@
// set up minimium params needed for rendering
mOutputState.dataspace = ui::Dataspace::SRGB;
mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
mOutputState.layerStackSpace = ProjectionSpace(ui::Size(20, 10), Rect(5, 10));
}
}
@@ -342,19 +352,19 @@
clientCompList2.push_back({});
clientCompList2[0].alpha = 0.75f;
- const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
- EXPECT_EQ(0.5f, layers[0]->alpha);
- EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(0.5f, layers[0].alpha);
+ EXPECT_EQ(0.75f, layers[1].alpha);
EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
-
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
EXPECT_CALL(*layerFE1,
@@ -363,7 +373,7 @@
EXPECT_CALL(*layerFE2,
prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(false)))
.WillOnce(Return(clientCompList2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = false;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
expectReadyBuffer(cachedSet);
@@ -394,19 +404,20 @@
clientCompList2.push_back({});
clientCompList2[0].alpha = 0.75f;
- const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
- EXPECT_EQ(0.5f, layers[0]->alpha);
- EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(0.5f, layers[0].alpha);
+ EXPECT_EQ(0.75f, layers[1].alpha);
EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
EXPECT_CALL(*layerFE1,
@@ -415,7 +426,55 @@
EXPECT_CALL(*layerFE2,
prepareClientCompositionList(ClientCompositionTargetSettingsSecureEq(true)))
.WillOnce(Return(clientCompList2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ mOutputState.isSecure = true;
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+ EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
+TEST_F(CachedSetTest, renderWhitePoint) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+
+ mOutputState.displayBrightnessNits = 400.f;
+
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ };
+
+ EXPECT_CALL(*layerFE1,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
mOutputState.isSecure = true;
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
expectReadyBuffer(cachedSet);
@@ -448,24 +507,25 @@
mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5));
- const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> size_t {
- EXPECT_EQ(mOutputState.framebufferSpace.content, displaySettings.physicalDisplay);
- EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip);
- EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation),
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay);
+ EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip);
+ EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.getOrientation()),
displaySettings.orientation);
- EXPECT_EQ(0.5f, layers[0]->alpha);
- EXPECT_EQ(0.75f, layers[1]->alpha);
+ EXPECT_EQ(0.5f, layers[0].alpha);
+ EXPECT_EQ(0.75f, layers[1].alpha);
EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace);
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
expectReadyBuffer(cachedSet);
@@ -650,33 +710,34 @@
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
- const auto drawLayers = [&](const renderengine::DisplaySettings&,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> size_t {
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
EXPECT_GE(layers.size(), 4u);
{
- const auto* holePunchSettings = layers[3];
- EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
- EXPECT_TRUE(holePunchSettings->disableBlending);
- EXPECT_EQ(0.0f, holePunchSettings->alpha);
+ const auto holePunchSettings = layers[3];
+ EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor);
+ EXPECT_TRUE(holePunchSettings.disableBlending);
+ EXPECT_EQ(0.0f, holePunchSettings.alpha);
}
{
- const auto* holePunchBackgroundSettings = layers[0];
- EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
- EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
- EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+ const auto holePunchBackgroundSettings = layers[0];
+ EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor);
+ EXPECT_FALSE(holePunchBackgroundSettings.disableBlending);
+ EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
}
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
}
@@ -710,34 +771,35 @@
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3));
- const auto drawLayers = [&](const renderengine::DisplaySettings&,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> size_t {
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
EXPECT_GE(layers.size(), 4u);
{
- const auto* holePunchSettings = layers[3];
- EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer);
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor);
- EXPECT_TRUE(holePunchSettings->disableBlending);
- EXPECT_EQ(0.0f, holePunchSettings->alpha);
+ const auto holePunchSettings = layers[3];
+ EXPECT_EQ(nullptr, holePunchSettings.source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings.source.solidColor);
+ EXPECT_TRUE(holePunchSettings.disableBlending);
+ EXPECT_EQ(0.0f, holePunchSettings.alpha);
}
{
- const auto* holePunchBackgroundSettings = layers[0];
- EXPECT_EQ(nullptr, holePunchBackgroundSettings->source.buffer.buffer);
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings->source.solidColor);
- EXPECT_FALSE(holePunchBackgroundSettings->disableBlending);
- EXPECT_EQ(1.0f, holePunchBackgroundSettings->alpha);
+ const auto holePunchBackgroundSettings = layers[0];
+ EXPECT_EQ(nullptr, holePunchBackgroundSettings.source.buffer.buffer);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchBackgroundSettings.source.solidColor);
+ EXPECT_FALSE(holePunchBackgroundSettings.disableBlending);
+ EXPECT_EQ(1.0f, holePunchBackgroundSettings.alpha);
}
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
}
@@ -859,22 +921,23 @@
BackgroundBlurOnly)))
.WillOnce(Return(clientCompList3));
- const auto drawLayers = [&](const renderengine::DisplaySettings&,
- const std::vector<const renderengine::LayerSettings*>& layers,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> int32_t {
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>& layers,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
// If the highlight layer is enabled, it will increase the size by 1.
// We're interested in the third layer either way.
EXPECT_GE(layers.size(), 3u);
- const auto* blurSettings = layers[2];
- EXPECT_TRUE(blurSettings->skipContentDraw);
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings->source.solidColor);
- EXPECT_EQ(0.0f, blurSettings->alpha);
+ const auto blurSettings = layers[2];
+ EXPECT_TRUE(blurSettings.skipContentDraw);
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), blurSettings.source.solidColor);
+ EXPECT_EQ(0.0f, blurSettings.alpha);
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
};
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index a28fb2c..35d051e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -26,6 +26,8 @@
#include <renderengine/mock/RenderEngine.h>
#include <chrono>
+#include "tests/TestUtils.h"
+
namespace android::compositionengine {
using namespace std::chrono_literals;
using impl::planner::CachedSet;
@@ -141,7 +143,7 @@
// set up minimium params needed for rendering
mOutputState.dataspace = ui::Dataspace::SRGB;
mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(10, 5));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
}
}
@@ -167,7 +169,9 @@
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -220,6 +224,22 @@
mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
+ mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
+ mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+ expectAllLayersFlattened(layers);
+}
+
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
auto& layerState1 = mTestLayers[0]->layerState;
auto& layerState2 = mTestLayers[1]->layerState;
@@ -401,7 +421,9 @@
// caleed for Layer2 and Layer3
layerState1->resetFramesSinceBufferUpdate();
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -423,7 +445,9 @@
layerState1->incrementFramesSinceBufferUpdate();
mTime += 200ms;
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -474,7 +498,9 @@
// called for Layer1 and Layer2
layerState3->resetFramesSinceBufferUpdate();
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -487,11 +513,13 @@
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(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_90;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
@@ -504,7 +532,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_180;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
@@ -515,8 +543,9 @@
layerState3->incrementFramesSinceBufferUpdate();
mTime += 200ms;
-
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
@@ -531,7 +560,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mOutputState.framebufferSpace.orientation = ui::ROTATION_270;
+ mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270);
mFlattener->renderCachedSets(mOutputState, std::nullopt);
EXPECT_NE(nullptr, overrideBuffer1);
@@ -570,7 +599,9 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
@@ -580,7 +611,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));
@@ -632,7 +663,9 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
@@ -642,7 +675,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));
@@ -660,6 +693,145 @@
EXPECT_EQ(peekThroughLayer1, peekThroughLayer2);
}
+// A test that verifies the hole puch optimization can be done on a single layer.
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+
+ // An opaque static background
+ auto& layerState0 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // a rounded updating layer
+ auto& layerState1 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState0.get(),
+ layerState1.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+ initializeOverrideBuffer(layers);
+ // Expect no cache invalidation the first time (there's no cache yet)
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // 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(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ 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);
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+ EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+ // expect 0's peek though layer to be 1's output layer
+ const auto* peekThroughLayer0 =
+ layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+ EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+ mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+
+ // An opaque static background
+ auto& layerState0 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // a rounded updating layer
+ auto& layerState1 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState0.get(),
+ layerState1.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+ initializeOverrideBuffer(layers);
+ // Expect no cache invalidation the first time (there's no cache yet)
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // 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(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ 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);
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+ EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+ // expect 0's peek though layer to be 1's output layer
+ const auto* peekThroughLayer0 =
+ layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+ EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
auto& layerState1 = mTestLayers[0]->layerState;
@@ -685,7 +857,9 @@
layerState3->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -729,7 +903,9 @@
layerState1->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillRepeatedly(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -781,7 +957,9 @@
layerState1->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -828,7 +1006,9 @@
layerStateWithBlurBehind->resetFramesSinceBufferUpdate();
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -869,7 +1049,9 @@
// Mark the layers inactive
mTime += 200ms;
// layers would be flattened but the buffer would not be overridden
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
@@ -881,7 +1063,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);
// We provide the override buffer now that it's rendered
@@ -928,13 +1110,15 @@
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));
}
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState,
std::chrono::steady_clock::now() -
(kCachedSetRenderDuration + 10ms));
@@ -968,7 +1152,9 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
@@ -978,7 +1164,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));
@@ -1017,7 +1203,9 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
@@ -1027,7 +1215,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));
@@ -1066,7 +1254,9 @@
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
// This will render a CachedSet.
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
mFlattener->renderCachedSets(mOutputState, std::nullopt);
// We've rendered a CachedSet, but we haven't merged it in.
@@ -1076,7 +1266,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));
@@ -1087,5 +1277,61 @@
EXPECT_EQ(nullptr, overrideBuffer3);
}
+TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // Rewrite the first two layers to just be a solid color.
+ mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+ mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+ mTestLayers[1]->layerFECompositionState.color = half4(0.f, 255.f, 0.f, 255.f);
+ mTestLayers[1]->layerFECompositionState.buffer = nullptr;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ layerState4.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+
+ // 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);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+ EXPECT_NE(nullptr, overrideBuffer4);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 9ad3ab4..84b3fc5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -27,6 +27,10 @@
#include "android/hardware_buffer.h"
#include "compositionengine/LayerFECompositionState.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine::impl::planner {
namespace {
@@ -277,33 +281,28 @@
TEST_F(LayerStateTest, getCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::DEVICE, mLayerState->getCompositionType());
}
TEST_F(LayerStateTest, getCompositionType_forcedClient) {
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.forceClientComposition = true;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::CLIENT, mLayerState->getCompositionType());
}
TEST_F(LayerStateTest, updateCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -311,29 +310,25 @@
mock::OutputLayer newOutputLayer;
mock::LayerFE newLayerFE;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType =
- hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
layerFECompositionStateTwo);
Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::SOLID_COLOR, mLayerState->getCompositionType());
EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
}
TEST_F(LayerStateTest, compareCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
mock::OutputLayer newOutputLayer;
mock::LayerFE newLayerFE;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType =
- hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
layerFECompositionStateTwo);
auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 1492707..6038268 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -24,6 +24,10 @@
#include <gtest/gtest.h>
#include <log/log.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine::impl::planner {
namespace {
@@ -103,7 +107,7 @@
mock::LayerFE layerFEOne;
OutputLayerCompositionState outputLayerCompositionStateOne;
LayerFECompositionState layerFECompositionStateOne;
- layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ layerFECompositionStateOne.compositionType = Composition::DEVICE;
setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
layerFECompositionStateOne);
LayerState layerStateOne(&outputLayerOne);
@@ -112,7 +116,7 @@
mock::LayerFE layerFETwo;
OutputLayerCompositionState outputLayerCompositionStateTwo;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
layerFECompositionStateTwo);
LayerState layerStateTwo(&outputLayerTwo);
@@ -370,7 +374,7 @@
TEST_F(PredictionTest, constructPrediction) {
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Prediction prediction({}, plan);
@@ -442,13 +446,13 @@
mock::LayerFE layerFEOne;
OutputLayerCompositionState outputLayerCompositionStateOne;
LayerFECompositionState layerFECompositionStateOne;
- layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ layerFECompositionStateOne.compositionType = Composition::DEVICE;
setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
layerFECompositionStateOne);
LayerState layerStateOne(&outputLayerOne);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -484,7 +488,7 @@
LayerState layerStateTwo(&outputLayerTwo);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -521,7 +525,7 @@
LayerState layerStateTwo(&outputLayerTwo);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -535,7 +539,7 @@
EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
Plan planTwo;
- planTwo.addLayerType(hal::Composition::CLIENT);
+ planTwo.addLayerType(Composition::CLIENT);
predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
// Now trying to retrieve the predicted plan again returns a nullopt instead.
// TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 841e79f..3ccc229 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -36,8 +36,7 @@
sp<Layer> ContainerLayer::createClone() {
sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
- LayerMetadata()));
+ LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
layer->setInitialValuesForClone(this);
return layer;
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4445eea..76bbe2c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,11 +116,11 @@
}
int DisplayDevice::getWidth() const {
- return mCompositionDisplay->getState().displaySpace.bounds.getWidth();
+ return mCompositionDisplay->getState().displaySpace.getBounds().width;
}
int DisplayDevice::getHeight() const {
- return mCompositionDisplay->getState().displaySpace.bounds.getHeight();
+ return mCompositionDisplay->getState().displaySpace.getBounds().height;
}
void DisplayDevice::setDisplayName(const std::string& displayName) {
@@ -139,6 +139,34 @@
return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
}
+std::pair<gui::DisplayInfo, ui::Transform> DisplayDevice::getInputInfo() const {
+ gui::DisplayInfo info;
+ info.displayId = getLayerStack().id;
+
+ // The physical orientation is set when the orientation of the display panel is
+ // different than the default orientation of the device. Other services like
+ // InputFlinger do not know about this, so we do not need to expose the physical
+ // orientation of the panel outside of SurfaceFlinger.
+ const ui::Rotation inversePhysicalOrientation = ui::ROTATION_0 - mPhysicalOrientation;
+ auto width = getWidth();
+ auto height = getHeight();
+ if (inversePhysicalOrientation == ui::ROTATION_90 ||
+ inversePhysicalOrientation == ui::ROTATION_270) {
+ std::swap(width, height);
+ }
+ const ui::Transform undoPhysicalOrientation(ui::Transform::toRotationFlags(
+ inversePhysicalOrientation),
+ width, height);
+ const auto& displayTransform = undoPhysicalOrientation * getTransform();
+ // Send the inverse display transform to input so it can convert display coordinates to
+ // logical display.
+ info.transform = displayTransform.inverse();
+
+ info.logicalWidth = getLayerStackSpaceRect().width();
+ info.logicalHeight = getLayerStackSpaceRect().height();
+ return {info, displayTransform};
+}
+
// ----------------------------------------------------------------------------
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
mPowerMode = mode;
@@ -232,7 +260,7 @@
}
void DisplayDevice::setLayerStack(ui::LayerStack stack) {
- mCompositionDisplay->setLayerStackFilter(stack, isInternal());
+ mCompositionDisplay->setLayerFilter({stack, isInternal()});
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setLayerStack(stack);
}
@@ -262,13 +290,15 @@
if (!orientedDisplaySpaceRect.isValid()) {
// The destination frame can be invalid if it has never been set,
// in that case we assume the whole display size.
- orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds;
+ orientedDisplaySpaceRect =
+ getCompositionDisplay()->getState().displaySpace.getBoundsAsRect();
}
if (layerStackSpaceRect.isEmpty()) {
// The layerStackSpaceRect can be invalid if it has never been set, in that case
// we assume the whole framebuffer size.
- layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds;
+ layerStackSpaceRect =
+ getCompositionDisplay()->getState().framebufferSpace.getBoundsAsRect();
if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom);
}
@@ -336,8 +366,8 @@
return mCompositionDisplay->isSecure();
}
-const Rect& DisplayDevice::getBounds() const {
- return mCompositionDisplay->getState().displaySpace.bounds;
+const Rect DisplayDevice::getBounds() const {
+ return mCompositionDisplay->getState().displaySpace.getBoundsAsRect();
}
const Region& DisplayDevice::getUndefinedRegion() const {
@@ -349,7 +379,7 @@
}
ui::LayerStack DisplayDevice::getLayerStack() const {
- return mCompositionDisplay->getState().layerStackId;
+ return mCompositionDisplay->getState().layerFilter.layerStack;
}
ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
@@ -361,11 +391,11 @@
}
const Rect& DisplayDevice::getLayerStackSpaceRect() const {
- return mCompositionDisplay->getState().layerStackSpace.content;
+ return mCompositionDisplay->getState().layerStackSpace.getContent();
}
const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const {
- return mCompositionDisplay->getState().orientedDisplaySpace.content;
+ return mCompositionDisplay->getState().orientedDisplaySpace.getContent();
}
bool DisplayDevice::hasWideColorGamut() const {
@@ -436,9 +466,9 @@
return false;
}
-void DisplayDevice::onInvalidate() {
+void DisplayDevice::animateRefreshRateOverlay() {
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->onInvalidate();
+ mRefreshRateOverlay->animate();
}
}
@@ -453,7 +483,7 @@
std::scoped_lock lock(mActiveModeLock);
if (mDesiredActiveModeChanged) {
// If a mode change is pending, just cache the latest request in mDesiredActiveMode
- const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+ const auto prevConfig = mDesiredActiveMode.event;
mDesiredActiveMode = info;
mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
return false;
@@ -478,7 +508,7 @@
void DisplayDevice::clearDesiredActiveModeState() {
std::scoped_lock lock(mActiveModeLock);
- mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+ mDesiredActiveMode.event = scheduler::DisplayModeEvent::None;
mDesiredActiveModeChanged = false;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4d435c7..324145e 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -86,9 +86,7 @@
bool isVirtual() const { return !mConnectionType; }
bool isPrimary() const { return mIsPrimary; }
- bool isInternal() const {
- return !isVirtual() && mConnectionType == ui::DisplayConnectionType::Internal;
- }
+ bool isInternal() const { return mConnectionType == ui::DisplayConnectionType::Internal; }
// isSecure indicates whether this display can be trusted to display
// secure surfaces.
@@ -158,8 +156,8 @@
// Return true if intent is supported by the display.
bool hasRenderIntent(ui::RenderIntent intent) const;
- const Rect& getBounds() const;
- const Rect& bounds() const { return getBounds(); }
+ const Rect getBounds() const;
+ const Rect bounds() const { return getBounds(); }
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
@@ -169,6 +167,10 @@
return mDeviceProductInfo;
}
+ // Get the DisplayInfo that will be sent to InputFlinger, and the display transform that should
+ // be applied to all the input windows on the display.
+ std::pair<gui::DisplayInfo, ui::Transform> getInputInfo() const;
+
/* ------------------------------------------------------------------------
* Display power mode management.
*/
@@ -188,7 +190,7 @@
struct ActiveModeInfo {
DisplayModePtr mode;
- scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None;
+ scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
bool operator!=(const ActiveModeInfo& other) const {
return mode != other.mode || event != other.event;
@@ -232,7 +234,7 @@
void enableRefreshRateOverlay(bool enable, bool showSpinner);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
- void onInvalidate();
+ void animateRefreshRateOverlay();
void onVsync(nsecs_t timestamp);
nsecs_t getVsyncPeriodFromHWC() const;
@@ -273,7 +275,7 @@
std::atomic<nsecs_t> mLastHwVsync = 0;
- // TODO(b/74619554): Remove special cases for primary display.
+ // TODO(b/182939859): Remove special cases for primary display.
const bool mIsPrimary;
uint32_t mFlags = 0;
@@ -311,7 +313,7 @@
int32_t sequenceId = sNextSequenceId++;
std::optional<Physical> physical;
sp<IGraphicBufferProducer> surface;
- ui::LayerStack layerStack = ui::NO_LAYER_STACK;
+ ui::LayerStack layerStack;
uint32_t flags = 0;
Rect layerStackSpaceRect;
Rect orientedDisplaySpaceRect;
@@ -354,16 +356,4 @@
DisplayModeId activeModeId;
};
-// Predicates for display lookup.
-
-struct WithLayerStack {
- explicit WithLayerStack(ui::LayerStack layerStack) : layerStack(layerStack) {}
-
- bool operator()(const DisplayDevice& display) const {
- return display.getLayerStack() == layerStack;
- }
-
- ui::LayerStack layerStack;
-};
-
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
new file mode 100644
index 0000000..c9b6144
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -0,0 +1,1019 @@
+/*
+ * 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 "HwcComposer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "AidlComposerHal.h"
+
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_manager.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <aidl/android/hardware/graphics/composer3/BnComposerCallback.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_vec;
+using hardware::Return;
+
+using aidl::android::hardware::graphics::composer3::BnComposerCallback;
+using aidl::android::hardware::graphics::composer3::Capability;
+using aidl::android::hardware::graphics::composer3::PowerMode;
+using aidl::android::hardware::graphics::composer3::VirtualDisplay;
+
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
+
+using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
+using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
+using AidlDisplayIdentification =
+ aidl::android::hardware::graphics::composer3::DisplayIdentification;
+using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::DisplayContentSample;
+using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
+using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
+using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
+using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
+using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
+using AidlPerFrameMetadataBlob = aidl::android::hardware::graphics::composer3::PerFrameMetadataBlob;
+using AidlRenderIntent = aidl::android::hardware::graphics::composer3::RenderIntent;
+using AidlVsyncPeriodChangeConstraints =
+ aidl::android::hardware::graphics::composer3::VsyncPeriodChangeConstraints;
+using AidlVsyncPeriodChangeTimeline =
+ aidl::android::hardware::graphics::composer3::VsyncPeriodChangeTimeline;
+using AidlLayerGenericMetadataKey =
+ aidl::android::hardware::graphics::composer3::LayerGenericMetadataKey;
+using AidlDisplayContentSamplingAttributes =
+ aidl::android::hardware::graphics::composer3::DisplayContentSamplingAttributes;
+using AidlFormatColorComponent = aidl::android::hardware::graphics::composer3::FormatColorComponent;
+using AidlDisplayConnectionType =
+ aidl::android::hardware::graphics::composer3::DisplayConnectionType;
+using AidlIComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+
+using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+using AidlFRect = aidl::android::hardware::graphics::common::FRect;
+using AidlRect = aidl::android::hardware::graphics::common::Rect;
+using AidlTransform = aidl::android::hardware::graphics::common::Transform;
+
+namespace Hwc2 {
+
+namespace {
+
+template <typename To, typename From>
+To translate(From x) {
+ return static_cast<To>(x);
+}
+
+template <typename To, typename From>
+std::vector<To> translate(const std::vector<From>& in) {
+ std::vector<To> out;
+ out.reserve(in.size());
+ std::transform(in.begin(), in.end(), std::back_inserter(out),
+ [](From x) { return translate<To>(x); });
+ return out;
+}
+
+template <>
+AidlRect translate(IComposerClient::Rect x) {
+ return AidlRect{
+ .left = x.left,
+ .top = x.top,
+ .right = x.right,
+ .bottom = x.bottom,
+ };
+}
+
+template <>
+AidlFRect translate(IComposerClient::FRect x) {
+ return AidlFRect{
+ .left = x.left,
+ .top = x.top,
+ .right = x.right,
+ .bottom = x.bottom,
+ };
+}
+
+template <>
+Color translate(IComposerClient::Color x) {
+ return Color{
+ .r = static_cast<int8_t>(x.r),
+ .g = static_cast<int8_t>(x.g),
+ .b = static_cast<int8_t>(x.b),
+ .a = static_cast<int8_t>(x.a),
+ };
+}
+
+template <>
+AidlPerFrameMetadataBlob translate(IComposerClient::PerFrameMetadataBlob x) {
+ AidlPerFrameMetadataBlob blob;
+ blob.key = translate<AidlPerFrameMetadataKey>(x.key),
+ std::copy(blob.blob.begin(), blob.blob.end(), x.blob.begin());
+ return blob;
+}
+
+template <>
+AidlPerFrameMetadata translate(IComposerClient::PerFrameMetadata x) {
+ return AidlPerFrameMetadata{
+ .key = translate<AidlPerFrameMetadataKey>(x.key),
+ .value = x.value,
+ };
+}
+
+template <>
+DisplayedFrameStats translate(AidlDisplayContentSample x) {
+ return DisplayedFrameStats{
+ .numFrames = static_cast<uint64_t>(x.frameCount),
+ .component_0_sample = translate<uint64_t>(x.sampleComponent0),
+ .component_1_sample = translate<uint64_t>(x.sampleComponent1),
+ .component_2_sample = translate<uint64_t>(x.sampleComponent2),
+ .component_3_sample = translate<uint64_t>(x.sampleComponent3),
+ };
+}
+
+template <>
+AidlVsyncPeriodChangeConstraints translate(IComposerClient::VsyncPeriodChangeConstraints x) {
+ return AidlVsyncPeriodChangeConstraints{
+ .desiredTimeNanos = x.desiredTimeNanos,
+ .seamlessRequired = x.seamlessRequired,
+ };
+}
+
+template <>
+VsyncPeriodChangeTimeline translate(AidlVsyncPeriodChangeTimeline x) {
+ return VsyncPeriodChangeTimeline{
+ .newVsyncAppliedTimeNanos = x.newVsyncAppliedTimeNanos,
+ .refreshRequired = x.refreshRequired,
+ .refreshTimeNanos = x.refreshTimeNanos,
+ };
+}
+
+template <>
+IComposerClient::LayerGenericMetadataKey translate(AidlLayerGenericMetadataKey x) {
+ return IComposerClient::LayerGenericMetadataKey{
+ .name = x.name,
+ .mandatory = x.mandatory,
+ };
+}
+
+template <>
+IComposerClient::ClientTargetProperty translate(ClientTargetProperty x) {
+ return IComposerClient::ClientTargetProperty{
+ .pixelFormat = translate<PixelFormat>(x.pixelFormat),
+ .dataspace = translate<Dataspace>(x.dataspace),
+ };
+}
+
+mat4 makeMat4(std::vector<float> in) {
+ return mat4(static_cast<const float*>(in.data()));
+}
+
+} // namespace
+
+class AidlIComposerCallbackWrapper : public BnComposerCallback {
+public:
+ AidlIComposerCallbackWrapper(sp<V2_4::IComposerCallback> callback)
+ : mCallback(std::move(callback)) {}
+
+ ::ndk::ScopedAStatus onHotplug(int64_t in_display, bool in_connected) override {
+ const auto connection = in_connected ? V2_4::IComposerCallback::Connection::CONNECTED
+ : V2_4::IComposerCallback::Connection::DISCONNECTED;
+ mCallback->onHotplug(translate<Display>(in_display), connection);
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ ::ndk::ScopedAStatus onRefresh(int64_t in_display) override {
+ mCallback->onRefresh(translate<Display>(in_display));
+ return ::ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onSeamlessPossible(int64_t in_display) override {
+ mCallback->onSeamlessPossible(translate<Display>(in_display));
+ return ::ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onVsync(int64_t in_display, int64_t in_timestamp,
+ int32_t in_vsyncPeriodNanos) override {
+ mCallback->onVsync_2_4(translate<Display>(in_display), in_timestamp,
+ static_cast<uint32_t>(in_vsyncPeriodNanos));
+ return ::ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onVsyncPeriodTimingChanged(
+ int64_t in_display, const AidlVsyncPeriodChangeTimeline& in_updatedTimeline) override {
+ mCallback->onVsyncPeriodTimingChanged(translate<Display>(in_display),
+ translate<V2_4::VsyncPeriodChangeTimeline>(
+ in_updatedTimeline));
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+private:
+ sp<V2_4::IComposerCallback> mCallback;
+};
+
+std::string AidlComposer::instance(const std::string& serviceName) {
+ return std::string(AidlIComposer::descriptor) + "/" + serviceName;
+}
+
+bool AidlComposer::isDeclared(const std::string& serviceName) {
+ return AServiceManager_isDeclared(instance(serviceName).c_str());
+}
+
+AidlComposer::AidlComposer(const std::string& serviceName) {
+ // This only waits if the service is actually declared
+ mAidlComposer = AidlIComposer::fromBinder(
+ ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str())));
+ if (!mAidlComposer) {
+ LOG_ALWAYS_FATAL("Failed to get AIDL composer service");
+ return;
+ }
+
+ if (!mAidlComposer->createClient(&mAidlComposerClient).isOk()) {
+ LOG_ALWAYS_FATAL("Can't create AidlComposerClient, fallback to HIDL");
+ return;
+ }
+
+ ALOGI("Loaded AIDL composer3 HAL service");
+}
+
+AidlComposer::~AidlComposer() = default;
+
+std::vector<IComposer::Capability> AidlComposer::getCapabilities() {
+ std::vector<Capability> capabilities;
+ const auto status = mAidlComposer->getCapabilities(&capabilities);
+ if (!status.isOk()) {
+ ALOGE("getCapabilities failed %s", status.getDescription().c_str());
+ return {};
+ }
+ return translate<IComposer::Capability>(capabilities);
+}
+
+std::string AidlComposer::dumpDebugInfo() {
+ std::string info;
+ const auto status = mAidlComposer->dumpDebugInfo(&info);
+ if (!status.isOk()) {
+ ALOGE("dumpDebugInfo failed %s", status.getDescription().c_str());
+ return {};
+ }
+ return info;
+}
+
+void AidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
+ if (mAidlComposerCallback) {
+ ALOGE("Callback already registered");
+ }
+ mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback);
+ AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2);
+
+ const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback);
+ if (!status.isOk()) {
+ ALOGE("registerCallback failed %s", status.getDescription().c_str());
+ }
+}
+
+void AidlComposer::resetCommands() {
+ mWriter.reset();
+}
+
+Error AidlComposer::executeCommands() {
+ return execute();
+}
+
+uint32_t AidlComposer::getMaxVirtualDisplayCount() {
+ int32_t count = 0;
+ const auto status = mAidlComposerClient->getMaxVirtualDisplayCount(&count);
+ if (!status.isOk()) {
+ ALOGE("getMaxVirtualDisplayCount failed %s", status.getDescription().c_str());
+ return 0;
+ }
+ return static_cast<uint32_t>(count);
+}
+
+Error AidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) {
+ using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+ const int32_t bufferSlotCount = 1;
+ VirtualDisplay virtualDisplay;
+ const auto status =
+ mAidlComposerClient->createVirtualDisplay(static_cast<int32_t>(width),
+ static_cast<int32_t>(height),
+ static_cast<AidlPixelFormat>(*format),
+ bufferSlotCount, &virtualDisplay);
+
+ if (!status.isOk()) {
+ ALOGE("createVirtualDisplay failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ *outDisplay = translate<Display>(virtualDisplay.display);
+ *format = static_cast<PixelFormat>(virtualDisplay.format);
+ return Error::NONE;
+}
+
+Error AidlComposer::destroyVirtualDisplay(Display display) {
+ const auto status = mAidlComposerClient->destroyVirtualDisplay(translate<int64_t>(display));
+ if (!status.isOk()) {
+ ALOGE("destroyVirtualDisplay failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::acceptDisplayChanges(Display display) {
+ mWriter.acceptDisplayChanges(translate<int64_t>(display));
+ return Error::NONE;
+}
+
+Error AidlComposer::createLayer(Display display, Layer* outLayer) {
+ int64_t layer;
+ const auto status = mAidlComposerClient->createLayer(translate<int64_t>(display),
+ kMaxLayerBufferCount, &layer);
+ if (!status.isOk()) {
+ ALOGE("createLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ *outLayer = translate<Layer>(layer);
+ return Error::NONE;
+}
+
+Error AidlComposer::destroyLayer(Display display, Layer layer) {
+ const auto status = mAidlComposerClient->destroyLayer(translate<int64_t>(display),
+ translate<int64_t>(layer));
+ if (!status.isOk()) {
+ ALOGE("destroyLayer failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::getActiveConfig(Display display, Config* outConfig) {
+ int32_t config;
+ const auto status = mAidlComposerClient->getActiveConfig(translate<int64_t>(display), &config);
+ if (!status.isOk()) {
+ ALOGE("getActiveConfig failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outConfig = translate<Config>(config);
+ return Error::NONE;
+}
+
+Error AidlComposer::getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+ std::vector<int64_t> layers;
+ mReader.takeChangedCompositionTypes(translate<int64_t>(display), &layers, outTypes);
+
+ *outLayers = translate<Layer>(layers);
+ return Error::NONE;
+}
+
+Error AidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
+ std::vector<AidlColorMode> modes;
+ const auto status = mAidlComposerClient->getColorModes(translate<int64_t>(display), &modes);
+ if (!status.isOk()) {
+ ALOGE("getColorModes failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outModes = translate<ColorMode>(modes);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue) {
+ const auto status =
+ mAidlComposerClient->getDisplayAttribute(translate<int64_t>(display),
+ translate<int32_t>(config),
+ static_cast<AidlDisplayAttribute>(attribute),
+ outValue);
+ if (!status.isOk()) {
+ ALOGE("getDisplayAttribute failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
+ std::vector<int32_t> configs;
+ const auto status =
+ mAidlComposerClient->getDisplayConfigs(translate<int64_t>(display), &configs);
+ if (!status.isOk()) {
+ ALOGE("getDisplayConfigs failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outConfigs = translate<Config>(configs);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayName(Display display, std::string* outName) {
+ const auto status = mAidlComposerClient->getDisplayName(translate<int64_t>(display), outName);
+ if (!status.isOk()) {
+ ALOGE("getDisplayName failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks) {
+ std::vector<int64_t> layers;
+ mReader.takeDisplayRequests(translate<int64_t>(display), outDisplayRequestMask, &layers,
+ outLayerRequestMasks);
+ *outLayers = translate<Layer>(layers);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
+ std::vector<AidlDisplayCapability> capabilities;
+ const auto status =
+ mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
+ if (!status.isOk()) {
+ ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outSupport = std::find(capabilities.begin(), capabilities.end(),
+ AidlDisplayCapability::DOZE) != capabilities.end();
+ return Error::NONE;
+}
+
+Error AidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ float* outMaxLuminance, float* outMaxAverageLuminance,
+ float* outMinLuminance) {
+ AidlHdrCapabilities capabilities;
+ const auto status =
+ mAidlComposerClient->getHdrCapabilities(translate<int64_t>(display), &capabilities);
+ if (!status.isOk()) {
+ ALOGE("getHdrCapabilities failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ *outTypes = translate<Hdr>(capabilities.types);
+ *outMaxLuminance = capabilities.maxLuminance;
+ *outMaxAverageLuminance = capabilities.maxAverageLuminance;
+ *outMinLuminance = capabilities.minLuminance;
+ return Error::NONE;
+}
+
+Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences) {
+ std::vector<int64_t> layers;
+ mReader.takeReleaseFences(translate<int64_t>(display), &layers, outReleaseFences);
+ *outLayers = translate<Layer>(layers);
+ return Error::NONE;
+}
+
+Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
+ ATRACE_NAME("HwcPresentDisplay");
+ mWriter.presentDisplay(translate<int64_t>(display));
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.takePresentFence(translate<int64_t>(display), outPresentFence);
+
+ return Error::NONE;
+}
+
+Error AidlComposer::setActiveConfig(Display display, Config config) {
+ const auto status = mAidlComposerClient->setActiveConfig(translate<int64_t>(display),
+ translate<int32_t>(config));
+ if (!status.isOk()) {
+ ALOGE("setActiveConfig failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage) {
+ const native_handle_t* handle = nullptr;
+ if (target.get()) {
+ handle = target->getNativeBuffer()->handle;
+ }
+
+ mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
+ translate<aidl::android::hardware::graphics::common::Dataspace>(
+ dataspace),
+ translate<AidlRect>(damage));
+ return Error::NONE;
+}
+
+Error AidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
+ const auto status =
+ mAidlComposerClient->setColorMode(translate<int64_t>(display),
+ translate<AidlColorMode>(mode),
+ translate<AidlRenderIntent>(renderIntent));
+ if (!status.isOk()) {
+ ALOGE("setColorMode failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::setColorTransform(Display display, const float* matrix, ColorTransform hint) {
+ mWriter.setColorTransform(translate<int64_t>(display), matrix,
+ translate<AidlColorTransform>(hint));
+ return Error::NONE;
+}
+
+Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) {
+ mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
+ return Error::NONE;
+}
+
+Error AidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+ const auto status = mAidlComposerClient->setPowerMode(translate<int64_t>(display),
+ translate<PowerMode>(mode));
+ if (!status.isOk()) {
+ ALOGE("setPowerMode failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+ const bool enableVsync = enabled == IComposerClient::Vsync::ENABLE;
+ const auto status =
+ mAidlComposerClient->setVsyncEnabled(translate<int64_t>(display), enableVsync);
+ if (!status.isOk()) {
+ ALOGE("setVsyncEnabled failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::setClientTargetSlotCount(Display display) {
+ const int32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
+ const auto status = mAidlComposerClient->setClientTargetSlotCount(translate<int64_t>(display),
+ bufferSlotCount);
+ if (!status.isOk()) {
+ ALOGE("setClientTargetSlotCount failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
+ ATRACE_NAME("HwcValidateDisplay");
+ mWriter.validateDisplay(translate<int64_t>(display));
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+
+ return Error::NONE;
+}
+
+Error AidlComposer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) {
+ ATRACE_NAME("HwcPresentOrValidateDisplay");
+ mWriter.presentOrvalidateDisplay(translate<int64_t>(display));
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.takePresentOrValidateStage(translate<int64_t>(display), state);
+
+ if (*state == 1) { // Present succeeded
+ mReader.takePresentFence(translate<int64_t>(display), outPresentFence);
+ }
+
+ if (*state == 0) { // Validate succeeded.
+ mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
+ }
+
+ return Error::NONE;
+}
+
+Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
+ mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
+ const sp<GraphicBuffer>& buffer, int acquireFence) {
+ const native_handle_t* handle = nullptr;
+ if (buffer.get()) {
+ handle = buffer->getNativeBuffer()->handle;
+ }
+
+ mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle,
+ acquireFence);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage) {
+ mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(damage));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
+ IComposerClient::BlendMode mode) {
+ mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<BlendMode>(mode));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerColor(Display display, Layer layer,
+ const IComposerClient::Color& color) {
+ mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<Color>(color));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
+ mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlDataspace>(dataspace));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame) {
+ mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(frame));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
+ mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) {
+ mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop) {
+ mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlFRect>(crop));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
+ mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlTransform>(transform));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible) {
+ mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(visible));
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+ mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
+ return Error::NONE;
+}
+
+Error AidlComposer::execute() {
+ const auto& commands = mWriter.getPendingCommands();
+ if (commands.empty()) {
+ mWriter.reset();
+ return Error::NONE;
+ }
+
+ std::vector<CommandResultPayload> results;
+ auto status = mAidlComposerClient->executeCommands(commands, &results);
+ if (!status.isOk()) {
+ ALOGE("executeCommands failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ mReader.parse(results);
+ const auto commandErrors = mReader.takeErrors();
+ Error error = Error::NONE;
+ for (const auto& cmdErr : commandErrors) {
+ const auto index = static_cast<size_t>(cmdErr.commandIndex);
+ if (index < 0 || index >= commands.size()) {
+ ALOGE("invalid command index %zu", index);
+ return Error::BAD_PARAMETER;
+ }
+
+ const auto& command = commands[index];
+ if (command.validateDisplay || command.presentDisplay || command.presentOrValidateDisplay) {
+ error = translate<Error>(cmdErr.errorCode);
+ } else {
+ ALOGW("command '%s' generated error %" PRId32, command.toString().c_str(),
+ cmdErr.errorCode);
+ }
+ }
+
+ mWriter.reset();
+
+ return error;
+}
+
+Error AidlComposer::setLayerPerFrameMetadata(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
+ mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlPerFrameMetadata>(perFrameMetadatas));
+ return Error::NONE;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> AidlComposer::getPerFrameMetadataKeys(
+ Display display) {
+ std::vector<AidlPerFrameMetadataKey> keys;
+ const auto status =
+ mAidlComposerClient->getPerFrameMetadataKeys(translate<int64_t>(display), &keys);
+ if (!status.isOk()) {
+ ALOGE("getPerFrameMetadataKeys failed %s", status.getDescription().c_str());
+ return {};
+ }
+ return translate<IComposerClient::PerFrameMetadataKey>(keys);
+}
+
+Error AidlComposer::getRenderIntents(Display display, ColorMode colorMode,
+ std::vector<RenderIntent>* outRenderIntents) {
+ std::vector<AidlRenderIntent> renderIntents;
+ const auto status = mAidlComposerClient->getRenderIntents(translate<int64_t>(display),
+ translate<AidlColorMode>(colorMode),
+ &renderIntents);
+ if (!status.isOk()) {
+ ALOGE("getRenderIntents failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outRenderIntents = translate<RenderIntent>(renderIntents);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) {
+ std::vector<float> matrix;
+ const auto status =
+ mAidlComposerClient->getDataspaceSaturationMatrix(translate<AidlDataspace>(dataspace),
+ &matrix);
+ if (!status.isOk()) {
+ ALOGE("getDataspaceSaturationMatrix failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outMatrix = makeMat4(matrix);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) {
+ AidlDisplayIdentification displayIdentification;
+ const auto status =
+ mAidlComposerClient->getDisplayIdentificationData(translate<int64_t>(display),
+ &displayIdentification);
+ if (!status.isOk()) {
+ ALOGE("getDisplayIdentificationData failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ *outPort = static_cast<uint8_t>(displayIdentification.port);
+ *outData = displayIdentification.data;
+
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
+ mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix);
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return Error::BAD_PARAMETER;
+ }
+
+ AidlDisplayContentSamplingAttributes attributes;
+ const auto status =
+ mAidlComposerClient->getDisplayedContentSamplingAttributes(translate<int64_t>(display),
+ &attributes);
+ if (!status.isOk()) {
+ ALOGE("getDisplayedContentSamplingAttributes failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+
+ *outFormat = translate<PixelFormat>(attributes.format);
+ *outDataspace = translate<Dataspace>(attributes.dataspace);
+ *outComponentMask = static_cast<uint8_t>(attributes.componentMask);
+ return Error::NONE;
+}
+
+Error AidlComposer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) {
+ const auto status =
+ mAidlComposerClient
+ ->setDisplayedContentSamplingEnabled(translate<int64_t>(display), enabled,
+ static_cast<AidlFormatColorComponent>(
+ componentMask),
+ static_cast<int64_t>(maxFrames));
+ if (!status.isOk()) {
+ ALOGE("setDisplayedContentSamplingEnabled failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
+ uint64_t timestamp, DisplayedFrameStats* outStats) {
+ if (!outStats) {
+ return Error::BAD_PARAMETER;
+ }
+
+ AidlDisplayContentSample sample;
+ const auto status =
+ mAidlComposerClient->getDisplayedContentSample(translate<int64_t>(display),
+ static_cast<int64_t>(maxFrames),
+ static_cast<int64_t>(timestamp),
+ &sample);
+ if (!status.isOk()) {
+ ALOGE("getDisplayedContentSample failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outStats = translate<DisplayedFrameStats>(sample);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+ mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlPerFrameMetadataBlob>(metadata));
+ return Error::NONE;
+}
+
+Error AidlComposer::setDisplayBrightness(Display display, float brightness) {
+ const auto status =
+ mAidlComposerClient->setDisplayBrightness(translate<int64_t>(display), brightness);
+ if (!status.isOk()) {
+ ALOGE("setDisplayBrightness failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ return Error::NONE;
+}
+
+Error AidlComposer::getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) {
+ std::vector<AidlDisplayCapability> capabilities;
+ const auto status =
+ mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
+ if (!status.isOk()) {
+ ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
+ return static_cast<Error>(status.getServiceSpecificError());
+ }
+ *outCapabilities = translate<DisplayCapability>(capabilities);
+ return Error::NONE;
+}
+
+V2_4::Error AidlComposer::getDisplayConnectionType(
+ Display display, IComposerClient::DisplayConnectionType* outType) {
+ AidlDisplayConnectionType type;
+ const auto status =
+ mAidlComposerClient->getDisplayConnectionType(translate<int64_t>(display), &type);
+ if (!status.isOk()) {
+ ALOGE("getDisplayConnectionType failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ *outType = translate<IComposerClient::DisplayConnectionType>(type);
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+ int32_t vsyncPeriod;
+ const auto status =
+ mAidlComposerClient->getDisplayVsyncPeriod(translate<int64_t>(display), &vsyncPeriod);
+ if (!status.isOk()) {
+ ALOGE("getDisplayVsyncPeriod failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ *outVsyncPeriod = translate<VsyncPeriodNanos>(vsyncPeriod);
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ AidlVsyncPeriodChangeTimeline timeline;
+ const auto status =
+ mAidlComposerClient
+ ->setActiveConfigWithConstraints(translate<int64_t>(display),
+ translate<int32_t>(config),
+ translate<AidlVsyncPeriodChangeConstraints>(
+ vsyncPeriodChangeConstraints),
+ &timeline);
+ if (!status.isOk()) {
+ ALOGE("setActiveConfigWithConstraints failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ *outTimeline = translate<VsyncPeriodChangeTimeline>(timeline);
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setAutoLowLatencyMode(Display display, bool on) {
+ const auto status = mAidlComposerClient->setAutoLowLatencyMode(translate<int64_t>(display), on);
+ if (!status.isOk()) {
+ ALOGE("setAutoLowLatencyMode failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::getSupportedContentTypes(
+ Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+ std::vector<AidlContentType> types;
+ const auto status =
+ mAidlComposerClient->getSupportedContentTypes(translate<int64_t>(displayId), &types);
+ if (!status.isOk()) {
+ ALOGE("getSupportedContentTypes failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ *outSupportedContentTypes = translate<IComposerClient::ContentType>(types);
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setContentType(Display display,
+ IComposerClient::ContentType contentType) {
+ const auto status =
+ mAidlComposerClient->setContentType(translate<int64_t>(display),
+ translate<AidlContentType>(contentType));
+ if (!status.isOk()) {
+ ALOGE("setContentType failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::setLayerGenericMetadata(Display display, Layer layer,
+ const std::string& key, bool mandatory,
+ const std::vector<uint8_t>& value) {
+ mWriter.setLayerGenericMetadata(translate<int64_t>(display), translate<int64_t>(layer), key,
+ mandatory, value);
+ return V2_4::Error::NONE;
+}
+
+V2_4::Error AidlComposer::getLayerGenericMetadataKeys(
+ std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+ std::vector<AidlLayerGenericMetadataKey> keys;
+ const auto status = mAidlComposerClient->getLayerGenericMetadataKeys(&keys);
+ if (!status.isOk()) {
+ ALOGE("getLayerGenericMetadataKeys failed %s", status.getDescription().c_str());
+ return static_cast<V2_4::Error>(status.getServiceSpecificError());
+ }
+ *outKeys = translate<IComposerClient::LayerGenericMetadataKey>(keys);
+ return V2_4::Error::NONE;
+}
+
+Error AidlComposer::getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* whitePointNits) {
+ ClientTargetProperty property;
+ mReader.takeClientTargetProperty(translate<int64_t>(display), &property, whitePointNits);
+ *outClientTargetProperty = translate<IComposerClient::ClientTargetProperty>(property);
+ return Error::NONE;
+}
+
+Error AidlComposer::setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) {
+ mWriter.setLayerWhitePointNits(translate<int64_t>(display), translate<int64_t>(layer),
+ whitePointNits);
+ return Error::NONE;
+}
+
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
new file mode 100644
index 0000000..057ba89
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -0,0 +1,233 @@
+/*
+ * 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 "ComposerHal.h"
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+
+#include <aidl/android/hardware/graphics/composer3/IComposer.h>
+#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
+#include <android/hardware/graphics/composer3/command-buffer.h>
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+
+namespace android::Hwc2 {
+
+using AidlCommandWriterBase = aidl::android::hardware::graphics::composer3::CommandWriterBase;
+using AidlCommandReaderBase = aidl::android::hardware::graphics::composer3::CommandReaderBase;
+
+class AidlIComposerCallbackWrapper;
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class AidlComposer final : public Hwc2::Composer {
+public:
+ static bool isDeclared(const std::string& serviceName);
+
+ explicit AidlComposer(const std::string& serviceName);
+ ~AidlComposer() override;
+
+ std::vector<IComposer::Capability> getCapabilities() override;
+ std::string dumpDebugInfo() override;
+
+ void registerCallback(const sp<IComposerCallback>& callback) override;
+
+ // Reset all pending commands in the command buffer. Useful if you want to
+ // skip a frame but have already queued some commands.
+ void resetCommands() override;
+
+ // Explicitly flush all pending commands in the command buffer.
+ Error executeCommands() override;
+
+ uint32_t getMaxVirtualDisplayCount() override;
+ Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) override;
+ Error destroyVirtualDisplay(Display display) override;
+
+ Error acceptDisplayChanges(Display display) override;
+
+ Error createLayer(Display display, Layer* outLayer) override;
+ Error destroyLayer(Display display, Layer layer) override;
+
+ Error getActiveConfig(Display display, Config* outConfig) override;
+ Error getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
+ Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
+ Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayName(Display display, std::string* outName) override;
+
+ Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks) override;
+
+ Error getDozeSupport(Display display, bool* outSupport) override;
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+ Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences) override;
+
+ Error presentDisplay(Display display, int* outPresentFence) override;
+
+ Error setActiveConfig(Display display, Config config) override;
+
+ /*
+ * The composer caches client targets internally. When target is nullptr,
+ * the composer uses slot to look up the client target from its cache.
+ * When target is not nullptr, the cache is updated with the new target.
+ */
+ Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage) override;
+ Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
+ Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
+ Error setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) override;
+ Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+ Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+ Error setClientTargetSlotCount(Display display) override;
+
+ Error validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) override;
+
+ Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
+ int* outPresentFence, uint32_t* state) override;
+
+ Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+ /* see setClientTarget for the purpose of slot */
+ Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+ const sp<GraphicBuffer>& buffer, int acquireFence) override;
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage) override;
+ Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
+ Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
+ Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) override;
+ Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
+ Error setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame) override;
+ Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+ Error setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) override;
+ Error setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop) override;
+ Error setLayerTransform(Display display, Layer layer, Transform transform) override;
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible) override;
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+ // Composer HAL 2.2
+ Error setLayerPerFrameMetadata(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
+ std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+ Display display) override;
+ Error getRenderIntents(Display display, ColorMode colorMode,
+ std::vector<RenderIntent>* outRenderIntents) override;
+ Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
+
+ // Composer HAL 2.3
+ Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) override;
+ Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+ Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) override;
+ Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) override;
+ Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) override;
+ Error setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+ Error setDisplayBrightness(Display display, float brightness) override;
+
+ // Composer HAL 2.4
+ bool isVsyncPeriodSwitchSupported() override { return true; }
+ Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) override;
+ V2_4::Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+ V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) override;
+ V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+ bool mandatory, const std::vector<uint8_t>& value) override;
+ V2_4::Error getLayerGenericMetadataKeys(
+ std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outClientTargetWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+
+private:
+ // Many public functions above simply write a command into the command
+ // queue to batch the calls. validateDisplay and presentDisplay will call
+ // this function to execute the command queue.
+ Error execute();
+
+ // returns the default instance name for the given service
+ static std::string instance(const std::string& serviceName);
+
+ // 64KiB minus a small space for metadata such as read/write pointers
+ static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
+ // Max number of buffers that may be cached for a given layer
+ // We obtain this number by:
+ // 1. Tightly coupling this cache to the max size of BufferQueue
+ // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+ static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+ AidlCommandWriterBase mWriter;
+ AidlCommandReaderBase mReader;
+
+ // Aidl interface
+ using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
+ using AidlIComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
+ std::shared_ptr<AidlIComposer> mAidlComposer;
+ std::shared_ptr<AidlIComposerClient> mAidlComposerClient;
+ std::shared_ptr<AidlIComposerCallbackWrapper> mAidlComposerCallback;
+};
+
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 09734c2..d69a923 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -14,1562 +14,23 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "HwcComposer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "ComposerHal.h"
+#include "AidlComposerHal.h"
+#include "HidlComposerHal.h"
-#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
-#include <hidl/HidlTransportSupport.h>
-#include <hidl/HidlTransportUtils.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-#include <algorithm>
-#include <cinttypes>
-
-namespace android {
-
-using hardware::Return;
-using hardware::hidl_vec;
-using hardware::hidl_handle;
-
-namespace Hwc2 {
+namespace android::Hwc2 {
Composer::~Composer() = default;
-namespace {
-
-class BufferHandle {
-public:
- explicit BufferHandle(const native_handle_t* buffer) {
- // nullptr is not a valid handle to HIDL
- mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+std::unique_ptr<Composer> Composer::create(const std::string& serviceName) {
+ if (AidlComposer::isDeclared(serviceName)) {
+ return std::make_unique<AidlComposer>(serviceName);
}
- operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
- {
- return mHandle;
- }
-
-private:
- NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
- hidl_handle mHandle;
-};
-
-class FenceHandle
-{
-public:
- FenceHandle(int fd, bool owned)
- : mOwned(owned)
- {
- native_handle_t* handle;
- if (fd >= 0) {
- handle = native_handle_init(mStorage, 1, 0);
- handle->data[0] = fd;
- } else {
- // nullptr is not a valid handle to HIDL
- handle = native_handle_init(mStorage, 0, 0);
- }
- mHandle = handle;
- }
-
- ~FenceHandle()
- {
- if (mOwned) {
- native_handle_close(mHandle);
- }
- }
-
- operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
- {
- return mHandle;
- }
-
-private:
- bool mOwned;
- NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
- hidl_handle mHandle;
-};
-
-// assume NO_RESOURCES when Status::isOk returns false
-constexpr Error kDefaultError = Error::NO_RESOURCES;
-constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
-
-template<typename T, typename U>
-T unwrapRet(Return<T>& ret, const U& default_val)
-{
- return (ret.isOk()) ? static_cast<T>(ret) :
- static_cast<T>(default_val);
+ return std::make_unique<HidlComposer>(serviceName);
}
-Error unwrapRet(Return<Error>& ret)
-{
- return unwrapRet(ret, kDefaultError);
-}
-
-} // anonymous namespace
-
-namespace impl {
-
-Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
- mComposer = V2_1::IComposer::getService(serviceName);
-
- if (mComposer == nullptr) {
- LOG_ALWAYS_FATAL("failed to get hwcomposer service");
- }
-
- if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
- composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError == V2_4::Error::NONE) {
- mClient = tmpClient;
- mClient_2_2 = tmpClient;
- mClient_2_3 = tmpClient;
- mClient_2_4 = tmpClient;
- }
- });
- } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
- composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError == Error::NONE) {
- mClient = tmpClient;
- mClient_2_2 = tmpClient;
- mClient_2_3 = tmpClient;
- }
- });
- } else {
- mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
- if (tmpError != Error::NONE) {
- return;
- }
-
- mClient = tmpClient;
- if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
- mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
- LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
- "IComposer 2.2 did not return IComposerClient 2.2");
- }
- });
- }
-
- if (mClient == nullptr) {
- LOG_ALWAYS_FATAL("failed to create composer client");
- }
-}
-
-Composer::~Composer() = default;
-
-std::vector<IComposer::Capability> Composer::getCapabilities()
-{
- std::vector<IComposer::Capability> capabilities;
- mComposer->getCapabilities(
- [&](const auto& tmpCapabilities) {
- capabilities = tmpCapabilities;
- });
- return capabilities;
-}
-
-std::string Composer::dumpDebugInfo()
-{
- std::string info;
- mComposer->dumpDebugInfo([&](const auto& tmpInfo) {
- info = tmpInfo.c_str();
- });
-
- return info;
-}
-
-void Composer::registerCallback(const sp<IComposerCallback>& callback)
-{
- android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
- auto ret = [&]() {
- if (mClient_2_4) {
- return mClient_2_4->registerCallback_2_4(callback);
- }
- return mClient->registerCallback(callback);
- }();
- if (!ret.isOk()) {
- ALOGE("failed to register IComposerCallback");
- }
-}
-
-void Composer::resetCommands() {
- mWriter.reset();
-}
-
-Error Composer::executeCommands() {
- return execute();
-}
-
-uint32_t Composer::getMaxVirtualDisplayCount()
-{
- auto ret = mClient->getMaxVirtualDisplayCount();
- return unwrapRet(ret, 0);
-}
-
-Error Composer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
- Display* outDisplay) {
- const uint32_t bufferSlotCount = 1;
- Error error = kDefaultError;
- if (mClient_2_2) {
- mClient_2_2->createVirtualDisplay_2_2(width, height,
- static_cast<types::V1_1::PixelFormat>(*format),
- bufferSlotCount,
- [&](const auto& tmpError, const auto& tmpDisplay,
- const auto& tmpFormat) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outDisplay = tmpDisplay;
- *format = static_cast<types::V1_2::PixelFormat>(
- tmpFormat);
- });
- } else {
- mClient->createVirtualDisplay(width, height,
- static_cast<types::V1_0::PixelFormat>(*format), bufferSlotCount,
- [&](const auto& tmpError, const auto& tmpDisplay,
- const auto& tmpFormat) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outDisplay = tmpDisplay;
- *format = static_cast<PixelFormat>(tmpFormat);
- });
- }
-
- return error;
-}
-
-Error Composer::destroyVirtualDisplay(Display display)
-{
- auto ret = mClient->destroyVirtualDisplay(display);
- return unwrapRet(ret);
-}
-
-Error Composer::acceptDisplayChanges(Display display)
-{
- mWriter.selectDisplay(display);
- mWriter.acceptDisplayChanges();
- return Error::NONE;
-}
-
-Error Composer::createLayer(Display display, Layer* outLayer)
-{
- Error error = kDefaultError;
- mClient->createLayer(display, kMaxLayerBufferCount,
- [&](const auto& tmpError, const auto& tmpLayer) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outLayer = tmpLayer;
- });
-
- return error;
-}
-
-Error Composer::destroyLayer(Display display, Layer layer)
-{
- auto ret = mClient->destroyLayer(display, layer);
- return unwrapRet(ret);
-}
-
-Error Composer::getActiveConfig(Display display, Config* outConfig)
-{
- Error error = kDefaultError;
- mClient->getActiveConfig(display,
- [&](const auto& tmpError, const auto& tmpConfig) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outConfig = tmpConfig;
- });
-
- return error;
-}
-
-Error Composer::getChangedCompositionTypes(Display display,
- std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes)
-{
- mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
- return Error::NONE;
-}
-
-Error Composer::getColorModes(Display display,
- std::vector<ColorMode>* outModes)
-{
- Error error = kDefaultError;
-
- if (mClient_2_3) {
- mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outModes = tmpModes;
- });
- } else if (mClient_2_2) {
- mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- for (types::V1_1::ColorMode colorMode : tmpModes) {
- outModes->push_back(static_cast<ColorMode>(colorMode));
- }
- });
- } else {
- mClient->getColorModes(display,
- [&](const auto& tmpError, const auto& tmpModes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
- for (types::V1_0::ColorMode colorMode : tmpModes) {
- outModes->push_back(static_cast<ColorMode>(colorMode));
- }
- });
- }
-
- return error;
-}
-
-Error Composer::getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute, int32_t* outValue)
-{
- Error error = kDefaultError;
- if (mClient_2_4) {
- mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
- [&](const auto& tmpError, const auto& tmpValue) {
- error = static_cast<Error>(tmpError);
- if (error != Error::NONE) {
- return;
- }
-
- *outValue = tmpValue;
- });
- } else {
- mClient->getDisplayAttribute(display, config,
- static_cast<V2_1::IComposerClient::Attribute>(attribute),
- [&](const auto& tmpError, const auto& tmpValue) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outValue = tmpValue;
- });
- }
-
- return error;
-}
-
-Error Composer::getDisplayConfigs(Display display,
- std::vector<Config>* outConfigs)
-{
- Error error = kDefaultError;
- mClient->getDisplayConfigs(display,
- [&](const auto& tmpError, const auto& tmpConfigs) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outConfigs = tmpConfigs;
- });
-
- return error;
-}
-
-Error Composer::getDisplayName(Display display, std::string* outName)
-{
- Error error = kDefaultError;
- mClient->getDisplayName(display,
- [&](const auto& tmpError, const auto& tmpName) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outName = tmpName.c_str();
- });
-
- return error;
-}
-
-Error Composer::getDisplayRequests(Display display,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks)
-{
- mReader.takeDisplayRequests(display, outDisplayRequestMask,
- outLayers, outLayerRequestMasks);
- return Error::NONE;
-}
-
-Error Composer::getDozeSupport(Display display, bool* outSupport)
-{
- Error error = kDefaultError;
- mClient->getDozeSupport(display,
- [&](const auto& tmpError, const auto& tmpSupport) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outSupport = tmpSupport;
- });
-
- return error;
-}
-
-Error Composer::getHdrCapabilities(Display display,
- std::vector<Hdr>* outTypes, float* outMaxLuminance,
- float* outMaxAverageLuminance, float* outMinLuminance)
-{
- Error error = kDefaultError;
- if (mClient_2_3) {
- mClient_2_3->getHdrCapabilities_2_3(display,
- [&](const auto& tmpError, const auto& tmpTypes,
- const auto& tmpMaxLuminance,
- const auto& tmpMaxAverageLuminance,
- const auto& tmpMinLuminance) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outTypes = tmpTypes;
- *outMaxLuminance = tmpMaxLuminance;
- *outMaxAverageLuminance = tmpMaxAverageLuminance;
- *outMinLuminance = tmpMinLuminance;
- });
- } else {
- mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
- const auto& tmpMaxLuminance,
- const auto& tmpMaxAverageLuminance,
- const auto& tmpMinLuminance) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- outTypes->clear();
- for (auto type : tmpTypes) {
- outTypes->push_back(static_cast<Hdr>(type));
- }
-
- *outMaxLuminance = tmpMaxLuminance;
- *outMaxAverageLuminance = tmpMaxAverageLuminance;
- *outMinLuminance = tmpMinLuminance;
- });
- }
-
- return error;
-}
-
-Error Composer::getReleaseFences(Display display,
- std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
-{
- mReader.takeReleaseFences(display, outLayers, outReleaseFences);
- return Error::NONE;
-}
-
-Error Composer::presentDisplay(Display display, int* outPresentFence)
-{
- ATRACE_NAME("HwcPresentDisplay");
- mWriter.selectDisplay(display);
- mWriter.presentDisplay();
-
- Error error = execute();
- if (error != Error::NONE) {
- return error;
- }
-
- mReader.takePresentFence(display, outPresentFence);
-
- return Error::NONE;
-}
-
-Error Composer::setActiveConfig(Display display, Config config)
-{
- auto ret = mClient->setActiveConfig(display, config);
- return unwrapRet(ret);
-}
-
-Error Composer::setClientTarget(Display display, uint32_t slot,
- const sp<GraphicBuffer>& target,
- int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage)
-{
- mWriter.selectDisplay(display);
-
- const native_handle_t* handle = nullptr;
- if (target.get()) {
- handle = target->getNativeBuffer()->handle;
- }
-
- mWriter.setClientTarget(slot, handle, acquireFence, dataspace, damage);
- return Error::NONE;
-}
-
-Error Composer::setColorMode(Display display, ColorMode mode,
- RenderIntent renderIntent)
-{
- hardware::Return<Error> ret(kDefaultError);
- if (mClient_2_3) {
- ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
- } else if (mClient_2_2) {
- ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
- renderIntent);
- } else {
- ret = mClient->setColorMode(display,
- static_cast<types::V1_0::ColorMode>(mode));
- }
- return unwrapRet(ret);
-}
-
-Error Composer::setColorTransform(Display display, const float* matrix,
- ColorTransform hint)
-{
- mWriter.selectDisplay(display);
- mWriter.setColorTransform(matrix, hint);
- return Error::NONE;
-}
-
-Error Composer::setOutputBuffer(Display display, const native_handle_t* buffer,
- int releaseFence)
-{
- mWriter.selectDisplay(display);
- mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
- return Error::NONE;
-}
-
-Error Composer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
- Return<Error> ret(Error::UNSUPPORTED);
- if (mClient_2_2) {
- ret = mClient_2_2->setPowerMode_2_2(display, mode);
- } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
- ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
- }
-
- return unwrapRet(ret);
-}
-
-Error Composer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled)
-{
- auto ret = mClient->setVsyncEnabled(display, enabled);
- return unwrapRet(ret);
-}
-
-Error Composer::setClientTargetSlotCount(Display display)
-{
- const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
- auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
- return unwrapRet(ret);
-}
-
-Error Composer::validateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests)
-{
- ATRACE_NAME("HwcValidateDisplay");
- mWriter.selectDisplay(display);
- mWriter.validateDisplay();
-
- Error error = execute();
- if (error != Error::NONE) {
- return error;
- }
-
- mReader.hasChanges(display, outNumTypes, outNumRequests);
-
- return Error::NONE;
-}
-
-Error Composer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests, int* outPresentFence, uint32_t* state) {
- ATRACE_NAME("HwcPresentOrValidateDisplay");
- mWriter.selectDisplay(display);
- mWriter.presentOrvalidateDisplay();
-
- Error error = execute();
- if (error != Error::NONE) {
- return error;
- }
-
- mReader.takePresentOrValidateStage(display, state);
-
- if (*state == 1) { // Present succeeded
- mReader.takePresentFence(display, outPresentFence);
- }
-
- if (*state == 0) { // Validate succeeded.
- mReader.hasChanges(display, outNumTypes, outNumRequests);
- }
-
- return Error::NONE;
-}
-
-Error Composer::setCursorPosition(Display display, Layer layer,
- int32_t x, int32_t y)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerCursorPosition(x, y);
- return Error::NONE;
-}
-
-Error Composer::setLayerBuffer(Display display, Layer layer,
- uint32_t slot, const sp<GraphicBuffer>& buffer, int acquireFence)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
-
- const native_handle_t* handle = nullptr;
- if (buffer.get()) {
- handle = buffer->getNativeBuffer()->handle;
- }
-
- mWriter.setLayerBuffer(slot, handle, acquireFence);
- return Error::NONE;
-}
-
-Error Composer::setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<IComposerClient::Rect>& damage)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerSurfaceDamage(damage);
- return Error::NONE;
-}
-
-Error Composer::setLayerBlendMode(Display display, Layer layer,
- IComposerClient::BlendMode mode)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerBlendMode(mode);
- return Error::NONE;
-}
-
-Error Composer::setLayerColor(Display display, Layer layer,
- const IComposerClient::Color& color)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerColor(color);
- return Error::NONE;
-}
-
-Error Composer::setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerCompositionType(type);
- return Error::NONE;
-}
-
-Error Composer::setLayerDataspace(Display display, Layer layer,
- Dataspace dataspace)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerDataspace(dataspace);
- return Error::NONE;
-}
-
-Error Composer::setLayerDisplayFrame(Display display, Layer layer,
- const IComposerClient::Rect& frame)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerDisplayFrame(frame);
- return Error::NONE;
-}
-
-Error Composer::setLayerPlaneAlpha(Display display, Layer layer,
- float alpha)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerPlaneAlpha(alpha);
- return Error::NONE;
-}
-
-Error Composer::setLayerSidebandStream(Display display, Layer layer,
- const native_handle_t* stream)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerSidebandStream(stream);
- return Error::NONE;
-}
-
-Error Composer::setLayerSourceCrop(Display display, Layer layer,
- const IComposerClient::FRect& crop)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerSourceCrop(crop);
- return Error::NONE;
-}
-
-Error Composer::setLayerTransform(Display display, Layer layer,
- Transform transform)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerTransform(transform);
- return Error::NONE;
-}
-
-Error Composer::setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<IComposerClient::Rect>& visible)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerVisibleRegion(visible);
- return Error::NONE;
-}
-
-Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
-{
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerZOrder(z);
- return Error::NONE;
-}
-
-Error Composer::execute()
-{
- // prepare input command queue
- bool queueChanged = false;
- uint32_t commandLength = 0;
- hidl_vec<hidl_handle> commandHandles;
- if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
- mWriter.reset();
- return Error::NO_RESOURCES;
- }
-
- // set up new input command queue if necessary
- if (queueChanged) {
- auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
- auto error = unwrapRet(ret);
- if (error != Error::NONE) {
- mWriter.reset();
- return error;
- }
- }
-
- if (commandLength == 0) {
- mWriter.reset();
- return Error::NONE;
- }
-
- Error error = kDefaultError;
- hardware::Return<void> ret;
- auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
- const auto& tmpOutLength, const auto& tmpOutHandles)
- {
- error = tmpError;
-
- // set up new output command queue if necessary
- if (error == Error::NONE && tmpOutChanged) {
- error = kDefaultError;
- mClient->getOutputCommandQueue(
- [&](const auto& tmpError,
- const auto& tmpDescriptor)
- {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- mReader.setMQDescriptor(tmpDescriptor);
- });
- }
-
- if (error != Error::NONE) {
- return;
- }
-
- if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
- error = mReader.parse();
- mReader.reset();
- } else {
- error = Error::NO_RESOURCES;
- }
- };
- if (mClient_2_2) {
- ret = mClient_2_2->executeCommands_2_2(commandLength, commandHandles, hidl_callback);
- } else {
- ret = mClient->executeCommands(commandLength, commandHandles, hidl_callback);
- }
- // executeCommands can fail because of out-of-fd and we do not want to
- // abort() in that case
- if (!ret.isOk()) {
- ALOGE("executeCommands failed because of %s", ret.description().c_str());
- }
-
- if (error == Error::NONE) {
- std::vector<CommandReader::CommandError> commandErrors =
- mReader.takeErrors();
-
- for (const auto& cmdErr : commandErrors) {
- auto command =
- static_cast<IComposerClient::Command>(mWriter.getCommand(cmdErr.location));
-
- if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
- command == IComposerClient::Command::PRESENT_DISPLAY ||
- command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
- error = cmdErr.error;
- } else {
- ALOGW("command 0x%x generated error %d",
- command, cmdErr.error);
- }
- }
- }
-
- mWriter.reset();
-
- return error;
-}
-
-// Composer HAL 2.2
-
-Error Composer::setLayerPerFrameMetadata(Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
- if (!mClient_2_2) {
- return Error::UNSUPPORTED;
- }
-
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerPerFrameMetadata(perFrameMetadatas);
- return Error::NONE;
-}
-
-std::vector<IComposerClient::PerFrameMetadataKey> Composer::getPerFrameMetadataKeys(
- Display display) {
- std::vector<IComposerClient::PerFrameMetadataKey> keys;
- if (!mClient_2_2) {
- return keys;
- }
-
- Error error = kDefaultError;
- if (mClient_2_3) {
- mClient_2_3->getPerFrameMetadataKeys_2_3(display,
- [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
- if (error != Error::NONE) {
- ALOGW("getPerFrameMetadataKeys failed "
- "with %d",
- tmpError);
- return;
- }
- keys = tmpKeys;
- });
- } else {
- mClient_2_2
- ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
- if (error != Error::NONE) {
- ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
- return;
- }
-
- keys.clear();
- for (auto key : tmpKeys) {
- keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
- }
- });
- }
-
- return keys;
-}
-
-Error Composer::getRenderIntents(Display display, ColorMode colorMode,
- std::vector<RenderIntent>* outRenderIntents) {
- if (!mClient_2_2) {
- outRenderIntents->push_back(RenderIntent::COLORIMETRIC);
- return Error::NONE;
- }
-
- Error error = kDefaultError;
-
- auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outRenderIntents = tmpKeys;
- };
-
- if (mClient_2_3) {
- mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
- } else {
- mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
- getRenderIntentsLambda);
- }
-
- return error;
-}
-
-Error Composer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix)
-{
- if (!mClient_2_2) {
- *outMatrix = mat4();
- return Error::NONE;
- }
-
- Error error = kDefaultError;
- mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
- [&](const auto& tmpError, const auto& tmpMatrix) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
- *outMatrix = mat4(tmpMatrix.data());
- });
-
- return error;
-}
-
-// Composer HAL 2.3
-
-Error Composer::getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
-
- Error error = kDefaultError;
- mClient_2_3->getDisplayIdentificationData(display,
- [&](const auto& tmpError, const auto& tmpPort,
- const auto& tmpData) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outPort = tmpPort;
- *outData = tmpData;
- });
-
- return error;
-}
-
-Error Composer::setLayerColorTransform(Display display, Layer layer, const float* matrix)
-{
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
-
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerColorTransform(matrix);
- return Error::NONE;
-}
-
-Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
- Dataspace* outDataspace,
- uint8_t* outComponentMask) {
- if (!outFormat || !outDataspace || !outComponentMask) {
- return Error::BAD_PARAMETER;
- }
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
- Error error = kDefaultError;
- mClient_2_3->getDisplayedContentSamplingAttributes(display,
- [&](const auto tmpError,
- const auto& tmpFormat,
- const auto& tmpDataspace,
- const auto& tmpComponentMask) {
- error = tmpError;
- if (error == Error::NONE) {
- *outFormat = tmpFormat;
- *outDataspace = tmpDataspace;
- *outComponentMask =
- static_cast<uint8_t>(
- tmpComponentMask);
- }
- });
- return error;
-}
-
-Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
- uint8_t componentMask, uint64_t maxFrames) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
-
- auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
- : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
- return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
- maxFrames);
-}
-
-Error Composer::getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
- DisplayedFrameStats* outStats) {
- if (!outStats) {
- return Error::BAD_PARAMETER;
- }
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
- Error error = kDefaultError;
- mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
- [&](const auto tmpError, auto tmpNumFrames,
- const auto& tmpSamples0, const auto& tmpSamples1,
- const auto& tmpSamples2, const auto& tmpSamples3) {
- error = tmpError;
- if (error == Error::NONE) {
- outStats->numFrames = tmpNumFrames;
- outStats->component_0_sample = tmpSamples0;
- outStats->component_1_sample = tmpSamples1;
- outStats->component_2_sample = tmpSamples2;
- outStats->component_3_sample = tmpSamples3;
- }
- });
- return error;
-}
-
-Error Composer::setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
-
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerPerFrameMetadataBlobs(metadata);
- return Error::NONE;
-}
-
-Error Composer::setDisplayBrightness(Display display, float brightness) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
- return mClient_2_3->setDisplayBrightness(display, brightness);
-}
-
-// Composer HAL 2.4
-
-Error Composer::getDisplayCapabilities(Display display,
- std::vector<DisplayCapability>* outCapabilities) {
- if (!mClient_2_3) {
- return Error::UNSUPPORTED;
- }
-
- V2_4::Error error = kDefaultError_2_4;
- if (mClient_2_4) {
- mClient_2_4->getDisplayCapabilities_2_4(display,
- [&](const auto& tmpError, const auto& tmpCaps) {
- error = tmpError;
- if (error != V2_4::Error::NONE) {
- return;
- }
- *outCapabilities = tmpCaps;
- });
- } else {
- mClient_2_3
- ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
- error = static_cast<V2_4::Error>(tmpError);
- if (error != V2_4::Error::NONE) {
- return;
- }
-
- outCapabilities->resize(tmpCaps.size());
- std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
- [](auto cap) { return static_cast<DisplayCapability>(cap); });
- });
- }
-
- return static_cast<Error>(error);
-}
-
-V2_4::Error Composer::getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- Error error = kDefaultError_2_4;
- mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
- error = tmpError;
- if (error != V2_4::Error::NONE) {
- return;
- }
-
- *outType = tmpType;
- });
-
- return error;
-}
-
-V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- Error error = kDefaultError_2_4;
- mClient_2_4->getDisplayVsyncPeriod(display,
- [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outVsyncPeriod = tmpVsyncPeriod;
- });
-
- return error;
-}
-
-V2_4::Error Composer::setActiveConfigWithConstraints(
- Display display, Config config,
- const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- VsyncPeriodChangeTimeline* outTimeline) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- Error error = kDefaultError_2_4;
- mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
- [&](const auto& tmpError, const auto& tmpTimeline) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outTimeline = tmpTimeline;
- });
-
- return error;
-}
-
-V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- return mClient_2_4->setAutoLowLatencyMode(display, on);
-}
-
-V2_4::Error Composer::getSupportedContentTypes(
- Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- Error error = kDefaultError_2_4;
- mClient_2_4->getSupportedContentTypes(displayId,
- [&](const auto& tmpError,
- const auto& tmpSupportedContentTypes) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outSupportedContentTypes = tmpSupportedContentTypes;
- });
- return error;
-}
-
-V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
-
- return mClient_2_4->setContentType(display, contentType);
-}
-
-V2_4::Error Composer::setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
- bool mandatory, const std::vector<uint8_t>& value) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
- mWriter.selectDisplay(display);
- mWriter.selectLayer(layer);
- mWriter.setLayerGenericMetadata(key, mandatory, value);
- return Error::NONE;
-}
-
-V2_4::Error Composer::getLayerGenericMetadataKeys(
- std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
- using Error = V2_4::Error;
- if (!mClient_2_4) {
- return Error::UNSUPPORTED;
- }
- Error error = kDefaultError_2_4;
- mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- *outKeys = tmpKeys;
- });
- return error;
-}
-
-Error Composer::getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
- mReader.takeClientTargetProperty(display, outClientTargetProperty);
- return Error::NONE;
-}
-
-CommandReader::~CommandReader()
-{
- resetData();
-}
-
-Error CommandReader::parse()
-{
- resetData();
-
- IComposerClient::Command command;
- uint16_t length = 0;
-
- while (!isEmpty()) {
- if (!beginCommand(&command, &length)) {
- break;
- }
-
- bool parsed = false;
- switch (command) {
- case IComposerClient::Command::SELECT_DISPLAY:
- parsed = parseSelectDisplay(length);
- break;
- case IComposerClient::Command::SET_ERROR:
- parsed = parseSetError(length);
- break;
- case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
- parsed = parseSetChangedCompositionTypes(length);
- break;
- case IComposerClient::Command::SET_DISPLAY_REQUESTS:
- parsed = parseSetDisplayRequests(length);
- break;
- case IComposerClient::Command::SET_PRESENT_FENCE:
- parsed = parseSetPresentFence(length);
- break;
- case IComposerClient::Command::SET_RELEASE_FENCES:
- parsed = parseSetReleaseFences(length);
- break;
- case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
- parsed = parseSetPresentOrValidateDisplayResult(length);
- break;
- case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
- parsed = parseSetClientTargetProperty(length);
- break;
- default:
- parsed = false;
- break;
- }
-
- endCommand();
-
- if (!parsed) {
- ALOGE("failed to parse command 0x%x length %" PRIu16,
- command, length);
- break;
- }
- }
-
- return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
-}
-
-bool CommandReader::parseSelectDisplay(uint16_t length)
-{
- if (length != CommandWriterBase::kSelectDisplayLength) {
- return false;
- }
-
- mCurrentReturnData = &mReturnData[read64()];
-
- return true;
-}
-
-bool CommandReader::parseSetError(uint16_t length)
-{
- if (length != CommandWriterBase::kSetErrorLength) {
- return false;
- }
-
- auto location = read();
- auto error = static_cast<Error>(readSigned());
-
- mErrors.emplace_back(CommandError{location, error});
-
- return true;
-}
-
-bool CommandReader::parseSetChangedCompositionTypes(uint16_t length)
-{
- // (layer id, composition type) pairs
- if (length % 3 != 0 || !mCurrentReturnData) {
- return false;
- }
-
- uint32_t count = length / 3;
- mCurrentReturnData->changedLayers.reserve(count);
- mCurrentReturnData->compositionTypes.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto type = static_cast<IComposerClient::Composition>(readSigned());
-
- mCurrentReturnData->changedLayers.push_back(layer);
- mCurrentReturnData->compositionTypes.push_back(type);
-
- count--;
- }
-
- return true;
-}
-
-bool CommandReader::parseSetDisplayRequests(uint16_t length)
-{
- // display requests followed by (layer id, layer requests) pairs
- if (length % 3 != 1 || !mCurrentReturnData) {
- return false;
- }
-
- mCurrentReturnData->displayRequests = read();
-
- uint32_t count = (length - 1) / 3;
- mCurrentReturnData->requestedLayers.reserve(count);
- mCurrentReturnData->requestMasks.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto layerRequestMask = read();
-
- mCurrentReturnData->requestedLayers.push_back(layer);
- mCurrentReturnData->requestMasks.push_back(layerRequestMask);
-
- count--;
- }
-
- return true;
-}
-
-bool CommandReader::parseSetPresentFence(uint16_t length)
-{
- if (length != CommandWriterBase::kSetPresentFenceLength ||
- !mCurrentReturnData) {
- return false;
- }
-
- if (mCurrentReturnData->presentFence >= 0) {
- close(mCurrentReturnData->presentFence);
- }
- mCurrentReturnData->presentFence = readFence();
-
- return true;
-}
-
-bool CommandReader::parseSetReleaseFences(uint16_t length)
-{
- // (layer id, release fence index) pairs
- if (length % 3 != 0 || !mCurrentReturnData) {
- return false;
- }
-
- uint32_t count = length / 3;
- mCurrentReturnData->releasedLayers.reserve(count);
- mCurrentReturnData->releaseFences.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto fence = readFence();
-
- mCurrentReturnData->releasedLayers.push_back(layer);
- mCurrentReturnData->releaseFences.push_back(fence);
-
- count--;
- }
-
- return true;
-}
-
-bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length)
-{
- if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
- return false;
- }
- mCurrentReturnData->presentOrValidateState = read();
- return true;
-}
-
-bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
- if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
- return false;
- }
- mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
- mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
- return true;
-}
-
-void CommandReader::resetData()
-{
- mErrors.clear();
-
- for (auto& data : mReturnData) {
- if (data.second.presentFence >= 0) {
- close(data.second.presentFence);
- }
- for (auto fence : data.second.releaseFences) {
- if (fence >= 0) {
- close(fence);
- }
- }
- }
-
- mReturnData.clear();
- mCurrentReturnData = nullptr;
-}
-
-std::vector<CommandReader::CommandError> CommandReader::takeErrors()
-{
- return std::move(mErrors);
-}
-
-bool CommandReader::hasChanges(Display display,
- uint32_t* outNumChangedCompositionTypes,
- uint32_t* outNumLayerRequestMasks) const
-{
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outNumChangedCompositionTypes = 0;
- *outNumLayerRequestMasks = 0;
- return false;
- }
-
- const ReturnData& data = found->second;
-
- *outNumChangedCompositionTypes = data.compositionTypes.size();
- *outNumLayerRequestMasks = data.requestMasks.size();
-
- return !(data.compositionTypes.empty() && data.requestMasks.empty());
-}
-
-void CommandReader::takeChangedCompositionTypes(Display display,
- std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes)
-{
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- outLayers->clear();
- outTypes->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outLayers = std::move(data.changedLayers);
- *outTypes = std::move(data.compositionTypes);
-}
-
-void CommandReader::takeDisplayRequests(Display display,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks)
-{
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outDisplayRequestMask = 0;
- outLayers->clear();
- outLayerRequestMasks->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outDisplayRequestMask = data.displayRequests;
- *outLayers = std::move(data.requestedLayers);
- *outLayerRequestMasks = std::move(data.requestMasks);
-}
-
-void CommandReader::takeReleaseFences(Display display,
- std::vector<Layer>* outLayers, std::vector<int>* outReleaseFences)
-{
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- outLayers->clear();
- outReleaseFences->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outLayers = std::move(data.releasedLayers);
- *outReleaseFences = std::move(data.releaseFences);
-}
-
-void CommandReader::takePresentFence(Display display, int* outPresentFence)
-{
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outPresentFence = -1;
- return;
- }
-
- ReturnData& data = found->second;
-
- *outPresentFence = data.presentFence;
- data.presentFence = -1;
-}
-
-void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *state= -1;
- return;
- }
- ReturnData& data = found->second;
- *state = data.presentOrValidateState;
-}
-
-void CommandReader::takeClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
- auto found = mReturnData.find(display);
-
- // If not found, return the default values.
- if (found == mReturnData.end()) {
- outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
- outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
- return;
- }
-
- ReturnData& data = found->second;
- *outClientTargetProperty = data.clientTargetProperty;
-}
-
-} // namespace impl
-} // namespace Hwc2
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index fe114b9..b4ba8ab 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -14,24 +14,15 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_COMPOSER_HAL_H
-#define ANDROID_SF_COMPOSER_HAL_H
+#pragma once
#include <memory>
-#include <optional>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
-#include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.4/IComposer.h>
-#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
#include <gui/BufferQueue.h>
#include <gui/HdrMetadata.h>
@@ -40,12 +31,12 @@
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
-namespace android {
-
-namespace Hwc2 {
+namespace android::Hwc2 {
namespace types = hardware::graphics::common;
@@ -80,6 +71,8 @@
class Composer {
public:
+ static std::unique_ptr<Composer> create(const std::string& serviceName);
+
virtual ~Composer() = 0;
virtual std::vector<IComposer::Capability> getCapabilities() = 0;
@@ -107,7 +100,7 @@
virtual Error getActiveConfig(Display display, Config* outConfig) = 0;
virtual Error getChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) = 0;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) = 0;
virtual Error getColorModes(Display display, std::vector<ColorMode>* outModes) = 0;
virtual Error getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) = 0;
@@ -164,8 +157,9 @@
IComposerClient::BlendMode mode) = 0;
virtual Error setLayerColor(Display display, Layer layer,
const IComposerClient::Color& color) = 0;
- virtual Error setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) = 0;
+ virtual Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) = 0;
virtual Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) = 0;
virtual Error setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) = 0;
@@ -230,282 +224,11 @@
virtual V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
virtual Error getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) = 0;
+
+ // AIDL Composer
+ virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
};
-namespace impl {
-
-class CommandReader : public CommandReaderBase {
-public:
- ~CommandReader();
-
- // Parse and execute commands from the command queue. The commands are
- // actually return values from the server and will be saved in ReturnData.
- Error parse();
-
- // Get and clear saved errors.
- struct CommandError {
- uint32_t location;
- Error error;
- };
- std::vector<CommandError> takeErrors();
-
- bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
- uint32_t* outNumLayerRequestMasks) const;
-
- // Get and clear saved changed composition types.
- void takeChangedCompositionTypes(Display display,
- std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes);
-
- // Get and clear saved display requests.
- void takeDisplayRequests(Display display,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks);
-
- // Get and clear saved release fences.
- void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
- std::vector<int>* outReleaseFences);
-
- // Get and clear saved present fence.
- void takePresentFence(Display display, int* outPresentFence);
-
- // Get what stage succeeded during PresentOrValidate: Present or Validate
- void takePresentOrValidateStage(Display display, uint32_t * state);
-
- // Get the client target properties requested by hardware composer.
- void takeClientTargetProperty(Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty);
-
-private:
- void resetData();
-
- bool parseSelectDisplay(uint16_t length);
- bool parseSetError(uint16_t length);
- bool parseSetChangedCompositionTypes(uint16_t length);
- bool parseSetDisplayRequests(uint16_t length);
- bool parseSetPresentFence(uint16_t length);
- bool parseSetReleaseFences(uint16_t length);
- bool parseSetPresentOrValidateDisplayResult(uint16_t length);
- bool parseSetClientTargetProperty(uint16_t length);
-
- struct ReturnData {
- uint32_t displayRequests = 0;
-
- std::vector<Layer> changedLayers;
- std::vector<IComposerClient::Composition> compositionTypes;
-
- std::vector<Layer> requestedLayers;
- std::vector<uint32_t> requestMasks;
-
- int presentFence = -1;
-
- std::vector<Layer> releasedLayers;
- std::vector<int> releaseFences;
-
- uint32_t presentOrValidateState;
-
- // Composer 2.4 implementation can return a client target property
- // structure to indicate the client target properties that hardware
- // composer requests. The composer client must change the client target
- // properties to match this request.
- IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
- Dataspace::UNKNOWN};
- };
-
- std::vector<CommandError> mErrors;
- std::unordered_map<Display, ReturnData> mReturnData;
-
- // When SELECT_DISPLAY is parsed, this is updated to point to the
- // display's return data in mReturnData. We use it to avoid repeated
- // map lookups.
- ReturnData* mCurrentReturnData;
-};
-
-// Composer is a wrapper to IComposer, a proxy to server-side composer.
-class Composer final : public Hwc2::Composer {
-public:
- explicit Composer(const std::string& serviceName);
- ~Composer() override;
-
- std::vector<IComposer::Capability> getCapabilities() override;
- std::string dumpDebugInfo() override;
-
- void registerCallback(const sp<IComposerCallback>& callback) override;
-
- // Reset all pending commands in the command buffer. Useful if you want to
- // skip a frame but have already queued some commands.
- void resetCommands() override;
-
- // Explicitly flush all pending commands in the command buffer.
- Error executeCommands() override;
-
- uint32_t getMaxVirtualDisplayCount() override;
- Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
- Display* outDisplay) override;
- Error destroyVirtualDisplay(Display display) override;
-
- Error acceptDisplayChanges(Display display) override;
-
- Error createLayer(Display display, Layer* outLayer) override;
- Error destroyLayer(Display display, Layer layer) override;
-
- Error getActiveConfig(Display display, Config* outConfig) override;
- Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) override;
- Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
- Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
- Error getDisplayName(Display display, std::string* outName) override;
-
- Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
- std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks) override;
-
- Error getDozeSupport(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
- float* outMaxAverageLuminance, float* outMinLuminance) override;
-
- Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
- std::vector<int>* outReleaseFences) override;
-
- Error presentDisplay(Display display, int* outPresentFence) override;
-
- Error setActiveConfig(Display display, Config config) override;
-
- /*
- * The composer caches client targets internally. When target is nullptr,
- * the composer uses slot to look up the client target from its cache.
- * When target is not nullptr, the cache is updated with the new target.
- */
- Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
- int acquireFence, Dataspace dataspace,
- const std::vector<IComposerClient::Rect>& damage) override;
- Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
- Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
- Error setOutputBuffer(Display display, const native_handle_t* buffer,
- int releaseFence) override;
- Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
- Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
-
- Error setClientTargetSlotCount(Display display) override;
-
- Error validateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests) override;
-
- Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
- int* outPresentFence, uint32_t* state) override;
-
- Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
- /* see setClientTarget for the purpose of slot */
- Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
- const sp<GraphicBuffer>& buffer, int acquireFence) override;
- Error setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<IComposerClient::Rect>& damage) override;
- Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
- Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
- Error setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) override;
- Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
- Error setLayerDisplayFrame(Display display, Layer layer,
- const IComposerClient::Rect& frame) override;
- Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
- Error setLayerSidebandStream(Display display, Layer layer,
- const native_handle_t* stream) override;
- Error setLayerSourceCrop(Display display, Layer layer,
- const IComposerClient::FRect& crop) override;
- Error setLayerTransform(Display display, Layer layer, Transform transform) override;
- Error setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<IComposerClient::Rect>& visible) override;
- Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
-
- // Composer HAL 2.2
- Error setLayerPerFrameMetadata(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
- std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
- Display display) override;
- Error getRenderIntents(Display display, ColorMode colorMode,
- std::vector<RenderIntent>* outRenderIntents) override;
- Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
-
- // Composer HAL 2.3
- Error getDisplayIdentificationData(Display display, uint8_t* outPort,
- std::vector<uint8_t>* outData) override;
- Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
- Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
- Dataspace* outDataspace,
- uint8_t* outComponentMask) override;
- Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
- uint64_t maxFrames) override;
- Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
- DisplayedFrameStats* outStats) override;
- Error setLayerPerFrameMetadataBlobs(
- Display display, Layer layer,
- const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
- Error setDisplayBrightness(Display display, float brightness) override;
-
- // Composer HAL 2.4
- bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
- Error getDisplayCapabilities(Display display,
- std::vector<DisplayCapability>* outCapabilities) override;
- V2_4::Error getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) override;
- V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
- V2_4::Error setActiveConfigWithConstraints(
- Display display, Config config,
- const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
- VsyncPeriodChangeTimeline* outTimeline) override;
- V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
- V2_4::Error getSupportedContentTypes(
- Display displayId,
- std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
- V2_4::Error setContentType(Display displayId,
- IComposerClient::ContentType contentType) override;
- V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
- bool mandatory, const std::vector<uint8_t>& value) override;
- V2_4::Error getLayerGenericMetadataKeys(
- std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
- Error getClientTargetProperty(
- Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
-
-private:
- class CommandWriter : public CommandWriterBase {
- public:
- explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
- ~CommandWriter() override {}
- };
-
- // Many public functions above simply write a command into the command
- // queue to batch the calls. validateDisplay and presentDisplay will call
- // this function to execute the command queue.
- Error execute();
-
- sp<V2_1::IComposer> mComposer;
-
- sp<V2_1::IComposerClient> mClient;
- sp<V2_2::IComposerClient> mClient_2_2;
- sp<V2_3::IComposerClient> mClient_2_3;
- sp<IComposerClient> mClient_2_4;
-
- // 64KiB minus a small space for metadata such as read/write pointers
- static constexpr size_t kWriterInitialSize =
- 64 * 1024 / sizeof(uint32_t) - 16;
- // Max number of buffers that may be cached for a given layer
- // We obtain this number by:
- // 1. Tightly coupling this cache to the max size of BufferQueue
- // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
- static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- CommandWriter mWriter;
- CommandReader mReader;
-};
-
-} // namespace impl
-
-} // namespace Hwc2
-
-} // namespace android
-
-#endif // ANDROID_SF_COMPOSER_HAL_H
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 5de622b..0ab9605 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -16,9 +16,9 @@
#pragma once
-#include "DisplayHardware/Hal.h"
-#include "Fps.h"
-#include "Scheduler/StrongTyping.h"
+#include <cstddef>
+#include <memory>
+#include <vector>
#include <android-base/stringprintf.h>
#include <android/configuration.h>
@@ -27,9 +27,10 @@
#include <ui/Size.h>
#include <utils/Timers.h>
-#include <cstddef>
-#include <memory>
-#include <vector>
+#include <scheduler/Fps.h>
+
+#include "DisplayHardware/Hal.h"
+#include "Scheduler/StrongTyping.h"
namespace android {
@@ -161,4 +162,4 @@
mode.getDpiY(), mode.getGroup());
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 27146ab..82f463e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -39,6 +39,8 @@
#include "ComposerHal.h"
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android {
using android::Fence;
@@ -147,7 +149,7 @@
Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
std::vector<Hwc2::Layer> layerIds;
- std::vector<Hwc2::IComposerClient::Composition> types;
+ std::vector<Composition> types;
auto intError = mComposer.getChangedCompositionTypes(
mId, &layerIds, &types);
uint32_t numElements = layerIds.size();
@@ -283,8 +285,21 @@
return Error::NONE;
}
+bool Display::hasCapability(hal::DisplayCapability capability) const {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ if (mDisplayCapabilities) {
+ return mDisplayCapabilities->count(capability) > 0;
+ }
+
+ ALOGW("Can't query capability %d."
+ " Display Capabilities were not queried from HWC yet",
+ static_cast<int>(capability));
+
+ return false;
+}
+
Error Display::supportsDoze(bool* outSupport) const {
- *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0;
+ *outSupport = hasCapability(DisplayCapability::DOZE);
return Error::NONE;
}
@@ -447,17 +462,21 @@
auto error =
static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
if (error == Error::NONE) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
for (auto capability : tmpCapabilities) {
- mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+ mDisplayCapabilities->emplace(static_cast<DisplayCapability>(capability));
}
} else if (error == Error::UNSUPPORTED) {
+ std::scoped_lock lock(mDisplayCapabilitiesMutex);
+ mDisplayCapabilities.emplace();
if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) {
- mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
+ mDisplayCapabilities->emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
}
bool dozeSupport = false;
error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
if (error == Error::NONE && dozeSupport) {
- mDisplayCapabilities.emplace(DisplayCapability::DOZE);
+ mDisplayCapabilities->emplace(DisplayCapability::DOZE);
}
}
});
@@ -538,8 +557,10 @@
return static_cast<Error>(intError);
}
-Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
- const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
+ const auto error =
+ mComposer.getClientTargetProperty(mId, outClientTargetProperty, outWhitePointNits);
return static_cast<Error>(error);
}
@@ -902,6 +923,16 @@
return static_cast<Error>(intError);
}
+// AIDL HAL
+Error Layer::setWhitePointNits(float whitePointNits) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerWhitePointNits(mDisplay->getId(), mId, whitePointNits);
+ return static_cast<Error>(intError);
+}
+
} // namespace impl
} // namespace HWC2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 871465d..1425c61 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/expected.h>
+#include <android-base/thread_annotations.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -35,6 +36,8 @@
#include "Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android {
class Fence;
@@ -79,7 +82,7 @@
virtual hal::HWDisplayId getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
- virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
+ virtual bool hasCapability(hal::DisplayCapability) const = 0;
virtual bool isVsyncPeriodSwitchSupported() const = 0;
virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
@@ -87,7 +90,8 @@
[[clang::warn_unused_result]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
createLayer() = 0;
[[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
- std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
+ std::unordered_map<Layer*, aidl::android::hardware::graphics::composer3::Composition>*
+ outTypes) = 0;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
std::vector<hal::ColorMode>* outModes) const = 0;
// Returns a bitmask which contains HdrMetadata::Type::*.
@@ -145,7 +149,7 @@
std::vector<hal::ContentType>*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
[[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
- hal::ClientTargetProperty* outClientTargetProperty) = 0;
+ hal::ClientTargetProperty* outClientTargetProperty, float* outWhitePointNits) = 0;
};
namespace impl {
@@ -162,7 +166,9 @@
hal::Error acceptChanges() override;
base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override;
hal::Error getChangedCompositionTypes(
- std::unordered_map<HWC2::Layer*, hal::Composition>* outTypes) override;
+ std::unordered_map<HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
// Returns a bitmask which contains HdrMetadata::Type::*.
int32_t getSupportedPerFrameMetadata() const override;
@@ -175,7 +181,7 @@
hal::DisplayRequest* outDisplayRequests,
std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override;
hal::Error getConnectionType(ui::DisplayConnectionType*) const override;
- hal::Error supportsDoze(bool* outSupport) const override;
+ hal::Error supportsDoze(bool* outSupport) const override EXCLUDES(mDisplayCapabilitiesMutex);
hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
hal::Dataspace* outDataspace,
@@ -208,15 +214,14 @@
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
hal::Error setContentType(hal::ContentType) override;
- hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+ hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
void setConnected(bool connected) override; // For use by Device only
- const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
- return mDisplayCapabilities;
- };
+ bool hasCapability(hal::DisplayCapability) const override EXCLUDES(mDisplayCapabilitiesMutex);
bool isVsyncPeriodSwitchSupported() const override;
void onLayerDestroyed(hal::HWLayerId layerId) override;
@@ -243,8 +248,10 @@
using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>;
Layers mLayers;
+ mutable std::mutex mDisplayCapabilitiesMutex;
std::once_flag mDisplayCapabilityQueryFlag;
- std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
+ std::optional<std::unordered_set<hal::DisplayCapability>> mDisplayCapabilities
+ GUARDED_BY(mDisplayCapabilitiesMutex);
};
} // namespace impl
@@ -264,7 +271,8 @@
[[clang::warn_unused_result]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
[[clang::warn_unused_result]] virtual hal::Error setColor(hal::Color color) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setCompositionType(hal::Composition type) = 0;
+ [[clang::warn_unused_result]] virtual hal::Error setCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition type) = 0;
[[clang::warn_unused_result]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
[[clang::warn_unused_result]] virtual hal::Error setPerFrameMetadata(
const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0;
@@ -287,6 +295,9 @@
// Composer HAL 2.4
[[clang::warn_unused_result]] virtual hal::Error setLayerGenericMetadata(
const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
+
+ // AIDL HAL
+ [[clang::warn_unused_result]] virtual hal::Error setWhitePointNits(float whitePointNits) = 0;
};
namespace impl {
@@ -311,7 +322,8 @@
hal::Error setBlendMode(hal::BlendMode mode) override;
hal::Error setColor(hal::Color color) override;
- hal::Error setCompositionType(hal::Composition type) override;
+ hal::Error setCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition type) override;
hal::Error setDataspace(hal::Dataspace dataspace) override;
hal::Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
const android::HdrMetadata& metadata) override;
@@ -330,6 +342,9 @@
hal::Error setLayerGenericMetadata(const std::string& name, bool mandatory,
const std::vector<uint8_t>& value) override;
+ // AIDL HAL
+ hal::Error setWhitePointNits(float whitePointNits) override;
+
private:
// These are references to data owned by HWC2::Device, which will outlive
// this HWC2::Layer, so these references are guaranteed to be valid for
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index a790b4c..d851e22 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -143,7 +143,7 @@
sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
- : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
+ : HWComposer(Hwc2::Composer::create(composerServiceName)) {}
HWComposer::~HWComposer() {
mDisplayData.clear();
@@ -183,7 +183,7 @@
bool HWComposer::hasDisplayCapability(HalDisplayId displayId,
hal::DisplayCapability capability) const {
RETURN_IF_INVALID_DISPLAY(displayId, false);
- return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+ return mDisplayData.at(displayId).hwcDisplay->hasCapability(capability);
}
std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
@@ -212,8 +212,6 @@
RETURN_IF_INVALID_DISPLAY(*displayId, false);
auto& displayData = mDisplayData[*displayId];
- LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
- __FUNCTION__, to_string(*displayId).c_str());
{
// There have been reports of HWCs that signal several vsync events
@@ -271,7 +269,6 @@
display->setConnected(true);
auto& displayData = mDisplayData[displayId];
displayData.hwcDisplay = std::move(display);
- displayData.isVirtual = true;
return true;
}
@@ -279,10 +276,8 @@
PhysicalDisplayId displayId) {
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
- if (!mInternalHwcDisplayId) {
- mInternalHwcDisplayId = hwcDisplayId;
- } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
- mExternalHwcDisplayId = hwcDisplayId;
+ if (!mPrimaryHwcDisplayId) {
+ mPrimaryHwcDisplayId = hwcDisplayId;
}
auto& displayData = mDisplayData[displayId];
@@ -372,7 +367,7 @@
ui::DisplayConnectionType type;
const auto error = hwcDisplay->getConnectionType(&type);
- const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+ const auto FALLBACK_TYPE = hwcDisplay->getId() == mPrimaryHwcDisplayId
? ui::DisplayConnectionType::Internal
: ui::DisplayConnectionType::External;
@@ -428,9 +423,6 @@
RETURN_IF_INVALID_DISPLAY(displayId);
auto& displayData = mDisplayData[displayId];
- LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
- __FUNCTION__, to_string(displayId).c_str());
-
// NOTE: we use our own internal lock here because we have to call
// into the HWC with the lock held, and we want to make sure
// that even if HWC blocks (which it shouldn't), it won't
@@ -530,11 +522,13 @@
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
- error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ float clientTargetWhitePointNits;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &clientTargetWhitePointNits);
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty)});
+ std::move(clientTargetProperty),
+ clientTargetWhitePointNits});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
@@ -597,14 +591,11 @@
status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- const auto& displayData = mDisplayData[displayId];
- LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s",
- __FUNCTION__, to_string(displayId).c_str());
-
if (mode == hal::PowerMode::OFF) {
setVsyncEnabled(displayId, hal::Vsync::DISABLE);
}
+ const auto& displayData = mDisplayData[displayId];
auto& hwcDisplay = displayData.hwcDisplay;
switch (mode) {
case hal::PowerMode::OFF:
@@ -678,12 +669,6 @@
auto& displayData = mDisplayData[displayId];
const auto hwcDisplayId = displayData.hwcDisplay->getId();
- // TODO(b/74619554): Select internal/external display from remaining displays.
- if (hwcDisplayId == mInternalHwcDisplayId) {
- mInternalHwcDisplayId.reset();
- } else if (hwcDisplayId == mExternalHwcDisplayId) {
- mExternalHwcDisplayId.reset();
- }
mPhysicalDisplayIdMap.erase(hwcDisplayId);
mDisplayData.erase(displayId);
}
@@ -693,9 +678,6 @@
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto& displayData = mDisplayData[displayId];
- LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s",
- __FUNCTION__, to_string(displayId).c_str());
-
auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
@@ -853,8 +835,7 @@
std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(
PhysicalDisplayId displayId) const {
- if (const auto it = mDisplayData.find(displayId);
- it != mDisplayData.end() && !it->second.isVirtual) {
+ if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end()) {
return it->second.hwcDisplay->getId();
}
return {};
@@ -868,7 +849,8 @@
return true;
}
- if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
+ // Legacy mode only supports IDs LEGACY_DISPLAY_TYPE_PRIMARY and LEGACY_DISPLAY_TYPE_EXTERNAL.
+ if (!mHasMultiDisplaySupport && mPhysicalDisplayIdMap.size() == 2) {
ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
return true;
}
@@ -909,7 +891,7 @@
}
info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
- const bool isPrimary = !mInternalHwcDisplayId;
+ const bool isPrimary = !mPrimaryHwcDisplayId;
if (mHasMultiDisplaySupport) {
if (const auto info = parseDisplayIdentificationData(port, data)) {
return *info;
@@ -922,8 +904,8 @@
}
return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port),
- .name = isPrimary ? "Internal display"
- : "External display",
+ .name = isPrimary ? "Primary display"
+ : "Secondary display",
.deviceProductInfo = std::nullopt};
}();
}
@@ -936,9 +918,12 @@
std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
hal::HWDisplayId hwcDisplayId) {
+ LOG_ALWAYS_FATAL_IF(hwcDisplayId == mPrimaryHwcDisplayId,
+ "Primary display cannot be disconnected.");
+
const auto displayId = toPhysicalDisplayId(hwcDisplayId);
if (!displayId) {
- ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
return {};
}
@@ -947,7 +932,7 @@
if (isConnected(*displayId)) {
mDisplayData[*displayId].hwcDisplay->setConnected(false);
} else {
- ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+ LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Already disconnected");
}
// The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
// via SurfaceFlinger's onHotplugReceived callback handling
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 49f96d9..aa4abdf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
+#pragma once
#include <cstdint>
#include <future>
@@ -44,6 +43,8 @@
#include "HWC2.h"
#include "Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android {
namespace hal = hardware::graphics::composer::hal;
@@ -71,7 +72,9 @@
class HWComposer {
public:
struct DeviceRequestedChanges {
- using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+ using ChangedTypes =
+ std::unordered_map<HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition>;
using ClientTargetProperty = hal::ClientTargetProperty;
using DisplayRequests = hal::DisplayRequest;
using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
@@ -80,6 +83,7 @@
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
+ float clientTargetWhitePointNits;
};
struct HWCDisplayMode {
@@ -228,14 +232,18 @@
virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
const = 0;
- // for debugging ----------------------------------------------------------
virtual void dump(std::string& out) const = 0;
virtual Hwc2::Composer* getComposer() const = 0;
- // TODO(b/74619554): Remove special cases for internal/external display.
- virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
- virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
+ // Returns the first display connected at boot. It cannot be disconnected, which implies an
+ // internal connection type. Its connection via HWComposer::onHotplug, which in practice is
+ // immediately after HWComposer construction, must occur before any call to this function.
+ //
+ // TODO(b/182939859): Remove special cases for primary display.
+ virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0;
+ virtual PhysicalDisplayId getPrimaryDisplayId() const = 0;
+ virtual bool isHeadless() const = 0;
virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0;
virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0;
@@ -366,14 +374,19 @@
Hwc2::Composer* getComposer() const override { return mComposer.get(); }
- // TODO(b/74619554): Remove special cases for internal/external display.
- std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override {
- return mInternalHwcDisplayId;
+ hal::HWDisplayId getPrimaryHwcDisplayId() const override {
+ LOG_ALWAYS_FATAL_IF(!mPrimaryHwcDisplayId, "Missing HWC primary display");
+ return *mPrimaryHwcDisplayId;
}
- std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override {
- return mExternalHwcDisplayId;
+
+ PhysicalDisplayId getPrimaryDisplayId() const override {
+ const auto id = toPhysicalDisplayId(getPrimaryHwcDisplayId());
+ LOG_ALWAYS_FATAL_IF(!id, "Missing primary display");
+ return *id;
}
+ virtual bool isHeadless() const override { return !mPrimaryHwcDisplayId; }
+
std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override;
std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override;
@@ -382,12 +395,9 @@
friend TestableSurfaceFlinger;
struct DisplayData {
- bool isVirtual = false;
std::unique_ptr<HWC2::Display> hwcDisplay;
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
- buffer_handle_t outbufHandle = nullptr;
- sp<Fence> outbufAcquireFence = Fence::NO_FENCE;
bool validateWasSkipped;
hal::Error presentError;
@@ -418,8 +428,7 @@
bool mRegisteredCallback = false;
std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap;
- std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
- std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
+ std::optional<hal::HWDisplayId> mPrimaryHwcDisplayId;
bool mHasMultiDisplaySupport = false;
const size_t mMaxVirtualDisplayDimension;
@@ -428,5 +437,3 @@
} // namespace impl
} // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 02d0658..77e704f 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,8 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
#define ERROR_HAS_CHANGES 5
namespace android {
@@ -49,7 +51,6 @@
using Attribute = IComposerClient::Attribute;
using BlendMode = IComposerClient::BlendMode;
using Color = IComposerClient::Color;
-using Composition = IComposerClient::Composition;
using Connection = IComposerCallback::Connection;
using ContentType = IComposerClient::ContentType;
using Capability = IComposer::Capability;
@@ -95,19 +96,20 @@
}
}
-inline std::string to_string(hardware::graphics::composer::hal::Composition composition) {
+inline std::string to_string(
+ aidl::android::hardware::graphics::composer3::Composition composition) {
switch (composition) {
- case hardware::graphics::composer::hal::Composition::INVALID:
+ case aidl::android::hardware::graphics::composer3::Composition::INVALID:
return "Invalid";
- case hardware::graphics::composer::hal::Composition::CLIENT:
+ case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
return "Client";
- case hardware::graphics::composer::hal::Composition::DEVICE:
+ case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
return "Device";
- case hardware::graphics::composer::hal::Composition::SOLID_COLOR:
+ case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
return "SolidColor";
- case hardware::graphics::composer::hal::Composition::CURSOR:
+ case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
return "Cursor";
- case hardware::graphics::composer::hal::Composition::SIDEBAND:
+ case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
return "Sideband";
default:
return "Unknown";
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
new file mode 100644
index 0000000..18f24c1
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -0,0 +1,1493 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "HwcComposer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "HidlComposerHal.h"
+
+#include <android/binder_manager.h>
+#include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/HidlTransportUtils.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cinttypes>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_vec;
+using hardware::Return;
+
+namespace Hwc2 {
+
+HidlComposer::~HidlComposer() = default;
+
+namespace {
+
+class BufferHandle {
+public:
+ explicit BufferHandle(const native_handle_t* buffer) {
+ // nullptr is not a valid handle to HIDL
+ mHandle = (buffer) ? buffer : native_handle_init(mStorage, 0, 0);
+ }
+
+ operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
+ {
+ return mHandle;
+ }
+
+private:
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 0, 0);
+ hidl_handle mHandle;
+};
+
+class FenceHandle {
+public:
+ FenceHandle(int fd, bool owned) : mOwned(owned) {
+ native_handle_t* handle;
+ if (fd >= 0) {
+ handle = native_handle_init(mStorage, 1, 0);
+ handle->data[0] = fd;
+ } else {
+ // nullptr is not a valid handle to HIDL
+ handle = native_handle_init(mStorage, 0, 0);
+ }
+ mHandle = handle;
+ }
+
+ ~FenceHandle() {
+ if (mOwned) {
+ native_handle_close(mHandle);
+ }
+ }
+
+ operator const hidl_handle&() const // NOLINT(google-explicit-constructor)
+ {
+ return mHandle;
+ }
+
+private:
+ bool mOwned;
+ NATIVE_HANDLE_DECLARE_STORAGE(mStorage, 1, 0);
+ hidl_handle mHandle;
+};
+
+// assume NO_RESOURCES when Status::isOk returns false
+constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
+
+template <typename T, typename U>
+T unwrapRet(Return<T>& ret, const U& default_val) {
+ return (ret.isOk()) ? static_cast<T>(ret) : static_cast<T>(default_val);
+}
+
+Error unwrapRet(Return<Error>& ret) {
+ return unwrapRet(ret, kDefaultError);
+}
+
+} // anonymous namespace
+
+HidlComposer::HidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
+ mComposer = V2_1::IComposer::getService(serviceName);
+
+ if (mComposer == nullptr) {
+ LOG_ALWAYS_FATAL("failed to get hwcomposer service");
+ }
+
+ if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
+ composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError == V2_4::Error::NONE) {
+ mClient = tmpClient;
+ mClient_2_2 = tmpClient;
+ mClient_2_3 = tmpClient;
+ mClient_2_4 = tmpClient;
+ }
+ });
+ } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
+ composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError == Error::NONE) {
+ mClient = tmpClient;
+ mClient_2_2 = tmpClient;
+ mClient_2_3 = tmpClient;
+ }
+ });
+ } else {
+ mComposer->createClient([&](const auto& tmpError, const auto& tmpClient) {
+ if (tmpError != Error::NONE) {
+ return;
+ }
+
+ mClient = tmpClient;
+ if (sp<V2_2::IComposer> composer_2_2 = V2_2::IComposer::castFrom(mComposer)) {
+ mClient_2_2 = V2_2::IComposerClient::castFrom(mClient);
+ LOG_ALWAYS_FATAL_IF(mClient_2_2 == nullptr,
+ "IComposer 2.2 did not return IComposerClient 2.2");
+ }
+ });
+ }
+
+ if (mClient == nullptr) {
+ LOG_ALWAYS_FATAL("failed to create composer client");
+ }
+}
+
+std::vector<IComposer::Capability> HidlComposer::getCapabilities() {
+ std::vector<IComposer::Capability> capabilities;
+ mComposer->getCapabilities(
+ [&](const auto& tmpCapabilities) { capabilities = tmpCapabilities; });
+ return capabilities;
+}
+
+std::string HidlComposer::dumpDebugInfo() {
+ std::string info;
+ mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); });
+
+ return info;
+}
+
+void HidlComposer::registerCallback(const sp<IComposerCallback>& callback) {
+ android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
+
+ auto ret = [&]() {
+ if (mClient_2_4) {
+ return mClient_2_4->registerCallback_2_4(callback);
+ }
+ return mClient->registerCallback(callback);
+ }();
+ if (!ret.isOk()) {
+ ALOGE("failed to register IComposerCallback");
+ }
+}
+
+void HidlComposer::resetCommands() {
+ mWriter.reset();
+}
+
+Error HidlComposer::executeCommands() {
+ return execute();
+}
+
+uint32_t HidlComposer::getMaxVirtualDisplayCount() {
+ auto ret = mClient->getMaxVirtualDisplayCount();
+ return unwrapRet(ret, 0);
+}
+
+Error HidlComposer::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) {
+ const uint32_t bufferSlotCount = 1;
+ Error error = kDefaultError;
+ if (mClient_2_2) {
+ mClient_2_2->createVirtualDisplay_2_2(width, height,
+ static_cast<types::V1_1::PixelFormat>(*format),
+ bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outDisplay = tmpDisplay;
+ *format = static_cast<types::V1_2::PixelFormat>(
+ tmpFormat);
+ });
+ } else {
+ mClient->createVirtualDisplay(width, height, static_cast<types::V1_0::PixelFormat>(*format),
+ bufferSlotCount,
+ [&](const auto& tmpError, const auto& tmpDisplay,
+ const auto& tmpFormat) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outDisplay = tmpDisplay;
+ *format = static_cast<PixelFormat>(tmpFormat);
+ });
+ }
+
+ return error;
+}
+
+Error HidlComposer::destroyVirtualDisplay(Display display) {
+ auto ret = mClient->destroyVirtualDisplay(display);
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::acceptDisplayChanges(Display display) {
+ mWriter.selectDisplay(display);
+ mWriter.acceptDisplayChanges();
+ return Error::NONE;
+}
+
+Error HidlComposer::createLayer(Display display, Layer* outLayer) {
+ Error error = kDefaultError;
+ mClient->createLayer(display, kMaxLayerBufferCount,
+ [&](const auto& tmpError, const auto& tmpLayer) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outLayer = tmpLayer;
+ });
+
+ return error;
+}
+
+Error HidlComposer::destroyLayer(Display display, Layer layer) {
+ auto ret = mClient->destroyLayer(display, layer);
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::getActiveConfig(Display display, Config* outConfig) {
+ Error error = kDefaultError;
+ mClient->getActiveConfig(display, [&](const auto& tmpError, const auto& tmpConfig) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outConfig = tmpConfig;
+ });
+
+ return error;
+}
+
+Error HidlComposer::getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+ mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+ return Error::NONE;
+}
+
+Error HidlComposer::getColorModes(Display display, std::vector<ColorMode>* outModes) {
+ Error error = kDefaultError;
+
+ if (mClient_2_3) {
+ mClient_2_3->getColorModes_2_3(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outModes = tmpModes;
+ });
+ } else if (mClient_2_2) {
+ mClient_2_2->getColorModes_2_2(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ for (types::V1_1::ColorMode colorMode : tmpModes) {
+ outModes->push_back(static_cast<ColorMode>(colorMode));
+ }
+ });
+ } else {
+ mClient->getColorModes(display, [&](const auto& tmpError, const auto& tmpModes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ for (types::V1_0::ColorMode colorMode : tmpModes) {
+ outModes->push_back(static_cast<ColorMode>(colorMode));
+ }
+ });
+ }
+
+ return error;
+}
+
+Error HidlComposer::getDisplayAttribute(Display display, Config config,
+ IComposerClient::Attribute attribute, int32_t* outValue) {
+ Error error = kDefaultError;
+ if (mClient_2_4) {
+ mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = static_cast<Error>(tmpError);
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outValue = tmpValue;
+ });
+ } else {
+ mClient->getDisplayAttribute(display, config,
+ static_cast<V2_1::IComposerClient::Attribute>(attribute),
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outValue = tmpValue;
+ });
+ }
+
+ return error;
+}
+
+Error HidlComposer::getDisplayConfigs(Display display, std::vector<Config>* outConfigs) {
+ Error error = kDefaultError;
+ mClient->getDisplayConfigs(display, [&](const auto& tmpError, const auto& tmpConfigs) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outConfigs = tmpConfigs;
+ });
+
+ return error;
+}
+
+Error HidlComposer::getDisplayName(Display display, std::string* outName) {
+ Error error = kDefaultError;
+ mClient->getDisplayName(display, [&](const auto& tmpError, const auto& tmpName) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outName = tmpName.c_str();
+ });
+
+ return error;
+}
+
+Error HidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks) {
+ mReader.takeDisplayRequests(display, outDisplayRequestMask, outLayers, outLayerRequestMasks);
+ return Error::NONE;
+}
+
+Error HidlComposer::getDozeSupport(Display display, bool* outSupport) {
+ Error error = kDefaultError;
+ mClient->getDozeSupport(display, [&](const auto& tmpError, const auto& tmpSupport) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupport = tmpSupport;
+ });
+
+ return error;
+}
+
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ float* outMaxLuminance, float* outMaxAverageLuminance,
+ float* outMinLuminance) {
+ Error error = kDefaultError;
+ if (mClient_2_3) {
+ mClient_2_3->getHdrCapabilities_2_3(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outTypes = tmpTypes;
+ *outMaxLuminance = tmpMaxLuminance;
+ *outMaxAverageLuminance = tmpMaxAverageLuminance;
+ *outMinLuminance = tmpMinLuminance;
+ });
+ } else {
+ mClient->getHdrCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpTypes,
+ const auto& tmpMaxLuminance,
+ const auto& tmpMaxAverageLuminance,
+ const auto& tmpMinLuminance) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ outTypes->clear();
+ for (auto type : tmpTypes) {
+ outTypes->push_back(static_cast<Hdr>(type));
+ }
+
+ *outMaxLuminance = tmpMaxLuminance;
+ *outMaxAverageLuminance = tmpMaxAverageLuminance;
+ *outMinLuminance = tmpMinLuminance;
+ });
+ }
+
+ return error;
+}
+
+Error HidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences) {
+ mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+ return Error::NONE;
+}
+
+Error HidlComposer::presentDisplay(Display display, int* outPresentFence) {
+ ATRACE_NAME("HwcPresentDisplay");
+ mWriter.selectDisplay(display);
+ mWriter.presentDisplay();
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.takePresentFence(display, outPresentFence);
+
+ return Error::NONE;
+}
+
+Error HidlComposer::setActiveConfig(Display display, Config config) {
+ auto ret = mClient->setActiveConfig(display, config);
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage) {
+ mWriter.selectDisplay(display);
+
+ const native_handle_t* handle = nullptr;
+ if (target.get()) {
+ handle = target->getNativeBuffer()->handle;
+ }
+
+ mWriter.setClientTarget(slot, handle, acquireFence, dataspace, damage);
+ return Error::NONE;
+}
+
+Error HidlComposer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) {
+ hardware::Return<Error> ret(kDefaultError);
+ if (mClient_2_3) {
+ ret = mClient_2_3->setColorMode_2_3(display, mode, renderIntent);
+ } else if (mClient_2_2) {
+ ret = mClient_2_2->setColorMode_2_2(display, static_cast<types::V1_1::ColorMode>(mode),
+ renderIntent);
+ } else {
+ ret = mClient->setColorMode(display, static_cast<types::V1_0::ColorMode>(mode));
+ }
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::setColorTransform(Display display, const float* matrix, ColorTransform hint) {
+ mWriter.selectDisplay(display);
+ mWriter.setColorTransform(matrix, hint);
+ return Error::NONE;
+}
+
+Error HidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) {
+ mWriter.selectDisplay(display);
+ mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
+ return Error::NONE;
+}
+
+Error HidlComposer::setPowerMode(Display display, IComposerClient::PowerMode mode) {
+ Return<Error> ret(Error::UNSUPPORTED);
+ if (mClient_2_2) {
+ ret = mClient_2_2->setPowerMode_2_2(display, mode);
+ } else if (mode != IComposerClient::PowerMode::ON_SUSPEND) {
+ ret = mClient->setPowerMode(display, static_cast<V2_1::IComposerClient::PowerMode>(mode));
+ }
+
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
+ auto ret = mClient->setVsyncEnabled(display, enabled);
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::setClientTargetSlotCount(Display display) {
+ const uint32_t bufferSlotCount = BufferQueue::NUM_BUFFER_SLOTS;
+ auto ret = mClient->setClientTargetSlotCount(display, bufferSlotCount);
+ return unwrapRet(ret);
+}
+
+Error HidlComposer::validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
+ ATRACE_NAME("HwcValidateDisplay");
+ mWriter.selectDisplay(display);
+ mWriter.validateDisplay();
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.hasChanges(display, outNumTypes, outNumRequests);
+
+ return Error::NONE;
+}
+
+Error HidlComposer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, int* outPresentFence,
+ uint32_t* state) {
+ ATRACE_NAME("HwcPresentOrValidateDisplay");
+ mWriter.selectDisplay(display);
+ mWriter.presentOrvalidateDisplay();
+
+ Error error = execute();
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ mReader.takePresentOrValidateStage(display, state);
+
+ if (*state == 1) { // Present succeeded
+ mReader.takePresentFence(display, outPresentFence);
+ }
+
+ if (*state == 0) { // Validate succeeded.
+ mReader.hasChanges(display, outNumTypes, outNumRequests);
+ }
+
+ return Error::NONE;
+}
+
+Error HidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerCursorPosition(x, y);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
+ const sp<GraphicBuffer>& buffer, int acquireFence) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+
+ const native_handle_t* handle = nullptr;
+ if (buffer.get()) {
+ handle = buffer->getNativeBuffer()->handle;
+ }
+
+ mWriter.setLayerBuffer(slot, handle, acquireFence);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSurfaceDamage(damage);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerBlendMode(Display display, Layer layer,
+ IComposerClient::BlendMode mode) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerBlendMode(mode);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerColor(Display display, Layer layer,
+ const IComposerClient::Color& color) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerColor(color);
+ return Error::NONE;
+}
+
+static IComposerClient::Composition to_hidl_type(
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ return static_cast<IComposerClient::Composition>(type);
+}
+
+Error HidlComposer::setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerCompositionType(to_hidl_type(type));
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerDataspace(dataspace);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerDisplayFrame(frame);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerPlaneAlpha(alpha);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSidebandStream(stream);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerSourceCrop(crop);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerTransform(transform);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerVisibleRegion(visible);
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerZOrder(z);
+ return Error::NONE;
+}
+
+Error HidlComposer::execute() {
+ // prepare input command queue
+ bool queueChanged = false;
+ uint32_t commandLength = 0;
+ hidl_vec<hidl_handle> commandHandles;
+ if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
+ mWriter.reset();
+ return Error::NO_RESOURCES;
+ }
+
+ // set up new input command queue if necessary
+ if (queueChanged) {
+ auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
+ auto error = unwrapRet(ret);
+ if (error != Error::NONE) {
+ mWriter.reset();
+ return error;
+ }
+ }
+
+ if (commandLength == 0) {
+ mWriter.reset();
+ return Error::NONE;
+ }
+
+ Error error = kDefaultError;
+ hardware::Return<void> ret;
+ auto hidl_callback = [&](const auto& tmpError, const auto& tmpOutChanged,
+ const auto& tmpOutLength, const auto& tmpOutHandles) {
+ error = tmpError;
+
+ // set up new output command queue if necessary
+ if (error == Error::NONE && tmpOutChanged) {
+ error = kDefaultError;
+ mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ mReader.setMQDescriptor(tmpDescriptor);
+ });
+ }
+
+ if (error != Error::NONE) {
+ return;
+ }
+
+ if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
+ error = mReader.parse();
+ mReader.reset();
+ } else {
+ error = Error::NO_RESOURCES;
+ }
+ };
+ if (mClient_2_2) {
+ ret = mClient_2_2->executeCommands_2_2(commandLength, commandHandles, hidl_callback);
+ } else {
+ ret = mClient->executeCommands(commandLength, commandHandles, hidl_callback);
+ }
+ // executeCommands can fail because of out-of-fd and we do not want to
+ // abort() in that case
+ if (!ret.isOk()) {
+ ALOGE("executeCommands failed because of %s", ret.description().c_str());
+ }
+
+ if (error == Error::NONE) {
+ std::vector<CommandReader::CommandError> commandErrors = mReader.takeErrors();
+
+ for (const auto& cmdErr : commandErrors) {
+ auto command =
+ static_cast<IComposerClient::Command>(mWriter.getCommand(cmdErr.location));
+
+ if (command == IComposerClient::Command::VALIDATE_DISPLAY ||
+ command == IComposerClient::Command::PRESENT_DISPLAY ||
+ command == IComposerClient::Command::PRESENT_OR_VALIDATE_DISPLAY) {
+ error = cmdErr.error;
+ } else {
+ ALOGW("command 0x%x generated error %d", command, cmdErr.error);
+ }
+ }
+ }
+
+ mWriter.reset();
+
+ return error;
+}
+
+// Composer HAL 2.2
+
+Error HidlComposer::setLayerPerFrameMetadata(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
+ if (!mClient_2_2) {
+ return Error::UNSUPPORTED;
+ }
+
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerPerFrameMetadata(perFrameMetadatas);
+ return Error::NONE;
+}
+
+std::vector<IComposerClient::PerFrameMetadataKey> HidlComposer::getPerFrameMetadataKeys(
+ Display display) {
+ std::vector<IComposerClient::PerFrameMetadataKey> keys;
+ if (!mClient_2_2) {
+ return keys;
+ }
+
+ Error error = kDefaultError;
+ if (mClient_2_3) {
+ mClient_2_3->getPerFrameMetadataKeys_2_3(display,
+ [&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ ALOGW("getPerFrameMetadataKeys failed "
+ "with %d",
+ tmpError);
+ return;
+ }
+ keys = tmpKeys;
+ });
+ } else {
+ mClient_2_2
+ ->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ ALOGW("getPerFrameMetadataKeys failed with %d", tmpError);
+ return;
+ }
+
+ keys.clear();
+ for (auto key : tmpKeys) {
+ keys.push_back(static_cast<IComposerClient::PerFrameMetadataKey>(key));
+ }
+ });
+ }
+
+ return keys;
+}
+
+Error HidlComposer::getRenderIntents(Display display, ColorMode colorMode,
+ std::vector<RenderIntent>* outRenderIntents) {
+ if (!mClient_2_2) {
+ outRenderIntents->push_back(RenderIntent::COLORIMETRIC);
+ return Error::NONE;
+ }
+
+ Error error = kDefaultError;
+
+ auto getRenderIntentsLambda = [&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outRenderIntents = tmpKeys;
+ };
+
+ if (mClient_2_3) {
+ mClient_2_3->getRenderIntents_2_3(display, colorMode, getRenderIntentsLambda);
+ } else {
+ mClient_2_2->getRenderIntents(display, static_cast<types::V1_1::ColorMode>(colorMode),
+ getRenderIntentsLambda);
+ }
+
+ return error;
+}
+
+Error HidlComposer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) {
+ if (!mClient_2_2) {
+ *outMatrix = mat4();
+ return Error::NONE;
+ }
+
+ Error error = kDefaultError;
+ mClient_2_2->getDataspaceSaturationMatrix(static_cast<types::V1_1::Dataspace>(dataspace),
+ [&](const auto& tmpError, const auto& tmpMatrix) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outMatrix = mat4(tmpMatrix.data());
+ });
+
+ return error;
+}
+
+// Composer HAL 2.3
+
+Error HidlComposer::getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayIdentificationData(display,
+ [&](const auto& tmpError, const auto& tmpPort,
+ const auto& tmpData) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outPort = tmpPort;
+ *outData = tmpData;
+ });
+
+ return error;
+}
+
+Error HidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerColorTransform(matrix);
+ return Error::NONE;
+}
+
+Error HidlComposer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return Error::BAD_PARAMETER;
+ }
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayedContentSamplingAttributes(display,
+ [&](const auto tmpError,
+ const auto& tmpFormat,
+ const auto& tmpDataspace,
+ const auto& tmpComponentMask) {
+ error = tmpError;
+ if (error == Error::NONE) {
+ *outFormat = tmpFormat;
+ *outDataspace = tmpDataspace;
+ *outComponentMask =
+ static_cast<uint8_t>(
+ tmpComponentMask);
+ }
+ });
+ return error;
+}
+
+Error HidlComposer::setDisplayContentSamplingEnabled(Display display, bool enabled,
+ uint8_t componentMask, uint64_t maxFrames) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ auto enable = enabled ? V2_3::IComposerClient::DisplayedContentSampling::ENABLE
+ : V2_3::IComposerClient::DisplayedContentSampling::DISABLE;
+ return mClient_2_3->setDisplayedContentSamplingEnabled(display, enable, componentMask,
+ maxFrames);
+}
+
+Error HidlComposer::getDisplayedContentSample(Display display, uint64_t maxFrames,
+ uint64_t timestamp, DisplayedFrameStats* outStats) {
+ if (!outStats) {
+ return Error::BAD_PARAMETER;
+ }
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
+ [&](const auto tmpError, auto tmpNumFrames,
+ const auto& tmpSamples0, const auto& tmpSamples1,
+ const auto& tmpSamples2, const auto& tmpSamples3) {
+ error = tmpError;
+ if (error == Error::NONE) {
+ outStats->numFrames = tmpNumFrames;
+ outStats->component_0_sample = tmpSamples0;
+ outStats->component_1_sample = tmpSamples1;
+ outStats->component_2_sample = tmpSamples2;
+ outStats->component_3_sample = tmpSamples3;
+ }
+ });
+ return error;
+}
+
+Error HidlComposer::setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerPerFrameMetadataBlobs(metadata);
+ return Error::NONE;
+}
+
+Error HidlComposer::setDisplayBrightness(Display display, float brightness) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ return mClient_2_3->setDisplayBrightness(display, brightness);
+}
+
+// Composer HAL 2.4
+
+Error HidlComposer::getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+
+ V2_4::Error error = kDefaultError_2_4;
+ if (mClient_2_4) {
+ mClient_2_4->getDisplayCapabilities_2_4(display,
+ [&](const auto& tmpError, const auto& tmpCaps) {
+ error = tmpError;
+ if (error != V2_4::Error::NONE) {
+ return;
+ }
+ *outCapabilities = tmpCaps;
+ });
+ } else {
+ mClient_2_3
+ ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
+ error = static_cast<V2_4::Error>(tmpError);
+ if (error != V2_4::Error::NONE) {
+ return;
+ }
+
+ outCapabilities->resize(tmpCaps.size());
+ std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+ [](auto cap) { return static_cast<DisplayCapability>(cap); });
+ });
+ }
+
+ return static_cast<Error>(error);
+}
+
+V2_4::Error HidlComposer::getDisplayConnectionType(
+ Display display, IComposerClient::DisplayConnectionType* outType) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
+ error = tmpError;
+ if (error != V2_4::Error::NONE) {
+ return;
+ }
+
+ *outType = tmpType;
+ });
+
+ return error;
+}
+
+V2_4::Error HidlComposer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getDisplayVsyncPeriod(display,
+ [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outVsyncPeriod = tmpVsyncPeriod;
+ });
+
+ return error;
+}
+
+V2_4::Error HidlComposer::setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+ [&](const auto& tmpError, const auto& tmpTimeline) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outTimeline = tmpTimeline;
+ });
+
+ return error;
+}
+
+V2_4::Error HidlComposer::setAutoLowLatencyMode(Display display, bool on) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error HidlComposer::getSupportedContentTypes(
+ Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getSupportedContentTypes(displayId,
+ [&](const auto& tmpError,
+ const auto& tmpSupportedContentTypes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupportedContentTypes = tmpSupportedContentTypes;
+ });
+ return error;
+}
+
+V2_4::Error HidlComposer::setContentType(Display display,
+ IComposerClient::ContentType contentType) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setContentType(display, contentType);
+}
+
+V2_4::Error HidlComposer::setLayerGenericMetadata(Display display, Layer layer,
+ const std::string& key, bool mandatory,
+ const std::vector<uint8_t>& value) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+ mWriter.selectDisplay(display);
+ mWriter.selectLayer(layer);
+ mWriter.setLayerGenericMetadata(key, mandatory, value);
+ return Error::NONE;
+}
+
+V2_4::Error HidlComposer::getLayerGenericMetadataKeys(
+ std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outKeys = tmpKeys;
+ });
+ return error;
+}
+
+Error HidlComposer::getClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
+ mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ *outWhitePointNits = -1.f;
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerWhitePointNits(Display, Layer, float) {
+ return Error::NONE;
+}
+
+CommandReader::~CommandReader() {
+ resetData();
+}
+
+Error CommandReader::parse() {
+ resetData();
+
+ IComposerClient::Command command;
+ uint16_t length = 0;
+
+ while (!isEmpty()) {
+ if (!beginCommand(&command, &length)) {
+ break;
+ }
+
+ bool parsed = false;
+ switch (command) {
+ case IComposerClient::Command::SELECT_DISPLAY:
+ parsed = parseSelectDisplay(length);
+ break;
+ case IComposerClient::Command::SET_ERROR:
+ parsed = parseSetError(length);
+ break;
+ case IComposerClient::Command::SET_CHANGED_COMPOSITION_TYPES:
+ parsed = parseSetChangedCompositionTypes(length);
+ break;
+ case IComposerClient::Command::SET_DISPLAY_REQUESTS:
+ parsed = parseSetDisplayRequests(length);
+ break;
+ case IComposerClient::Command::SET_PRESENT_FENCE:
+ parsed = parseSetPresentFence(length);
+ break;
+ case IComposerClient::Command::SET_RELEASE_FENCES:
+ parsed = parseSetReleaseFences(length);
+ break;
+ case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
+ parsed = parseSetPresentOrValidateDisplayResult(length);
+ break;
+ case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
+ parsed = parseSetClientTargetProperty(length);
+ break;
+ default:
+ parsed = false;
+ break;
+ }
+
+ endCommand();
+
+ if (!parsed) {
+ ALOGE("failed to parse command 0x%x length %" PRIu16, command, length);
+ break;
+ }
+ }
+
+ return isEmpty() ? Error::NONE : Error::NO_RESOURCES;
+}
+
+bool CommandReader::parseSelectDisplay(uint16_t length) {
+ if (length != CommandWriterBase::kSelectDisplayLength) {
+ return false;
+ }
+
+ mCurrentReturnData = &mReturnData[read64()];
+
+ return true;
+}
+
+bool CommandReader::parseSetError(uint16_t length) {
+ if (length != CommandWriterBase::kSetErrorLength) {
+ return false;
+ }
+
+ auto location = read();
+ auto error = static_cast<Error>(readSigned());
+
+ mErrors.emplace_back(CommandError{location, error});
+
+ return true;
+}
+
+bool CommandReader::parseSetChangedCompositionTypes(uint16_t length) {
+ // (layer id, composition type) pairs
+ if (length % 3 != 0 || !mCurrentReturnData) {
+ return false;
+ }
+
+ uint32_t count = length / 3;
+ mCurrentReturnData->changedLayers.reserve(count);
+ mCurrentReturnData->compositionTypes.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto type = static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+ readSigned());
+
+ mCurrentReturnData->changedLayers.push_back(layer);
+ mCurrentReturnData->compositionTypes.push_back(type);
+
+ count--;
+ }
+
+ return true;
+}
+
+bool CommandReader::parseSetDisplayRequests(uint16_t length) {
+ // display requests followed by (layer id, layer requests) pairs
+ if (length % 3 != 1 || !mCurrentReturnData) {
+ return false;
+ }
+
+ mCurrentReturnData->displayRequests = read();
+
+ uint32_t count = (length - 1) / 3;
+ mCurrentReturnData->requestedLayers.reserve(count);
+ mCurrentReturnData->requestMasks.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto layerRequestMask = read();
+
+ mCurrentReturnData->requestedLayers.push_back(layer);
+ mCurrentReturnData->requestMasks.push_back(layerRequestMask);
+
+ count--;
+ }
+
+ return true;
+}
+
+bool CommandReader::parseSetPresentFence(uint16_t length) {
+ if (length != CommandWriterBase::kSetPresentFenceLength || !mCurrentReturnData) {
+ return false;
+ }
+
+ if (mCurrentReturnData->presentFence >= 0) {
+ close(mCurrentReturnData->presentFence);
+ }
+ mCurrentReturnData->presentFence = readFence();
+
+ return true;
+}
+
+bool CommandReader::parseSetReleaseFences(uint16_t length) {
+ // (layer id, release fence index) pairs
+ if (length % 3 != 0 || !mCurrentReturnData) {
+ return false;
+ }
+
+ uint32_t count = length / 3;
+ mCurrentReturnData->releasedLayers.reserve(count);
+ mCurrentReturnData->releaseFences.reserve(count);
+ while (count > 0) {
+ auto layer = read64();
+ auto fence = readFence();
+
+ mCurrentReturnData->releasedLayers.push_back(layer);
+ mCurrentReturnData->releaseFences.push_back(fence);
+
+ count--;
+ }
+
+ return true;
+}
+
+bool CommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length) {
+ if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
+ return false;
+ }
+ mCurrentReturnData->presentOrValidateState = read();
+ return true;
+}
+
+bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
+ if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
+ return false;
+ }
+ mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
+ mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
+ return true;
+}
+
+void CommandReader::resetData() {
+ mErrors.clear();
+
+ for (auto& data : mReturnData) {
+ if (data.second.presentFence >= 0) {
+ close(data.second.presentFence);
+ }
+ for (auto fence : data.second.releaseFences) {
+ if (fence >= 0) {
+ close(fence);
+ }
+ }
+ }
+
+ mReturnData.clear();
+ mCurrentReturnData = nullptr;
+}
+
+std::vector<CommandReader::CommandError> CommandReader::takeErrors() {
+ return std::move(mErrors);
+}
+
+bool CommandReader::hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+ uint32_t* outNumLayerRequestMasks) const {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outNumChangedCompositionTypes = 0;
+ *outNumLayerRequestMasks = 0;
+ return false;
+ }
+
+ const ReturnData& data = found->second;
+
+ *outNumChangedCompositionTypes = data.compositionTypes.size();
+ *outNumLayerRequestMasks = data.requestMasks.size();
+
+ return !(data.compositionTypes.empty() && data.requestMasks.empty());
+}
+
+void CommandReader::takeChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ outLayers->clear();
+ outTypes->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outLayers = std::move(data.changedLayers);
+ *outTypes = std::move(data.compositionTypes);
+}
+
+void CommandReader::takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks) {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outDisplayRequestMask = 0;
+ outLayers->clear();
+ outLayerRequestMasks->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outDisplayRequestMask = data.displayRequests;
+ *outLayers = std::move(data.requestedLayers);
+ *outLayerRequestMasks = std::move(data.requestMasks);
+}
+
+void CommandReader::takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences) {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ outLayers->clear();
+ outReleaseFences->clear();
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outLayers = std::move(data.releasedLayers);
+ *outReleaseFences = std::move(data.releaseFences);
+}
+
+void CommandReader::takePresentFence(Display display, int* outPresentFence) {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *outPresentFence = -1;
+ return;
+ }
+
+ ReturnData& data = found->second;
+
+ *outPresentFence = data.presentFence;
+ data.presentFence = -1;
+}
+
+void CommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
+ auto found = mReturnData.find(display);
+ if (found == mReturnData.end()) {
+ *state = -1;
+ return;
+ }
+ ReturnData& data = found->second;
+ *state = data.presentOrValidateState;
+}
+
+void CommandReader::takeClientTargetProperty(
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ auto found = mReturnData.find(display);
+
+ // If not found, return the default values.
+ if (found == mReturnData.end()) {
+ outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+ outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+ return;
+ }
+
+ ReturnData& data = found->second;
+ *outClientTargetProperty = data.clientTargetProperty;
+}
+
+} // namespace Hwc2
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
new file mode 100644
index 0000000..5b2219e
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2016 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 "ComposerHal.h"
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wextra"
+
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
+#include <gui/BufferQueue.h>
+#include <gui/HdrMetadata.h>
+#include <math/mat4.h>
+#include <ui/DisplayedFrameStats.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+
+namespace android::Hwc2 {
+
+namespace types = hardware::graphics::common;
+
+namespace V2_1 = hardware::graphics::composer::V2_1;
+namespace V2_2 = hardware::graphics::composer::V2_2;
+namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
+
+using types::V1_0::ColorTransform;
+using types::V1_0::Transform;
+using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
+
+using V2_1::Config;
+using V2_1::Display;
+using V2_1::Error;
+using V2_1::Layer;
+using V2_4::CommandReaderBase;
+using V2_4::CommandWriterBase;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
+using DisplayCapability = IComposerClient::DisplayCapability;
+using PerFrameMetadata = IComposerClient::PerFrameMetadata;
+using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+
+class CommandReader : public CommandReaderBase {
+public:
+ ~CommandReader();
+
+ // Parse and execute commands from the command queue. The commands are
+ // actually return values from the server and will be saved in ReturnData.
+ Error parse();
+
+ // Get and clear saved errors.
+ struct CommandError {
+ uint32_t location;
+ Error error;
+ };
+ std::vector<CommandError> takeErrors();
+
+ bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
+ uint32_t* outNumLayerRequestMasks) const;
+
+ // Get and clear saved changed composition types.
+ void takeChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes);
+
+ // Get and clear saved display requests.
+ void takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks);
+
+ // Get and clear saved release fences.
+ void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences);
+
+ // Get and clear saved present fence.
+ void takePresentFence(Display display, int* outPresentFence);
+
+ // Get what stage succeeded during PresentOrValidate: Present or Validate
+ void takePresentOrValidateStage(Display display, uint32_t* state);
+
+ // Get the client target properties requested by hardware composer.
+ void takeClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
+private:
+ void resetData();
+
+ bool parseSelectDisplay(uint16_t length);
+ bool parseSetError(uint16_t length);
+ bool parseSetChangedCompositionTypes(uint16_t length);
+ bool parseSetDisplayRequests(uint16_t length);
+ bool parseSetPresentFence(uint16_t length);
+ bool parseSetReleaseFences(uint16_t length);
+ bool parseSetPresentOrValidateDisplayResult(uint16_t length);
+ bool parseSetClientTargetProperty(uint16_t length);
+
+ struct ReturnData {
+ uint32_t displayRequests = 0;
+
+ std::vector<Layer> changedLayers;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition> compositionTypes;
+
+ std::vector<Layer> requestedLayers;
+ std::vector<uint32_t> requestMasks;
+
+ int presentFence = -1;
+
+ std::vector<Layer> releasedLayers;
+ std::vector<int> releaseFences;
+
+ uint32_t presentOrValidateState;
+
+ // Composer 2.4 implementation can return a client target property
+ // structure to indicate the client target properties that hardware
+ // composer requests. The composer client must change the client target
+ // properties to match this request.
+ IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
+ Dataspace::UNKNOWN};
+ };
+
+ std::vector<CommandError> mErrors;
+ std::unordered_map<Display, ReturnData> mReturnData;
+
+ // When SELECT_DISPLAY is parsed, this is updated to point to the
+ // display's return data in mReturnData. We use it to avoid repeated
+ // map lookups.
+ ReturnData* mCurrentReturnData;
+};
+
+// Composer is a wrapper to IComposer, a proxy to server-side composer.
+class HidlComposer final : public Composer {
+public:
+ explicit HidlComposer(const std::string& serviceName);
+ ~HidlComposer() override;
+
+ std::vector<IComposer::Capability> getCapabilities() override;
+ std::string dumpDebugInfo() override;
+
+ void registerCallback(const sp<IComposerCallback>& callback) override;
+
+ // Reset all pending commands in the command buffer. Useful if you want to
+ // skip a frame but have already queued some commands.
+ void resetCommands() override;
+
+ // Explicitly flush all pending commands in the command buffer.
+ Error executeCommands() override;
+
+ uint32_t getMaxVirtualDisplayCount() override;
+ Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
+ Display* outDisplay) override;
+ Error destroyVirtualDisplay(Display display) override;
+
+ Error acceptDisplayChanges(Display display) override;
+
+ Error createLayer(Display display, Layer* outLayer) override;
+ Error destroyLayer(Display display, Layer layer) override;
+
+ Error getActiveConfig(Display display, Config* outConfig) override;
+ Error getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
+ Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
+ Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ Error getDisplayConfigs(Display display, std::vector<Config>* outConfigs);
+ Error getDisplayName(Display display, std::string* outName) override;
+
+ Error getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outLayers,
+ std::vector<uint32_t>* outLayerRequestMasks) override;
+
+ Error getDozeSupport(Display display, bool* outSupport) override;
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ float* outMaxAverageLuminance, float* outMinLuminance) override;
+
+ Error getReleaseFences(Display display, std::vector<Layer>* outLayers,
+ std::vector<int>* outReleaseFences) override;
+
+ Error presentDisplay(Display display, int* outPresentFence) override;
+
+ Error setActiveConfig(Display display, Config config) override;
+
+ /*
+ * The composer caches client targets internally. When target is nullptr,
+ * the composer uses slot to look up the client target from its cache.
+ * When target is not nullptr, the cache is updated with the new target.
+ */
+ Error setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
+ int acquireFence, Dataspace dataspace,
+ const std::vector<IComposerClient::Rect>& damage) override;
+ Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
+ Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
+ Error setOutputBuffer(Display display, const native_handle_t* buffer,
+ int releaseFence) override;
+ Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
+ Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+
+ Error setClientTargetSlotCount(Display display) override;
+
+ Error validateDisplay(Display display, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) override;
+
+ Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
+ int* outPresentFence, uint32_t* state) override;
+
+ Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+ /* see setClientTarget for the purpose of slot */
+ Error setLayerBuffer(Display display, Layer layer, uint32_t slot,
+ const sp<GraphicBuffer>& buffer, int acquireFence) override;
+ Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& damage) override;
+ Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
+ Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
+ Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) override;
+ Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
+ Error setLayerDisplayFrame(Display display, Layer layer,
+ const IComposerClient::Rect& frame) override;
+ Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+ Error setLayerSidebandStream(Display display, Layer layer,
+ const native_handle_t* stream) override;
+ Error setLayerSourceCrop(Display display, Layer layer,
+ const IComposerClient::FRect& crop) override;
+ Error setLayerTransform(Display display, Layer layer, Transform transform) override;
+ Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<IComposerClient::Rect>& visible) override;
+ Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+ // Composer HAL 2.2
+ Error setLayerPerFrameMetadata(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override;
+ std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys(
+ Display display) override;
+ Error getRenderIntents(Display display, ColorMode colorMode,
+ std::vector<RenderIntent>* outRenderIntents) override;
+ Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) override;
+
+ // Composer HAL 2.3
+ Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) override;
+ Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+ Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) override;
+ Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
+ uint64_t maxFrames) override;
+ Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+ DisplayedFrameStats* outStats) override;
+ Error setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
+ Error setDisplayBrightness(Display display, float brightness) override;
+
+ // Composer HAL 2.4
+ bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
+ Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) override;
+ V2_4::Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+ V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) override;
+ V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+ bool mandatory, const std::vector<uint8_t>& value) override;
+ V2_4::Error getLayerGenericMetadataKeys(
+ std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
+
+private:
+ class CommandWriter : public CommandWriterBase {
+ public:
+ explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
+ ~CommandWriter() override {}
+ };
+
+ // Many public functions above simply write a command into the command
+ // queue to batch the calls. validateDisplay and presentDisplay will call
+ // this function to execute the command queue.
+ Error execute();
+
+ sp<V2_1::IComposer> mComposer;
+
+ sp<V2_1::IComposerClient> mClient;
+ sp<V2_2::IComposerClient> mClient_2_2;
+ sp<V2_3::IComposerClient> mClient_2_3;
+ sp<IComposerClient> mClient_2_4;
+
+ // 64KiB minus a small space for metadata such as read/write pointers
+ static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16;
+ // Max number of buffers that may be cached for a given layer
+ // We obtain this number by:
+ // 1. Tightly coupling this cache to the max size of BufferQueue
+ // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
+ static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
+ CommandWriter mWriter;
+ CommandReader mReader;
+};
+
+} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 1765caf..5c2390e 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -19,7 +19,10 @@
#undef LOG_TAG
#define LOG_TAG "PowerAdvisor"
+#include <unistd.h>
#include <cinttypes>
+#include <cstdint>
+#include <optional>
#include <android-base/properties.h>
#include <utils/Log.h>
@@ -27,6 +30,9 @@
#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/IPowerHintSession.h>
+#include <android/hardware/power/WorkDuration.h>
+
#include <binder/IServiceManager.h>
#include "../SurfaceFlingerProperties.h"
@@ -47,10 +53,14 @@
using android::hardware::power::Boost;
using android::hardware::power::IPower;
+using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
-using base::GetIntProperty;
+using android::hardware::power::WorkDuration;
+
using scheduler::OneShotTimer;
+class AidlPowerHalWrapper;
+
PowerAdvisor::~PowerAdvisor() = default;
namespace {
@@ -83,6 +93,13 @@
void PowerAdvisor::onBootFinished() {
mBootFinished.store(true);
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* halWrapper = getPowerHal();
+ if (halWrapper != nullptr && usePowerHintSession()) {
+ mPowerHintSessionRunning = halWrapper->startPowerHintSession();
+ }
+ }
}
void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
@@ -136,6 +153,80 @@
}
}
+// checks both if it supports and if it's enabled
+bool PowerAdvisor::usePowerHintSession() {
+ // uses cached value since the underlying support and flag are unlikely to change at runtime
+ ALOGE_IF(!mPowerHintEnabled.has_value(), "Power hint session cannot be used before boot!");
+ return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
+}
+
+bool PowerAdvisor::supportsPowerHintSession() {
+ // cache to avoid needing lock every time
+ if (!mSupportsPowerHint.has_value()) {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ mSupportsPowerHint = halWrapper->supportsPowerHintSession();
+ }
+ return *mSupportsPowerHint;
+}
+
+bool PowerAdvisor::isPowerHintSessionRunning() {
+ return mPowerHintSessionRunning;
+}
+
+void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
+ // we check "supports" here not "usePowerHintSession" because this needs to work
+ // before the session is actually running, and "use" will always fail before boot
+ // we store the values passed in before boot to start the session with during onBootFinished
+ if (!supportsPowerHintSession()) {
+ ALOGV("Power hint session target duration cannot be set, skipping");
+ return;
+ }
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ if (halWrapper != nullptr) {
+ halWrapper->setTargetWorkDuration(targetDurationNanos);
+ }
+ }
+}
+
+void PowerAdvisor::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
+ // we check "supports" here not "usePowerHintSession" because this needs to wsork
+ // before the session is actually running, and "use" will always fail before boot.
+ // we store the values passed in before boot to start the session with during onBootFinished
+ if (!supportsPowerHintSession()) {
+ ALOGV("Power hint session thread ids cannot be set, skipping");
+ return;
+ }
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ if (halWrapper != nullptr) {
+ halWrapper->setPowerHintSessionThreadIds(const_cast<std::vector<int32_t>&>(threadIds));
+ }
+ }
+}
+
+void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
+ if (!mBootFinished || !usePowerHintSession()) {
+ ALOGV("Actual work duration power hint cannot be sent, skipping");
+ return;
+ }
+ {
+ std::lock_guard lock(mPowerHalMutex);
+ HalWrapper* const halWrapper = getPowerHal();
+ if (halWrapper != nullptr) {
+ halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
+ }
+ }
+}
+
+// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
+void PowerAdvisor::enablePowerHint(bool enabled) {
+ mPowerHintEnabled = enabled;
+}
+
class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
public:
HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -178,6 +269,26 @@
return true;
}
+ bool supportsPowerHintSession() override { return false; }
+
+ bool isPowerHintSessionRunning() override { return false; }
+
+ void restartPowerHintSession() override {}
+
+ void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {}
+
+ bool startPowerHintSession() override { return false; }
+
+ void setTargetWorkDuration(int64_t) override {}
+
+ void sendActualWorkDuration(int64_t, nsecs_t) override {}
+
+ bool shouldReconnectHAL() override { return false; }
+
+ std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; }
+
+ std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; }
+
private:
const sp<V1_3::IPower> mPowerHal = nullptr;
};
@@ -195,9 +306,21 @@
if (!ret.isOk()) {
mHasDisplayUpdateImminent = false;
}
+
+ // This just gives a number not a binder status, so no .isOk()
+ mSupportsPowerHints = mPowerHal->getInterfaceVersion() >= 2;
+
+ if (mSupportsPowerHints) {
+ mPowerHintQueue.reserve(MAX_QUEUE_SIZE);
+ }
}
- ~AidlPowerHalWrapper() override = default;
+ ~AidlPowerHalWrapper() override {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
+ }
+ };
static std::unique_ptr<HalWrapper> connect() {
// This only waits if the service is actually declared
@@ -232,10 +355,147 @@
return ret.isOk();
}
+ // only version 2+ of the aidl supports power hint sessions, hidl has no support
+ bool supportsPowerHintSession() override { return mSupportsPowerHints; }
+
+ bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }
+
+ void closePowerHintSession() {
+ if (mPowerHintSession != nullptr) {
+ mPowerHintSession->close();
+ mPowerHintSession = nullptr;
+ }
+ }
+
+ void restartPowerHintSession() {
+ closePowerHintSession();
+ startPowerHintSession();
+ }
+
+ void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
+ if (threadIds != mPowerHintThreadIds) {
+ mPowerHintThreadIds = threadIds;
+ if (isPowerHintSessionRunning()) {
+ restartPowerHintSession();
+ }
+ }
+ }
+
+ bool startPowerHintSession() override {
+ if (mPowerHintSession != nullptr || !mPowerHintTargetDuration.has_value() ||
+ mPowerHintThreadIds.empty()) {
+ ALOGV("Cannot start power hint session, skipping");
+ return false;
+ }
+ auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mPowerHintThreadIds, *mPowerHintTargetDuration,
+ &mPowerHintSession);
+ if (!ret.isOk()) {
+ ALOGW("Failed to start power hint session with error: %s",
+ ret.exceptionToString(ret.exceptionCode()).c_str());
+ // Indicate to the poweradvisor that this wrapper likely needs to be remade
+ mShouldReconnectHal = true;
+ }
+ return isPowerHintSessionRunning();
+ }
+
+ bool shouldSetTargetDuration(int64_t targetDurationNanos) {
+ if (!mLastTargetDurationSent.has_value()) {
+ return true;
+ }
+
+ // report if the change in target from our last submission to now exceeds the threshold
+ return abs(1.0 -
+ static_cast<double>(*mLastTargetDurationSent) /
+ static_cast<double>(targetDurationNanos)) >=
+ ALLOWED_TARGET_DEVIATION_PERCENT;
+ }
+
+ void setTargetWorkDuration(int64_t targetDurationNanos) override {
+ mPowerHintTargetDuration = targetDurationNanos;
+ if (shouldSetTargetDuration(targetDurationNanos) && isPowerHintSessionRunning()) {
+ mLastTargetDurationSent = targetDurationNanos;
+ auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
+ if (!ret.isOk()) {
+ ALOGW("Failed to set power hint target work duration with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ }
+ }
+
+ bool shouldReportActualDurationsNow() {
+ // report if we have never reported before or have exceeded the max queue size
+ if (!mLastMessageReported.has_value() || mPowerHintQueue.size() >= MAX_QUEUE_SIZE) {
+ return true;
+ }
+
+ // duration of most recent timing
+ const double mostRecentActualDuration =
+ static_cast<double>(mPowerHintQueue.back().durationNanos);
+ // duration of the last timing actually reported to the powerhal
+ const double lastReportedActualDuration =
+ static_cast<double>(mLastMessageReported->durationNanos);
+
+ // report if the change in duration from then to now exceeds the threshold
+ return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
+ ALLOWED_ACTUAL_DEVIATION_PERCENT;
+ }
+
+ void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
+ if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
+ ALOGV("Failed to send actual work duration, skipping");
+ return;
+ }
+
+ WorkDuration duration;
+ duration.durationNanos = actualDurationNanos;
+ duration.timeStampNanos = timeStampNanos;
+ mPowerHintQueue.push_back(duration);
+
+ // This rate limiter queues similar duration reports to the powerhal into
+ // batches to avoid excessive binder calls. The criteria to send a given batch
+ // are outlined in shouldReportActualDurationsNow()
+ if (shouldReportActualDurationsNow()) {
+ auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
+ if (!ret.isOk()) {
+ ALOGW("Failed to report actual work durations with error: %s",
+ ret.exceptionMessage().c_str());
+ mShouldReconnectHal = true;
+ }
+ mPowerHintQueue.clear();
+ mLastMessageReported = duration;
+ }
+ }
+
+ bool shouldReconnectHAL() override { return mShouldReconnectHal; }
+
+ std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }
+
+ std::optional<int64_t> getTargetWorkDuration() override { return mPowerHintTargetDuration; }
+
private:
+ // max number of messages allowed in mPowerHintQueue before reporting is forced
+ static constexpr int32_t MAX_QUEUE_SIZE = 15;
+ // max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
+ static constexpr double ALLOWED_ACTUAL_DEVIATION_PERCENT = 0.1;
+ // max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
+ static constexpr double ALLOWED_TARGET_DEVIATION_PERCENT = 0.05;
+
const sp<IPower> mPowerHal = nullptr;
bool mHasExpensiveRendering = false;
bool mHasDisplayUpdateImminent = false;
+ bool mShouldReconnectHal = false; // used to indicate an error state and need for reconstruction
+ // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
+ sp<IPowerHintSession> mPowerHintSession = nullptr;
+ std::vector<WorkDuration> mPowerHintQueue;
+ // halwrapper owns these values so we can init when we want and reconnect if broken
+ std::optional<int64_t> mPowerHintTargetDuration;
+ std::vector<int32_t> mPowerHintThreadIds;
+ // keep track of the last messages sent for rate limiter change detection
+ std::optional<WorkDuration> mLastMessageReported;
+ std::optional<int64_t> mLastTargetDurationSent;
+ bool mSupportsPowerHints;
};
PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
@@ -246,6 +506,15 @@
return nullptr;
}
+ // grab old hint session values before we destroy any existing wrapper
+ std::vector<int32_t> oldPowerHintSessionThreadIds;
+ std::optional<int64_t> oldTargetWorkDuration;
+
+ if (sHalWrapper != nullptr) {
+ oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
+ oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
+ }
+
// If we used to have a HAL, but it stopped responding, attempt to reconnect
if (mReconnectPowerHal) {
sHalWrapper = nullptr;
@@ -253,15 +522,34 @@
}
if (sHalWrapper != nullptr) {
- return sHalWrapper.get();
+ auto wrapper = sHalWrapper.get();
+ // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
+ if (!wrapper->shouldReconnectHAL()) {
+ return wrapper;
+ }
+ sHalWrapper = nullptr;
}
+ // at this point, we know for sure there is no running session
+ mPowerHintSessionRunning = false;
+
// First attempt to connect to the AIDL Power HAL
sHalWrapper = AidlPowerHalWrapper::connect();
// If that didn't succeed, attempt to connect to the HIDL Power HAL
if (sHalWrapper == nullptr) {
sHalWrapper = HidlPowerHalWrapper::connect();
+ } else { // if AIDL, pass on any existing hint session values
+ // thread ids always safe to set
+ sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
+ // only set duration and start if duration is defined
+ if (oldTargetWorkDuration.has_value()) {
+ sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
+ // only start if possible to run and both threadids and duration are defined
+ if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
+ mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
+ }
+ }
}
// If we make it to this point and still don't have a HAL, it's unlikely we
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index f2d0766..b8fd17d 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -40,6 +40,13 @@
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual bool isUsingExpensiveRendering() = 0;
virtual void notifyDisplayUpdateImminent() = 0;
+ virtual bool usePowerHintSession() = 0;
+ virtual bool supportsPowerHintSession() = 0;
+ virtual bool isPowerHintSessionRunning() = 0;
+ virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
+ virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
+ virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
+ virtual void enablePowerHint(bool enabled) = 0;
};
namespace impl {
@@ -54,6 +61,17 @@
virtual bool setExpensiveRendering(bool enabled) = 0;
virtual bool notifyDisplayUpdateImminent() = 0;
+ virtual bool supportsPowerHintSession() = 0;
+ virtual bool isPowerHintSessionRunning() = 0;
+ virtual void restartPowerHintSession() = 0;
+ virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
+ virtual bool startPowerHintSession() = 0;
+ virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
+ virtual void sendActualWorkDuration(int64_t actualDurationNanos,
+ nsecs_t timeStampNanos) = 0;
+ virtual bool shouldReconnectHAL() = 0;
+ virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
+ virtual std::optional<int64_t> getTargetWorkDuration() = 0;
};
PowerAdvisor(SurfaceFlinger& flinger);
@@ -62,8 +80,15 @@
void init() override;
void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
- bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }
+ bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
void notifyDisplayUpdateImminent() override;
+ bool usePowerHintSession() override;
+ bool supportsPowerHintSession() override;
+ bool isPowerHintSessionRunning() override;
+ void setTargetWorkDuration(int64_t targetDurationNanos) override;
+ void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
+ void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
+ void enablePowerHint(bool enabled) override;
private:
HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
@@ -71,6 +96,9 @@
std::mutex mPowerHalMutex;
std::atomic_bool mBootFinished = false;
+ std::optional<bool> mPowerHintEnabled;
+ std::optional<bool> mSupportsPowerHint;
+ bool mPowerHintSessionRunning = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
bool mNotifiedExpensiveRendering = false;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 82a9ae2..b4fb51f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -21,20 +21,18 @@
// #define LOG_NDEBUG 0
#include "VirtualDisplaySurface.h"
-#include <inttypes.h>
+#include <cinttypes>
#include "HWComposer.h"
#include "SurfaceFlinger.h"
+#include <ftl/Flags.h>
+#include <ftl/enum.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
#include <system/window.h>
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
mDisplayName.c_str(), ##__VA_ARGS__)
#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
@@ -42,20 +40,11 @@
#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
mDisplayName.c_str(), ##__VA_ARGS__)
-static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
- switch (type) {
- case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
- return "UNKNOWN";
- case compositionengine::DisplaySurface::COMPOSITION_GPU:
- return "GPU";
- case compositionengine::DisplaySurface::COMPOSITION_HWC:
- return "HWC";
- case compositionengine::DisplaySurface::COMPOSITION_MIXED:
- return "MIXED";
- default:
- return "<INVALID>";
- }
-}
+#define UNSUPPORTED() \
+ VDS_LOGE("%s: Invalid operation on virtual display", __func__); \
+ return INVALID_OPERATION
+
+namespace android {
VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
const sp<IGraphicBufferProducer>& sink,
@@ -76,14 +65,10 @@
mQueueBufferOutput(),
mSinkBufferWidth(0),
mSinkBufferHeight(0),
- mCompositionType(COMPOSITION_UNKNOWN),
mFbFence(Fence::NO_FENCE),
mOutputFence(Fence::NO_FENCE),
mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mDbgState(DBG_STATE_IDLE),
- mDbgLastCompositionType(COMPOSITION_UNKNOWN),
- mMustRecompose(false),
mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
mSource[SOURCE_SINK] = sink;
mSource[SOURCE_SCRATCH] = bqProducer;
@@ -131,9 +116,9 @@
mMustRecompose = mustRecompose;
- VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
- "Unexpected beginFrame() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_BEGUN;
+ VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Begun;
return refreshOutputBuffer();
}
@@ -143,12 +128,12 @@
return NO_ERROR;
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
- "Unexpected prepareFrame() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_PREPARED;
+ VDS_LOGW_IF(mDebugState != DebugState::Begun, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Prepared;
mCompositionType = compositionType;
- if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
+ if (mForceHwcCopy && mCompositionType == CompositionType::Gpu) {
// Some hardware can do RGB->YUV conversion more efficiently in hardware
// controlled by HWC than in hardware controlled by the video encoder.
// Forcing GPU-composed frames to go through an extra copy by the HWC
@@ -157,16 +142,16 @@
//
// On the other hand, when the consumer prefers RGB or can consume RGB
// inexpensively, this forces an unnecessary copy.
- mCompositionType = COMPOSITION_MIXED;
+ mCompositionType = CompositionType::Mixed;
}
- if (mCompositionType != mDbgLastCompositionType) {
- VDS_LOGV("prepareFrame: composition type changed to %s",
- dbgCompositionTypeStr(mCompositionType));
- mDbgLastCompositionType = mCompositionType;
+ if (mCompositionType != mDebugLastCompositionType) {
+ VDS_LOGV("%s: composition type changed to %s", __func__,
+ toString(mCompositionType).c_str());
+ mDebugLastCompositionType = mCompositionType;
}
- if (mCompositionType != COMPOSITION_GPU &&
+ if (mCompositionType != CompositionType::Gpu &&
(mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
// We must have just switched from GPU-only to MIXED or HWC
// composition. Stop using the format and usage requested by the GPU
@@ -191,33 +176,32 @@
return NO_ERROR;
}
- if (mCompositionType == COMPOSITION_HWC) {
- VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
- "Unexpected advanceFrame() in %s state on HWC frame",
- dbgStateStr());
+ if (mCompositionType == CompositionType::Hwc) {
+ VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state on HWC frame",
+ __func__, ftl::enum_string(mDebugState).c_str());
} else {
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
- "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
+ VDS_LOGW_IF(mDebugState != DebugState::GpuDone,
+ "Unexpected %s in %s state on GPU/MIXED frame", __func__,
+ ftl::enum_string(mDebugState).c_str());
}
- mDbgState = DBG_STATE_HWC;
+ mDebugState = DebugState::Hwc;
if (mOutputProducerSlot < 0 ||
- (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
+ (mCompositionType != CompositionType::Hwc && mFbProducerSlot < 0)) {
// Last chance bailout if something bad happened earlier. For example,
// in a graphics API configuration, if the sink disappears then dequeueBuffer
// will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
- VDS_LOGE("advanceFrame: no buffer, bailing out");
+ VDS_LOGE("%s: no buffer, bailing out", __func__);
return NO_MEMORY;
}
sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
- VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
- mFbProducerSlot, fbBuffer.get(),
- mOutputProducerSlot, outBuffer.get());
+ VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
+ mOutputProducerSlot, outBuffer.get());
const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
LOG_FATAL_IF(!halDisplayId);
@@ -245,16 +229,16 @@
return;
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
- "Unexpected onFrameCommitted() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_IDLE;
+ VDS_LOGW_IF(mDebugState != DebugState::Hwc, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Idle;
sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
- if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
+ if (mCompositionType == CompositionType::Mixed && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
- VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
+ VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
retireFence);
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
@@ -263,7 +247,7 @@
if (mOutputProducerSlot >= 0) {
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
QueueBufferOutput qbo;
- VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
+ VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot);
if (mMustRecompose) {
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
QueueBufferInput(
@@ -308,8 +292,8 @@
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
- dbgStateStr());
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s pslot=%d in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
*outBuf = mProducerBuffers[pslot];
return NO_ERROR;
@@ -334,8 +318,8 @@
if (result < 0)
return result;
int pslot = mapSource2ProducerSlot(source, *sslot);
- VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
- dbgSourceStr(source), *sslot, pslot, result);
+ VDS_LOGV("%s(%s): sslot=%d pslot=%d result=%d", __func__, ftl::enum_string(source).c_str(),
+ *sslot, pslot, result);
uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
// reset producer slot reallocation flag
@@ -363,10 +347,9 @@
mSource[source]->cancelBuffer(*sslot, *fence);
return result;
}
- VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64,
- dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
- mProducerBuffers[pslot]->getPixelFormat(),
- mProducerBuffers[pslot]->getUsage());
+ VDS_LOGV("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__,
+ ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(),
+ mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage());
// propagate reallocation to VDS consumer
mProducerSlotNeedReallocation |= 1ULL << pslot;
@@ -384,11 +367,11 @@
outTimestamps);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
- "Unexpected dequeueBuffer() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_GPU;
+ VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Gpu;
- VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
+ VDS_LOGV("%s %dx%d fmt=%d usage=%#" PRIx64, __func__, w, h, format, usage);
status_t result = NO_ERROR;
Source source = fbSourceForCompositionType(mCompositionType);
@@ -401,7 +384,7 @@
// will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
- VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+ VDS_LOGE("%s: no buffer, bailing out", __func__);
return NO_MEMORY;
}
@@ -417,12 +400,11 @@
(format != 0 && format != buf->getPixelFormat()) ||
(w != 0 && w != mSinkBufferWidth) ||
(h != 0 && h != mSinkBufferHeight)) {
- VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
- "want %dx%d fmt=%d use=%#" PRIx64 ", "
- "have %dx%d fmt=%d use=%#" PRIx64,
- w, h, format, usage,
- mSinkBufferWidth, mSinkBufferHeight,
- buf->getPixelFormat(), buf->getUsage());
+ VDS_LOGV("%s: dequeueing new output buffer: "
+ "want %dx%d fmt=%d use=%#" PRIx64 ", "
+ "have %dx%d fmt=%d use=%#" PRIx64,
+ __func__, w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight,
+ buf->getPixelFormat(), buf->getUsage());
mOutputFormat = format;
mOutputUsage = usage;
result = refreshOutputBuffer();
@@ -452,21 +434,16 @@
return result;
}
-status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
- VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachBuffer(int) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::detachNextBuffer(
- sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
- VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachNextBuffer(sp<GraphicBuffer>*, sp<Fence>*) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
- const sp<GraphicBuffer>& /* buffer */) {
- VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::attachBuffer(int*, const sp<GraphicBuffer>&) {
+ UNSUPPORTED();
}
status_t VirtualDisplaySurface::queueBuffer(int pslot,
@@ -475,14 +452,14 @@
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- mDbgState = DBG_STATE_GPU_DONE;
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::GpuDone;
- VDS_LOGV("queueBuffer pslot=%d", pslot);
+ VDS_LOGV("%s pslot=%d", __func__, pslot);
status_t result;
- if (mCompositionType == COMPOSITION_MIXED) {
+ if (mCompositionType == CompositionType::Mixed) {
// Queue the buffer back into the scratch pool
QueueBufferOutput scratchQBO;
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
@@ -498,15 +475,15 @@
if (result != NO_ERROR)
return result;
VDS_LOGW_IF(item.mSlot != sslot,
- "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
- item.mSlot, sslot);
+ "%s: acquired sslot %d from SCRATCH after queueing sslot %d", __func__,
+ item.mSlot, sslot);
mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mSlot);
mFbFence = mSlots[item.mSlot].mFence;
} else {
- LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
- "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
- dbgCompositionTypeStr(mCompositionType));
+ LOG_FATAL_IF(mCompositionType != CompositionType::Gpu,
+ "Unexpected %s in state %s for composition type %s", __func__,
+ ftl::enum_string(mDebugState).c_str(), toString(mCompositionType).c_str());
// Extract the GPU release fence for HWC to acquire
int64_t timestamp;
@@ -533,9 +510,9 @@
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- VDS_LOGV("cancelBuffer pslot=%d", pslot);
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
+ VDS_LOGV("%s pslot=%d", __func__, pslot);
Source source = fbSourceForCompositionType(mCompositionType);
return mSource[source]->cancelBuffer(
mapProducer2SourceSlot(source, pslot), fence);
@@ -573,8 +550,8 @@
return mSource[SOURCE_SINK]->disconnect(api, mode);
}
-status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>&) {
+ UNSUPPORTED();
}
void VirtualDisplaySurface::allocateBuffers(uint32_t /* width */,
@@ -586,40 +563,32 @@
return INVALID_OPERATION;
}
-status_t VirtualDisplaySurface::setGenerationNumber(uint32_t /* generation */) {
- ALOGE("setGenerationNumber not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setGenerationNumber(uint32_t) {
+ UNSUPPORTED();
}
String8 VirtualDisplaySurface::getConsumerName() const {
return String8("VirtualDisplaySurface");
}
-status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) {
- ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSharedBufferMode(bool) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
- ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setAutoRefresh(bool) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
- ALOGE("setDequeueTimeout not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::getLastQueuedBuffer(
- sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/,
- float[16] /* outTransformMatrix*/) {
- ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getLastQueuedBuffer(sp<GraphicBuffer>*, sp<Fence>*, float[16]) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const {
- ALOGE("getUniqueId not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getUniqueId(uint64_t*) const {
+ UNSUPPORTED();
}
status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const {
@@ -633,7 +602,7 @@
}
void VirtualDisplaySurface::resetPerFrameState() {
- mCompositionType = COMPOSITION_UNKNOWN;
+ mCompositionType = CompositionType::Unknown;
mFbFence = Fence::NO_FENCE;
mOutputFence = Fence::NO_FENCE;
mOutputProducerSlot = -1;
@@ -682,39 +651,16 @@
return mapSource2ProducerSlot(source, pslot);
}
-VirtualDisplaySurface::Source
-VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
- return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
+auto VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) -> Source {
+ return type == CompositionType::Mixed ? SOURCE_SCRATCH : SOURCE_SINK;
}
-const char* VirtualDisplaySurface::dbgStateStr() const {
- switch (mDbgState) {
- case DBG_STATE_IDLE:
- return "IDLE";
- case DBG_STATE_PREPARED:
- return "PREPARED";
- case DBG_STATE_GPU:
- return "GPU";
- case DBG_STATE_GPU_DONE:
- return "GPU_DONE";
- case DBG_STATE_HWC:
- return "HWC";
- default:
- return "INVALID";
- }
+std::string VirtualDisplaySurface::toString(CompositionType type) {
+ using namespace std::literals;
+ return type == CompositionType::Unknown ? "Unknown"s : Flags(type).string();
}
-const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
- switch (s) {
- case SOURCE_SINK: return "SINK";
- case SOURCE_SCRATCH: return "SCRATCH";
- default: return "INVALID";
- }
-}
-
-// ---------------------------------------------------------------------------
} // namespace android
-// ---------------------------------------------------------------------------
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index bbb6306..7720713 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
-#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+#pragma once
#include <optional>
#include <string>
@@ -28,9 +27,7 @@
#include "DisplayIdentification.h"
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
class HWComposer;
class IProducerListener;
@@ -94,7 +91,13 @@
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
private:
- enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1};
+ enum Source : size_t {
+ SOURCE_SINK = 0,
+ SOURCE_SCRATCH = 1,
+
+ ftl_first = SOURCE_SINK,
+ ftl_last = SOURCE_SCRATCH,
+ };
virtual ~VirtualDisplaySurface();
@@ -133,6 +136,8 @@
// Utility methods
//
static Source fbSourceForCompositionType(CompositionType);
+ static std::string toString(CompositionType);
+
status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
void updateQueueBufferOutput(QueueBufferOutput&&);
void resetPerFrameState();
@@ -197,7 +202,7 @@
// Composition type and graphics buffer source for the current frame.
// Valid after prepareFrame(), cleared in onFrameCommitted.
- CompositionType mCompositionType;
+ CompositionType mCompositionType = CompositionType::Unknown;
// mFbFence is the fence HWC should wait for before reading the framebuffer
// target buffer.
@@ -219,47 +224,42 @@
// +-----------+-------------------+-------------+
// | State | Event || Next State |
// +-----------+-------------------+-------------+
- // | IDLE | beginFrame || BEGUN |
- // | BEGUN | prepareFrame || PREPARED |
- // | PREPARED | dequeueBuffer [1] || GPU |
- // | PREPARED | advanceFrame [2] || HWC |
- // | GPU | queueBuffer || GPU_DONE |
- // | GPU_DONE | advanceFrame || HWC |
- // | HWC | onFrameCommitted || IDLE |
+ // | Idle | beginFrame || Begun |
+ // | Begun | prepareFrame || Prepared |
+ // | Prepared | dequeueBuffer [1] || Gpu |
+ // | Prepared | advanceFrame [2] || Hwc |
+ // | Gpu | queueBuffer || GpuDone |
+ // | GpuDone | advanceFrame || Hwc |
+ // | Hwc | onFrameCommitted || Idle |
// +-----------+-------------------++------------+
- // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
- // [2] COMPOSITION_HWC frames.
+ // [1] CompositionType::Gpu and CompositionType::Mixed frames.
+ // [2] CompositionType::Hwc frames.
//
- enum DbgState {
+ enum class DebugState {
// no buffer dequeued, don't know anything about the next frame
- DBG_STATE_IDLE,
+ Idle,
// output buffer dequeued, framebuffer source not yet known
- DBG_STATE_BEGUN,
+ Begun,
// output buffer dequeued, framebuffer source known but not provided
// to GPU yet.
- DBG_STATE_PREPARED,
+ Prepared,
// GPU driver has a buffer dequeued
- DBG_STATE_GPU,
+ Gpu,
// GPU driver has queued the buffer, we haven't sent it to HWC yet
- DBG_STATE_GPU_DONE,
+ GpuDone,
// HWC has the buffer for this frame
- DBG_STATE_HWC,
+ Hwc,
+
+ ftl_last = Hwc
};
- DbgState mDbgState;
- CompositionType mDbgLastCompositionType;
+ DebugState mDebugState = DebugState::Idle;
+ CompositionType mDebugLastCompositionType = CompositionType::Unknown;
- const char* dbgStateStr() const;
- static const char* dbgSourceStr(Source s);
-
- bool mMustRecompose;
+ bool mMustRecompose = false;
compositionengine::impl::HwcBufferCache mHwcBufferCache;
bool mForceHwcCopy;
};
-// ---------------------------------------------------------------------------
} // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index 86c6b21..cc85352 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -109,7 +109,8 @@
auto* compositionState = editCompositionState();
compositionState->color = getColor();
- compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
}
sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
@@ -136,8 +137,7 @@
sp<Layer> EffectLayer::createClone() {
sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
- LayerMetadata()));
+ LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
layer->setInitialValuesForClone(this);
return layer;
}
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
new file mode 100644
index 0000000..7602e6d
--- /dev/null
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "FlagManager.h"
+
+#include <SurfaceFlingerProperties.sysprop.h>
+#include <android-base/parsebool.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+#include <cinttypes>
+
+namespace android {
+static constexpr const char* kExperimentNamespace = "surface_flinger_native_boot";
+static constexpr const int64_t kDemoFlag = -1;
+
+FlagManager::~FlagManager() = default;
+
+void FlagManager::dump(std::string& result) const {
+ base::StringAppendF(&result, "FlagManager values: \n");
+ base::StringAppendF(&result, "demo_flag: %" PRId64 "\n", demo_flag());
+ base::StringAppendF(&result, "use_adpf_cpu_hint: %s\n", use_adpf_cpu_hint() ? "true" : "false");
+}
+
+namespace {
+template <typename T>
+std::optional<T> doParse(const char* str);
+
+template <>
+[[maybe_unused]] std::optional<int32_t> doParse(const char* str) {
+ int32_t ret;
+ return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
+}
+
+template <>
+[[maybe_unused]] std::optional<int64_t> doParse(const char* str) {
+ int64_t ret;
+ return base::ParseInt(str, &ret) ? std::make_optional(ret) : std::nullopt;
+}
+
+template <>
+[[maybe_unused]] std::optional<bool> doParse(const char* str) {
+ base::ParseBoolResult parseResult = base::ParseBool(str);
+ switch (parseResult) {
+ case base::ParseBoolResult::kTrue:
+ return std::make_optional(true);
+ case base::ParseBoolResult::kFalse:
+ return std::make_optional(false);
+ case base::ParseBoolResult::kError:
+ return std::nullopt;
+ }
+}
+} // namespace
+
+std::string FlagManager::getServerConfigurableFlag(const std::string& experimentFlagName) const {
+ return server_configurable_flags::GetServerConfigurableFlag(kExperimentNamespace,
+ experimentFlagName, "");
+}
+
+template int32_t FlagManager::getValue<int32_t>(const std::string&, std::optional<int32_t>,
+ int32_t) const;
+template int64_t FlagManager::getValue<int64_t>(const std::string&, std::optional<int64_t>,
+ int64_t) const;
+template bool FlagManager::getValue<bool>(const std::string&, std::optional<bool>, bool) const;
+template <typename T>
+T FlagManager::getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+ T defaultValue) const {
+ // System property takes precedence over the experiment config server value.
+ if (systemPropertyOpt.has_value()) {
+ return *systemPropertyOpt;
+ }
+ std::string str = getServerConfigurableFlag(experimentFlagName);
+ return str.empty() ? defaultValue : doParse<T>(str.c_str()).value_or(defaultValue);
+}
+
+int64_t FlagManager::demo_flag() const {
+ std::optional<int64_t> sysPropVal = std::nullopt;
+ return getValue("DemoFeature__demo_flag", sysPropVal, kDemoFlag);
+}
+
+bool FlagManager::use_adpf_cpu_hint() const {
+ std::optional<bool> sysPropVal = std::nullopt;
+ return getValue("AdpfFeature__adpf_cpu_hint", sysPropVal, false);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
new file mode 100644
index 0000000..24d83a2
--- /dev/null
+++ b/services/surfaceflinger/FlagManager.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 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 <cstdint>
+#include <optional>
+#include <string>
+
+namespace android {
+// Manages flags for SurfaceFlinger, including default values, system properties, and Mendel
+// experiment configuration values.
+class FlagManager {
+public:
+ FlagManager() = default;
+ virtual ~FlagManager();
+ void dump(std::string& result) const;
+
+ int64_t demo_flag() const;
+
+ bool use_adpf_cpu_hint() const;
+
+private:
+ friend class FlagManagerTest;
+
+ // Wrapper for mocking in test.
+ virtual std::string getServerConfigurableFlag(const std::string& experimentFlagName) const;
+
+ template <typename T>
+ T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+ T defaultValue) const;
+};
+} // namespace android
diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Fps.h
deleted file mode 100644
index e9f06e5..0000000
--- a/services/surfaceflinger/Fps.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cmath>
-#include <ostream>
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-// Value which represents "frames per second". This class is a wrapper around
-// float, providing some useful utilities, such as comparisons with tolerance
-// and converting between period duration and frequency.
-class Fps {
-public:
- static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); }
-
- Fps() = default;
- explicit constexpr Fps(float fps)
- : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {}
-
- constexpr float getValue() const { return fps; }
-
- constexpr nsecs_t getPeriodNsecs() const { return period; }
-
- bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; }
-
- // DO NOT use for std::sort. Instead use comparesLess().
- bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; }
-
- bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; }
-
- bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); }
-
- bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); }
-
- bool isValid() const { return fps > 0.0f; }
-
- int getIntValue() const { return static_cast<int>(std::round(fps)); }
-
- // Use this comparator for sorting. Using a comparator with margins can
- // cause std::sort to crash.
- inline static bool comparesLess(const Fps& left, const Fps& right) {
- return left.fps < right.fps;
- }
-
- // Compares two FPS with margin.
- // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c.
- // DO NOT use with hash maps. Instead use EqualsInBuckets.
- struct EqualsWithMargin {
- bool operator()(const Fps& left, const Fps& right) const {
- return left.equalsWithMargin(right);
- }
- };
-
- // Equals comparator which can be used with hash maps.
- // It's guaranteed that if two elements are equal, then their hashes are equal.
- struct EqualsInBuckets {
- bool operator()(const Fps& left, const Fps& right) const {
- return left.getBucket() == right.getBucket();
- }
- };
-
- inline friend std::string to_string(const Fps& fps) {
- return base::StringPrintf("%.2ffps", fps.fps);
- }
-
- inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) {
- return os << to_string(fps);
- }
-
-private:
- friend std::hash<android::Fps>;
-
- constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {}
-
- float getBucket() const { return std::round(fps / kMargin); }
-
- static constexpr float kMargin = 0.001f;
- float fps = 0;
- nsecs_t period = 0;
-};
-
-static_assert(std::is_trivially_copyable_v<Fps>);
-
-} // namespace android
-
-namespace std {
-template <>
-struct hash<android::Fps> {
- std::size_t operator()(const android::Fps& fps) const {
- return std::hash<float>()(fps.getBucket());
- }
-};
-} // namespace std
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index bd7b9a5..438b1aa 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -24,6 +24,7 @@
#include "Clock.h"
#include "FrameTimeline/FrameTimeline.h"
+#include "WpHash.h"
namespace android {
@@ -50,11 +51,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
struct TrackedListener {
sp<gui::IFpsListener> listener;
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 10a5833..2d4ec04 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -13,6 +13,9 @@
srcs: [
"FrameTimeline.cpp",
],
+ header_libs: [
+ "libscheduler_headers",
+ ],
shared_libs: [
"android.hardware.graphics.composer@2.4",
"libbase",
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c294ff2..0c4e112 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -304,7 +304,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode gameMode)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -778,7 +778,7 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) {
+ std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 139f91f..36d6290 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,10 +16,17 @@
#pragma once
-#include <../Fps.h>
-#include <../TimeStats/TimeStats.h>
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+
#include <gui/ISurfaceComposer.h>
#include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
#include <perfetto/trace/android/frame_timeline_event.pbzero.h>
#include <perfetto/tracing.h>
#include <ui/FenceTime.h>
@@ -28,8 +35,9 @@
#include <utils/Timers.h>
#include <utils/Vector.h>
-#include <deque>
-#include <mutex>
+#include <scheduler/Fps.h>
+
+#include "../TimeStats/TimeStats.h"
namespace android::frametimeline {
@@ -154,7 +162,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -260,7 +268,7 @@
// buffer(animations)
bool mIsBuffer;
// GameMode from the layer. Used in metrics.
- int32_t mGameMode = 0;
+ GameMode mGameMode = GameMode::Unsupported;
};
/*
@@ -281,7 +289,7 @@
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) = 0;
+ GameMode) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -441,7 +449,7 @@
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) override;
+ GameMode) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 671395f..4ada2b6 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -22,6 +22,8 @@
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
@@ -63,11 +65,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
struct TrackedListener {
sp<gui::IHdrLayerInfoListener> listener;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index da7ff71..6e9138c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -36,6 +36,7 @@
#include <cutils/compiler.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <gui/BufferItem.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
@@ -45,6 +46,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
@@ -87,11 +89,12 @@
std::atomic<int32_t> Layer::sSequence{1};
Layer::Layer(const LayerCreationArgs& args)
- : mFlinger(args.flinger),
- mName(args.name),
+ : sequence(args.sequence.value_or(sSequence++)),
+ mFlinger(args.flinger),
+ mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
mClientRef(args.client),
- mWindowType(
- static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
+ mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))),
+ mLayerCreationFlags(args.flags) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -99,15 +102,13 @@
if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
layerFlags |= layer_state_t::eLayerSkipScreenshot;
- mDrawingState.active_legacy.w = args.w;
- mDrawingState.active_legacy.h = args.h;
mDrawingState.flags = layerFlags;
mDrawingState.active_legacy.transform.set(0, 0);
mDrawingState.crop.makeInvalid();
mDrawingState.requestedCrop = mDrawingState.crop;
mDrawingState.z = 0;
mDrawingState.color.a = 1.0f;
- mDrawingState.layerStack = 0;
+ mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
mDrawingState.sequence = 0;
mDrawingState.requested_legacy = mDrawingState.active_legacy;
mDrawingState.width = UINT32_MAX;
@@ -185,12 +186,10 @@
}
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
+ uint32_t flags, LayerMetadata metadata)
: flinger(flinger),
client(std::move(client)),
name(std::move(name)),
- w(w),
- h(h),
flags(flags),
metadata(std::move(metadata)) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -207,7 +206,8 @@
* Layer. So, the implementation is done in BufferLayer. When called on a
* EffectLayer object, it's essentially a NOP.
*/
-void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
+void Layer::onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> /*futureRenderEngineResult*/) {}
void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
if (mDrawingState.zOrderRelativeOf == nullptr) {
@@ -393,7 +393,6 @@
void Layer::prepareBasicGeometryCompositionState() {
const auto& drawingState{getDrawingState()};
- const uint32_t layerStack = getLayerStack();
const auto alpha = static_cast<float>(getAlpha());
const bool opaque = isOpaque(drawingState);
const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
@@ -405,9 +404,7 @@
}
auto* compositionState = editCompositionState();
- compositionState->layerStackId =
- (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
- compositionState->internalOnly = getPrimaryDisplayOnly();
+ compositionState->outputFilter = getOutputFilter();
compositionState->isVisible = isVisible();
compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
compositionState->shadowRadius = mEffectiveShadowRadius;
@@ -422,25 +419,13 @@
compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
compositionState->alpha = alpha;
+ compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+ compositionState->blurRegions = drawingState.blurRegions;
compositionState->stretchEffect = getStretchEffect();
}
void Layer::prepareGeometryCompositionState() {
const auto& drawingState{getDrawingState()};
-
- int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
- int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
- sp<Layer> parent = mDrawingParent.promote();
- if (parent.get()) {
- auto& parentState = parent->getDrawingState();
- const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
- const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
- if (parentType > 0 && parentAppId > 0) {
- type = parentType;
- appId = parentAppId;
- }
- }
-
auto* compositionState = editCompositionState();
compositionState->geomBufferSize = getBufferSize(drawingState);
@@ -500,6 +485,11 @@
// If there are no visible region changes, we still need to update blur parameters.
compositionState->blurRegions = drawingState.blurRegions;
compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+
+ // Layer framerate is used in caching decisions.
+ // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
+ // LayerFECompositionState where it would be visible to Flattener.
+ compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
}
void Layer::prepareCursorCompositionState() {
@@ -596,6 +586,8 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
+
+ layerSettings.whitePointNits = targetSettings.whitePointNits;
switch (targetSettings.blurSetting) {
case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
@@ -658,15 +650,16 @@
return {*layerSettings};
}
-Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+ const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (outputLayer == nullptr) {
- return Hwc2::IComposerClient::Composition::INVALID;
+ return aidl::android::hardware::graphics::composer3::Composition::INVALID;
}
if (outputLayer->getState().hwc) {
return (*outputLayer->getState().hwc).hwcCompositionType;
} else {
- return Hwc2::IComposerClient::Composition::CLIENT;
+ return aidl::android::hardware::graphics::composer3::Composition::CLIENT;
}
}
@@ -731,14 +724,14 @@
mDrawingState.bufferlessSurfaceFramesTX.clear();
}
-uint32_t Layer::getTransactionFlags(uint32_t flags) {
- auto ret = mTransactionFlags & flags;
- mTransactionFlags &= ~flags;
- return ret;
+uint32_t Layer::clearTransactionFlags(uint32_t mask) {
+ const auto flags = mTransactionFlags & mask;
+ mTransactionFlags &= ~mask;
+ return flags;
}
-uint32_t Layer::setTransactionFlags(uint32_t flags) {
- return mTransactionFlags |= flags;
+void Layer::setTransactionFlags(uint32_t mask) {
+ mTransactionFlags |= mask;
}
bool Layer::setPosition(float x, float y) {
@@ -903,7 +896,7 @@
uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
std::string name = mName + "BackgroundColorLayer";
mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags,
+ LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags,
LayerMetadata()));
// add to child list
@@ -950,7 +943,6 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
-
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
ui::Transform t;
@@ -1016,7 +1008,7 @@
return true;
}
-bool Layer::setLayerStack(uint32_t layerStack) {
+bool Layer::setLayerStack(ui::LayerStack layerStack) {
if (mDrawingState.layerStack == layerStack) return false;
mDrawingState.sequence++;
mDrawingState.layerStack = layerStack;
@@ -1063,12 +1055,11 @@
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
-uint32_t Layer::getLayerStack() const {
- auto p = mDrawingParent.promote();
- if (p == nullptr) {
- return getDrawingState().layerStack;
+ui::LayerStack Layer::getLayerStack() const {
+ if (const auto parent = mDrawingParent.promote()) {
+ return parent->getLayerStack();
}
- return p->getLayerStack();
+ return getDrawingState().layerStack;
}
bool Layer::setShadowRadius(float shadowRadius) {
@@ -1149,7 +1140,7 @@
if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote &&
childrenHaveFrameRate) {
*transactionNeeded |=
- setFrameRateForLayerTree(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote));
+ setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote));
}
// We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for
@@ -1183,9 +1174,6 @@
}
bool Layer::setFrameRate(FrameRate frameRate) {
- if (!mFlinger->useFrameRateApi) {
- return false;
- }
if (mDrawingState.frameRate == frameRate) {
return false;
}
@@ -1316,8 +1304,8 @@
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
- LayerHistory::LayerUpdateType::SetFrameRate);
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
return true;
}
@@ -1382,7 +1370,7 @@
info.mVisibleRegion = getVisibleRegion(display);
info.mSurfaceDamageRegion = surfaceDamageRegion;
- info.mLayerStack = getLayerStack();
+ info.mLayerStack = getLayerStack().id;
info.mX = ds.transform.tx();
info.mY = ds.transform.ty();
info.mZ = ds.z;
@@ -1434,19 +1422,6 @@
result.append("\n");
}
-std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
- switch (compatibility) {
- case FrameRateCompatibility::Default:
- return "Default";
- case FrameRateCompatibility::ExactOrMultiple:
- return "ExactOrMultiple";
- case FrameRateCompatibility::NoVote:
- return "NoVote";
- case FrameRateCompatibility::Exact:
- return "Exact";
- }
-}
-
void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (!outputLayer) {
@@ -1485,8 +1460,8 @@
const auto frameRate = getFrameRateForLayerTree();
if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
- frameRateCompatibilityString(frameRate.type).c_str(),
- toString(frameRate.seamlessness).c_str());
+ ftl::enum_string(frameRate.type).c_str(),
+ ftl::enum_string(frameRate.seamlessness).c_str());
} else {
result.append(41, ' ');
}
@@ -1569,11 +1544,10 @@
return count;
}
-void Layer::setGameModeForTree(int parentGameMode) {
- int gameMode = parentGameMode;
- auto& currentState = getDrawingState();
+void Layer::setGameModeForTree(GameMode gameMode) {
+ const auto& currentState = getDrawingState();
if (currentState.metadata.has(METADATA_GAME_MODE)) {
- gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0);
+ gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0));
}
setGameMode(gameMode);
for (const sp<Layer>& child : mCurrentChildren) {
@@ -1599,7 +1573,7 @@
const auto removeResult = mCurrentChildren.remove(layer);
updateTreeHasFrameRateVote();
- layer->setGameModeForTree(0);
+ layer->setGameModeForTree(GameMode::Unsupported);
layer->updateTreeHasFrameRateVote();
return removeResult;
@@ -2028,10 +2002,10 @@
writeToProtoDrawingState(layerProto, traceFlags, display);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
- if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
// Only populate for the primary display.
if (display) {
- const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
+ const auto compositionType = getCompositionType(*display);
layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
}
}
@@ -2046,43 +2020,38 @@
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
const DisplayDevice* display) {
const ui::Transform transform = getTransform();
+ auto buffer = getBuffer();
+ if (buffer != nullptr) {
+ LayerProtoHelper::writeToProto(buffer,
+ [&]() { return layerInfo->mutable_active_buffer(); });
+ LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
+ layerInfo->mutable_buffer_transform());
+ }
+ layerInfo->set_invalidate(contentDirty);
+ layerInfo->set_is_protected(isProtected());
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
+ layerInfo->set_queued_frames(getQueuedFrameCount());
+ layerInfo->set_refresh_pending(isBufferLatched());
+ layerInfo->set_curr_frame(mCurrentFrameNumber);
+ layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
- if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
+ layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
+ layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
+ layerInfo->set_is_trusted_overlay(isTrustedOverlay());
+ LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
+ LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+ LayerProtoHelper::writeToProto(getVisibleRegion(display),
+ [&]() { return layerInfo->mutable_visible_region(); });
+ }
+ LayerProtoHelper::writeToProto(surfaceDamageRegion,
+ [&]() { return layerInfo->mutable_damage_region(); });
- auto buffer = getBuffer();
- if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(buffer,
- [&]() { return layerInfo->mutable_active_buffer(); });
- LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
- layerInfo->mutable_buffer_transform());
- }
- layerInfo->set_invalidate(contentDirty);
- layerInfo->set_is_protected(isProtected());
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
- layerInfo->set_queued_frames(getQueuedFrameCount());
- layerInfo->set_refresh_pending(isBufferLatched());
- layerInfo->set_curr_frame(mCurrentFrameNumber);
- layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
-
- layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
- layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
- layerInfo->set_is_trusted_overlay(isTrustedOverlay());
- LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
- LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
- [&]() { return layerInfo->mutable_position(); });
- LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
- if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
- LayerProtoHelper::writeToProto(getVisibleRegion(display),
- [&]() { return layerInfo->mutable_visible_region(); });
- }
- LayerProtoHelper::writeToProto(surfaceDamageRegion,
- [&]() { return layerInfo->mutable_damage_region(); });
-
- if (hasColorTransform()) {
- LayerProtoHelper::writeToProto(getColorTransform(),
- layerInfo->mutable_color_transform());
- }
+ if (hasColorTransform()) {
+ LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform());
}
LayerProtoHelper::writeToProto(mSourceBounds,
@@ -2102,73 +2071,69 @@
ui::Transform requestedTransform = state.transform;
- if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
- layerInfo->set_id(sequence);
- layerInfo->set_name(getName().c_str());
- layerInfo->set_type(getType());
+ layerInfo->set_id(sequence);
+ layerInfo->set_name(getName().c_str());
+ layerInfo->set_type(getType());
- for (const auto& child : children) {
- layerInfo->add_children(child->sequence);
- }
-
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- layerInfo->add_relatives(strongRelative->sequence);
- }
- }
-
- LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
- [&]() { return layerInfo->mutable_transparent_region(); });
-
- layerInfo->set_layer_stack(getLayerStack());
- layerInfo->set_z(state.z);
-
- LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(),
- [&]() {
- return layerInfo->mutable_requested_position();
- });
-
- LayerProtoHelper::writeSizeToProto(state.width, state.height,
- [&]() { return layerInfo->mutable_size(); });
-
- LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
-
- layerInfo->set_is_opaque(isOpaque(state));
-
-
- layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
- LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
- LayerProtoHelper::writeToProto(state.color,
- [&]() { return layerInfo->mutable_requested_color(); });
- layerInfo->set_flags(state.flags);
-
- LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
- layerInfo->mutable_requested_transform());
-
- auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent != nullptr) {
- layerInfo->set_parent(parent->sequence);
- } else {
- layerInfo->set_parent(-1);
- }
-
- auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
- } else {
- layerInfo->set_z_order_relative_of(-1);
- }
-
- layerInfo->set_is_relative_of(state.isRelativeOf);
-
- layerInfo->set_owner_uid(mOwnerUid);
+ for (const auto& child : children) {
+ layerInfo->add_children(child->sequence);
}
- if (traceFlags & SurfaceTracing::TRACE_INPUT) {
+ for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+ sp<Layer> strongRelative = weakRelative.promote();
+ if (strongRelative != nullptr) {
+ layerInfo->add_relatives(strongRelative->sequence);
+ }
+ }
+
+ LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+ [&]() { return layerInfo->mutable_transparent_region(); });
+
+ layerInfo->set_layer_stack(getLayerStack().id);
+ layerInfo->set_z(state.z);
+
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+ return layerInfo->mutable_requested_position();
+ });
+
+ LayerProtoHelper::writeSizeToProto(state.width, state.height,
+ [&]() { return layerInfo->mutable_size(); });
+
+ LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
+
+ layerInfo->set_is_opaque(isOpaque(state));
+
+ layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+ LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
+ LayerProtoHelper::writeToProto(state.color,
+ [&]() { return layerInfo->mutable_requested_color(); });
+ layerInfo->set_flags(state.flags);
+
+ LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+ layerInfo->mutable_requested_transform());
+
+ auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+ if (parent != nullptr) {
+ layerInfo->set_parent(parent->sequence);
+ } else {
+ layerInfo->set_parent(-1);
+ }
+
+ auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
+ if (zOrderRelativeOf != nullptr) {
+ layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+ } else {
+ layerInfo->set_z_order_relative_of(-1);
+ }
+
+ layerInfo->set_is_relative_of(state.isRelativeOf);
+
+ layerInfo->set_owner_uid(mOwnerUid);
+
+ if (traceFlags & LayerTracing::TRACE_INPUT) {
WindowInfo info;
if (useDrawing) {
- info = fillInputInfo({nullptr});
+ info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
} else {
info = state.inputInfo;
}
@@ -2177,7 +2142,7 @@
[&]() { return layerInfo->mutable_input_window_info(); });
}
- if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
+ if (traceFlags & LayerTracing::TRACE_EXTRA) {
auto protoMap = layerInfo->mutable_metadata();
for (const auto& entry : state.metadata.mMap) {
(*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
@@ -2197,7 +2162,7 @@
return getCroppedBufferSize(getDrawingState());
}
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& displayTransform) {
// Transform layer size to screen space and inset it by surface insets.
// If this is a portal window, set the touchableRegion to the layerBounds.
Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
@@ -2219,13 +2184,13 @@
return;
}
- ui::Transform layerToDisplay = getInputTransform();
+ const ui::Transform layerTransform = getInputTransform();
// Transform that takes window coordinates to non-rotated display coordinates
- ui::Transform t = toNonRotatedDisplay * layerToDisplay;
+ ui::Transform t = displayTransform * layerTransform;
int32_t xSurfaceInset = info.surfaceInset;
int32_t ySurfaceInset = info.surfaceInset;
- // Bring screenBounds into non-rotated space
- Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds});
+ // Bring screenBounds into non-unrotated space
+ Rect screenBounds = displayTransform.transform(Rect{mScreenBounds});
const float xScale = t.getScaleX();
const float yScale = t.getScaleY();
@@ -2372,47 +2337,21 @@
}
}
-WindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) {
+WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) {
if (!hasInputInfo()) {
mDrawingState.inputInfo.name = getName();
mDrawingState.inputInfo.ownerUid = mOwnerUid;
mDrawingState.inputInfo.ownerPid = mOwnerPid;
mDrawingState.inputInfo.inputFeatures = WindowInfo::Feature::NO_INPUT_CHANNEL;
mDrawingState.inputInfo.flags = WindowInfo::Flag::NOT_TOUCH_MODAL;
- mDrawingState.inputInfo.displayId = getLayerStack();
+ mDrawingState.inputInfo.displayId = getLayerStack().id;
}
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
- info.displayId = getLayerStack();
+ info.displayId = getLayerStack().id;
- // Transform that goes from "logical(rotated)" display to the non-rotated display.
- ui::Transform toNonRotatedDisplay;
- if (display) {
- // The physical orientation is set when the orientation of the display panel is different
- // than the default orientation of the device. We do not need to expose the physical
- // orientation of the panel outside of SurfaceFlinger.
- const ui::Rotation inversePhysicalOrientation =
- ui::ROTATION_0 - display->getPhysicalOrientation();
- auto width = display->getWidth();
- auto height = display->getHeight();
- if (inversePhysicalOrientation == ui::ROTATION_90 ||
- inversePhysicalOrientation == ui::ROTATION_270) {
- std::swap(width, height);
- }
- const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation);
- const ui::Transform undoPhysicalOrientation(rotationFlags, width, height);
- toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform();
-
- // Send the inverse of the display orientation so that input can transform points back to
- // the rotated display space.
- const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation();
- info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation);
-
- info.displayWidth = width;
- info.displayHeight = height;
- }
- fillInputFrameInfo(info, toNonRotatedDisplay);
+ fillInputFrameInfo(info, displayTransform);
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
@@ -2427,17 +2366,19 @@
fillTouchOcclusionMode(info);
handleDropInputMode(info);
+ // If the window will be blacked out on a display because the display does not have the secure
+ // flag and the layer has the secure flag set, then drop input.
+ if (!displayIsSecure && isSecure()) {
+ info.inputFeatures |= WindowInfo::Feature::DROP_INPUT;
+ }
+
auto cropLayer = mDrawingState.touchableRegionCrop.promote();
if (info.replaceTouchableRegionWithCrop) {
- if (cropLayer == nullptr) {
- info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds}));
- } else {
- info.touchableRegion =
- Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
- }
+ const Rect bounds(cropLayer ? cropLayer->mScreenBounds : mScreenBounds);
+ info.touchableRegion = Region(displayTransform.transform(bounds));
} else if (cropLayer != nullptr) {
info.touchableRegion = info.touchableRegion.intersect(
- toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
+ displayTransform.transform(Rect{cropLayer->mScreenBounds}));
}
// Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2448,9 +2389,8 @@
// If the layer is a clone, we need to crop the input region to cloned root to prevent
// touches from going outside the cloned area.
if (isClone()) {
- sp<Layer> clonedRoot = getClonedRoot();
- if (clonedRoot != nullptr) {
- Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds});
+ if (const sp<Layer> clonedRoot = getClonedRoot()) {
+ const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds});
info.touchableRegion = info.touchableRegion.intersect(rect);
}
}
@@ -2645,14 +2585,14 @@
}
}
-bool Layer::getPrimaryDisplayOnly() const {
+bool Layer::isInternalDisplayOverlay() const {
const State& s(mDrawingState);
if (s.flags & layer_state_t::eLayerSkipScreenshot) {
return true;
}
sp<Layer> parent = mDrawingParent.promote();
- return parent == nullptr ? false : parent->getPrimaryDisplayOnly();
+ return parent && parent->isInternalDisplayOverlay();
}
void Layer::setClonedChild(const sp<Layer>& clonedChild) {
@@ -2696,12 +2636,11 @@
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
- return stream << "{rate=" << rate.rate
- << " type=" << Layer::frameRateCompatibilityString(rate.type)
- << " seamlessness=" << toString(rate.seamlessness) << "}";
+ return stream << "{rate=" << rate.rate << " type=" << ftl::enum_string(rate.type)
+ << " seamlessness=" << ftl::enum_string(rate.seamlessness) << '}';
}
-}; // namespace android
+} // namespace android
#if defined(__gl_h_)
#error "don't include gl/gl.h in this file"
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index c1af468..31cdf0b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,7 +18,6 @@
#pragma once
#include <android/gui/DropInputMode.h>
-#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
@@ -39,6 +38,10 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <compositionengine/LayerFE.h>
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
#include <chrono>
#include <cstdint>
#include <list>
@@ -49,15 +52,13 @@
#include "ClientCache.h"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "RenderArea.h"
#include "Scheduler/LayerInfo.h"
-#include "Scheduler/Seamlessness.h"
#include "SurfaceFlinger.h"
-#include "SurfaceTracing.h"
+#include "Tracing/LayerTracing.h"
#include "TransactionCallbackInvoker.h"
using namespace android::surfaceflinger;
@@ -85,20 +86,18 @@
} // namespace frametimeline
struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
- uint32_t flags, LayerMetadata);
+ LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
SurfaceFlinger* flinger;
const sp<Client> client;
std::string name;
- uint32_t w;
- uint32_t h;
uint32_t flags;
LayerMetadata metadata;
pid_t callingPid;
uid_t callingUid;
uint32_t textureName;
+ std::optional<uint32_t> sequence = std::nullopt;
};
class Layer : public virtual RefBase, compositionengine::LayerFE {
@@ -152,12 +151,7 @@
Geometry requested_legacy;
int32_t z;
- // The identifier of the layer stack this layer belongs to. A layer can
- // only be associated to a single layer stack. A layer stack is a
- // z-ordered group of layers which can be associated to one or more
- // displays. Using the same layer stack on different displays is a way
- // to achieve mirroring.
- uint32_t layerStack;
+ ui::LayerStack layerStack;
uint32_t flags;
uint8_t reserved[2];
@@ -285,6 +279,8 @@
sp<IBinder> releaseBufferEndpoint;
gui::DropInputMode dropInputMode;
+
+ bool autoRefresh = false;
};
/*
@@ -332,7 +328,6 @@
static bool isLayerFocusedBasedOnPriority(int32_t priority);
static void miniDumpHeader(std::string& result);
- static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
// Provide unique string for each class type in the Layer hierarchy
virtual const char* getType() const = 0;
@@ -408,8 +403,8 @@
virtual bool setTransparentRegionHint(const Region& transparent);
virtual bool setTrustedOverlay(bool);
virtual bool setFlags(uint32_t flags, uint32_t mask);
- virtual bool setLayerStack(uint32_t layerStack);
- virtual uint32_t getLayerStack() const;
+ virtual bool setLayerStack(ui::LayerStack);
+ virtual ui::LayerStack getLayerStack() const;
virtual bool setMetadata(const LayerMetadata& data);
virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -421,17 +416,11 @@
// Used only to set BufferStateLayer state
virtual bool setTransform(uint32_t /*transform*/) { return false; };
virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
- virtual bool setBuffer(const std::shared_ptr<renderengine::ExternalTexture>& /*buffer*/,
- const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/,
- nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */,
- std::optional<nsecs_t> /* dequeueTime */,
- const FrameTimelineInfo& /*info*/,
- const sp<ITransactionCompletedListener>& /* releaseBufferListener */,
- const sp<IBinder>& /* releaseBufferEndpoint */) {
+ virtual bool setBuffer(const BufferData&, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
+ bool /*isAutoTimestamp*/, std::optional<nsecs_t> /* dequeueTime */,
+ const FrameTimelineInfo& /*info*/) {
return false;
};
- virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; };
virtual bool setDataspace(ui::Dataspace /*dataspace*/) { return false; };
virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) { return false; };
virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) { return false; };
@@ -532,18 +521,14 @@
virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
- virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; }
-
/*
* called after composition.
* returns true if the layer latched a new buffer this frame.
*/
- virtual bool onPostComposition(const DisplayDevice*,
+ virtual void onPostComposition(const DisplayDevice*,
const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&) {
- return false;
- }
+ const CompositorTiming&) {}
// If a buffer was replaced this frame, release the former buffer
virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
@@ -604,10 +589,6 @@
}
virtual FrameRate getFrameRateForLayerTree() const;
- virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) {
- return {};
- }
-
virtual bool getTransformToDisplayInverse() const { return false; }
// Returns how rounded corners should be drawn for this layer.
@@ -635,7 +616,13 @@
void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
- void onLayerDisplayed(const sp<Fence>& releaseFence) override;
+ void onLayerDisplayed(
+ std::shared_future<renderengine::RenderEngineResult> futureRenderEngineResult) override;
+
+ void setWasClientComposed(const sp<Fence>& fence) override {
+ mLastClientCompositionFence = fence;
+ }
+
const char* getDebugName() const override;
bool setShadowRadius(float shadowRadius);
@@ -648,13 +635,12 @@
bool isLegacyDataSpace() const;
uint32_t getTransactionFlags() const { return mTransactionFlags; }
- uint32_t getTransactionFlags(uint32_t flags);
- uint32_t setTransactionFlags(uint32_t flags);
- // Deprecated, please use compositionengine::Output::belongsInOutput()
- // instead.
- // TODO(lpique): Move the remaining callers (screencap) to the new function.
- bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; }
+ // Sets the masked bits.
+ void setTransactionFlags(uint32_t mask);
+
+ // Clears and returns the masked bits.
+ uint32_t clearTransactionFlags(uint32_t mask);
FloatRect getBounds(const Region& activeTransparentRegion) const;
FloatRect getBounds() const;
@@ -687,6 +673,14 @@
*/
bool isHiddenByPolicy() const;
+ // True if the layer should be skipped in screenshots, screen recordings,
+ // and mirroring to external or virtual displays.
+ bool isInternalDisplayOverlay() const;
+
+ ui::LayerFilter getOutputFilter() const {
+ return {getLayerStack(), isInternalDisplayOverlay()};
+ }
+
bool isRemovedFromCurrentState() const;
LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*);
@@ -699,12 +693,10 @@
// external mStateLock. If writing drawing state, this function should be called on the
// main or tracing thread.
void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
+ uint32_t traceFlags = LayerTracing::TRACE_ALL);
gui::WindowInfo::Type getWindowType() const { return mWindowType; }
- bool getPrimaryDisplayOnly() const;
-
void updateMirrorInfo();
/*
@@ -854,18 +846,19 @@
bool getPremultipledAlpha() const;
void setInputInfo(const gui::WindowInfo& info);
- gui::WindowInfo fillInputInfo(const sp<DisplayDevice>& display);
+ gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure);
+
/**
* Returns whether this layer has an explicitly set input-info.
*/
bool hasInputInfo() const;
- // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied
- // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with
- // its children) that has the metadata set will use the gameMode from the metadata.
- void setGameModeForTree(int32_t parentGameMode);
- void setGameMode(int32_t gameMode) { mGameMode = gameMode; };
- int32_t getGameMode() const { return mGameMode; }
+ // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this
+ // GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
+ void setGameModeForTree(GameMode);
+
+ void setGameMode(GameMode gameMode) { mGameMode = gameMode; }
+ GameMode getGameMode() const { return mGameMode; }
virtual uid_t getOwnerUid() const { return mOwnerUid; }
@@ -886,7 +879,7 @@
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
- int32_t sequence{sSequence++};
+ const int32_t sequence;
bool mPendingHWCDestroy{false};
@@ -1036,13 +1029,16 @@
mutable bool mDrawingStateModified = false;
+ sp<Fence> mLastClientCompositionFence;
+ bool mLastClientCompositionDisplayed = false;
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
// Returns true if the layer can draw shadows on its border.
virtual bool canDrawShadows() const { return true; }
- Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+ const DisplayDevice&) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
@@ -1078,8 +1074,8 @@
// hasInputInfo() or no-op if no such parent is found.
void fillTouchOcclusionMode(gui::WindowInfo& info);
- // Fills in the frame and transform info for the gui::WindowInfo
- void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay);
+ // Fills in the frame and transform info for the gui::WindowInfo.
+ void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& displayTransform);
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling, which is
@@ -1115,14 +1111,15 @@
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
- // Game mode for the layer. Set by WindowManagerShell, game mode is used in
- // metrics(SurfaceFlingerStats).
- int32_t mGameMode = 0;
+ // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
+ GameMode mGameMode = GameMode::Unsupported;
// A list of regions on this layer that should have blurs.
const std::vector<BlurRegion> getBlurRegions() const;
bool mIsAtRoot = false;
+
+ uint32_t mLayerCreationFlags;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 1062126..ee23561 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -53,28 +53,52 @@
return;
}
+ writeToProto(region, getRegionProto());
+}
+
+void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+ if (region.isEmpty()) {
+ return;
+ }
+
Region::const_iterator head = region.begin();
Region::const_iterator const tail = region.end();
// Use a lambda do avoid writing the object header when the object is empty
- RegionProto* regionProto = getRegionProto();
while (head != tail) {
- std::function<RectProto*()> getProtoRect = [&]() { return regionProto->add_rect(); };
- writeToProto(*head, getProtoRect);
+ writeToProto(*head, regionProto->add_rect());
head++;
}
}
+void LayerProtoHelper::readFromProto(const RegionProto& regionProto, Region& outRegion) {
+ for (int i = 0; i < regionProto.rect_size(); i++) {
+ Rect rect;
+ readFromProto(regionProto.rect(i), rect);
+ outRegion.orSelf(rect);
+ }
+}
+
void LayerProtoHelper::writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto) {
if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
// Use a lambda do avoid writing the object header when the object is empty
- RectProto* rectProto = getRectProto();
- rectProto->set_left(rect.left);
- rectProto->set_top(rect.top);
- rectProto->set_bottom(rect.bottom);
- rectProto->set_right(rect.right);
+ writeToProto(rect, getRectProto());
}
}
+void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
+ rectProto->set_left(rect.left);
+ rectProto->set_top(rect.top);
+ rectProto->set_bottom(rect.bottom);
+ rectProto->set_right(rect.right);
+}
+
+void LayerProtoHelper::readFromProto(const RectProto& proto, Rect& outRect) {
+ outRect.left = proto.left();
+ outRect.top = proto.top();
+ outRect.bottom = proto.bottom();
+ outRect.right = proto.right();
+}
+
void LayerProtoHelper::writeToProto(const FloatRect& rect,
std::function<FloatRectProto*()> getFloatRectProto) {
if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) {
@@ -189,6 +213,39 @@
}
}
+void LayerProtoHelper::readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix) {
+ for (int i = 0; i < mat4::ROW_SIZE; i++) {
+ for (int j = 0; j < mat4::COL_SIZE; j++) {
+ matrix[i][j] = colorTransformProto.val(i * mat4::COL_SIZE + j);
+ }
+ }
+}
+
+void LayerProtoHelper::writeToProto(const android::BlurRegion region, BlurRegion* proto) {
+ proto->set_blur_radius(region.blurRadius);
+ proto->set_corner_radius_tl(region.cornerRadiusTL);
+ proto->set_corner_radius_tr(region.cornerRadiusTR);
+ proto->set_corner_radius_bl(region.cornerRadiusBL);
+ proto->set_corner_radius_br(region.cornerRadiusBR);
+ proto->set_alpha(region.alpha);
+ proto->set_left(region.left);
+ proto->set_top(region.top);
+ proto->set_right(region.right);
+ proto->set_bottom(region.bottom);
+}
+
+void LayerProtoHelper::readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion) {
+ outRegion.blurRadius = proto.blur_radius();
+ outRegion.cornerRadiusTL = proto.corner_radius_tl();
+ outRegion.cornerRadiusTR = proto.corner_radius_tr();
+ outRegion.cornerRadiusBL = proto.corner_radius_bl();
+ outRegion.cornerRadiusBR = proto.corner_radius_br();
+ outRegion.alpha = proto.alpha();
+ outRegion.left = proto.left();
+ outRegion.top = proto.top();
+ outRegion.right = proto.right();
+ outRegion.bottom = proto.bottom();
+}
} // namespace surfaceflinger
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 36e0647..249ec42 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -19,6 +19,7 @@
#include <Layer.h>
#include <gui/WindowInfo.h>
#include <math/vec4.h>
+#include <ui/BlurRegion.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -33,9 +34,13 @@
static void writeSizeToProto(const uint32_t w, const uint32_t h,
std::function<SizeProto*()> getSizeProto);
static void writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto);
+ static void writeToProto(const Rect& rect, RectProto* rectProto);
+ static void readFromProto(const RectProto& proto, Rect& outRect);
static void writeToProto(const FloatRect& rect,
std::function<FloatRectProto*()> getFloatRectProto);
static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto);
+ static void writeToProto(const Region& region, RegionProto* regionProto);
+ static void readFromProto(const RegionProto& regionProto, Region& outRegion);
static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto);
// This writeToProto for transform is incorrect, but due to backwards compatibility, we can't
// update Layers to use it. Use writeTransformToProto for any new transform proto data.
@@ -49,6 +54,9 @@
const wp<Layer>& touchableRegionBounds,
std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
+ static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix);
+ static void writeToProto(const android::BlurRegion region, BlurRegion*);
+ static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion);
};
} // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 11fe6d0..a1e1455 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -112,12 +112,10 @@
}
drawLayers();
} else {
- uint32_t w = static_cast<uint32_t>(getWidth());
- uint32_t h = static_cast<uint32_t>(getHeight());
// In the "childrenOnly" case we reparent the children to a screenshot
// layer which has no properties set and which does not draw.
sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
- {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+ {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
drawLayers();
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index aee820a..f52e60d 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -45,8 +45,8 @@
const auto& lState = l->getDrawingState();
const auto& rState = r->getDrawingState();
- uint32_t ls = lState.layerStack;
- uint32_t rs = rState.layerStack;
+ const auto ls = lState.layerStack;
+ const auto rs = rState.layerStack;
if (ls != rs)
return (ls > rs) ? 1 : -1;
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 6b2d745..df76f50 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -33,13 +33,7 @@
mFlinger(flinger),
mLayer(layer) {}
-MonitoredProducer::~MonitoredProducer() {
- // Remove ourselves from SurfaceFlinger's list. We do this asynchronously
- // because we don't know where this destructor is called from. It could be
- // called with the mStateLock held, leading to a dead-lock (it actually
- // happens).
- mFlinger->removeGraphicBufferProducerAsync(onAsBinder());
-}
+MonitoredProducer::~MonitoredProducer() {}
status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
return mProducer->requestBuffer(slot, buf);
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index bc32a1d..81c1566 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -25,92 +25,77 @@
#include "Client.h"
#include "Layer.h"
+#include <SkBlendMode.h>
+#include <SkPaint.h>
+#include <SkRect.h>
+#include <SkSurface.h>
#include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#undef LOG_TAG
#define LOG_TAG "RefreshRateOverlay"
namespace android {
-void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
- for (int32_t j = r.top; j < r.bottom; j++) {
- if (j >= buffer->getHeight()) {
- break;
- }
-
- for (int32_t i = r.left; i < r.right; i++) {
- if (i >= buffer->getWidth()) {
- break;
- }
-
- uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
- iter[0] = uint8_t(color.r * 255);
- iter[1] = uint8_t(color.g * 255);
- iter[2] = uint8_t(color.b * 255);
- iter[3] = uint8_t(color.a * 255);
- }
- }
-}
-
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
- const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
- const Rect rect = [&]() {
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor& color,
+ SkCanvas& canvas) {
+ const SkRect rect = [&]() {
switch (segment) {
case Segment::Upper:
- return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+ return SkRect::MakeLTRB(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
case Segment::UpperLeft:
- return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+ return SkRect::MakeLTRB(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
case Segment::UpperRight:
- return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
- DIGIT_HEIGHT / 2);
+ return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT / 2);
case Segment::Middle:
- return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
- DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+ return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2,
+ left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
case Segment::LowerLeft:
- return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+ return SkRect::MakeLTRB(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
case Segment::LowerRight:
- return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
- DIGIT_HEIGHT);
- case Segment::Buttom:
- return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+ return SkRect::MakeLTRB(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2,
+ left + DIGIT_WIDTH, DIGIT_HEIGHT);
+ case Segment::Bottom:
+ return SkRect::MakeLTRB(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT);
}
}();
- drawRect(rect, color, buffer, pixels);
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas.drawRect(rect, paint);
}
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor& color,
+ SkCanvas& canvas) {
if (digit < 0 || digit > 9) return;
if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
digit == 8 || digit == 9)
- drawSegment(Segment::Upper, left, color, buffer, pixels);
+ drawSegment(Segment::Upper, left, color, canvas);
if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
- drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ drawSegment(Segment::UpperLeft, left, color, canvas);
if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
digit == 8 || digit == 9)
- drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ drawSegment(Segment::UpperRight, left, color, canvas);
if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
digit == 9)
- drawSegment(Segment::Middle, left, color, buffer, pixels);
+ drawSegment(Segment::Middle, left, color, canvas);
if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
- drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ drawSegment(Segment::LowerLeft, left, color, canvas);
if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
digit == 7 || digit == 8 || digit == 9)
- drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ drawSegment(Segment::LowerRight, left, color, canvas);
if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
digit == 9)
- drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ drawSegment(Segment::Bottom, left, color, canvas);
}
-std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
- int number, const half4& color, bool showSpinner) {
+std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::draw(
+ int number, SkColor& color, ui::Transform::RotationFlags rotation, bool showSpinner) {
if (number < 0 || number > 1000) return {};
const auto hundreds = number / 100;
@@ -120,55 +105,76 @@
std::vector<sp<GraphicBuffer>> buffers;
const auto loopCount = showSpinner ? 6 : 1;
for (int i = 0; i < loopCount; i++) {
+ // Pre-rotate the buffer before it reaches SurfaceFlinger.
+ SkMatrix canvasTransform = SkMatrix();
+ auto [bufferWidth, bufferHeight] = [&] {
+ switch (rotation) {
+ case ui::Transform::ROT_90:
+ canvasTransform.setTranslate(BUFFER_HEIGHT, 0);
+ canvasTransform.preRotate(90);
+ return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+ case ui::Transform::ROT_270:
+ canvasTransform.setRotate(270, BUFFER_WIDTH / 2.0, BUFFER_WIDTH / 2.0);
+ return std::make_tuple(BUFFER_HEIGHT, BUFFER_WIDTH);
+ default:
+ return std::make_tuple(BUFFER_WIDTH, BUFFER_HEIGHT);
+ }
+ }();
sp<GraphicBuffer> buffer =
- new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ new GraphicBuffer(bufferWidth, bufferHeight, HAL_PIXEL_FORMAT_RGBA_8888, 1,
GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
GRALLOC_USAGE_HW_TEXTURE,
"RefreshRateOverlayBuffer");
const status_t bufferStatus = buffer->initCheck();
LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
bufferStatus);
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
- // Clear buffer content
- drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+ SkCanvas* canvas = surface->getCanvas();
+ canvas->setMatrix(canvasTransform);
+
int left = 0;
if (hundreds != 0) {
- drawDigit(hundreds, left, color, buffer, pixels);
+ drawDigit(hundreds, left, color, *canvas);
}
left += DIGIT_WIDTH + DIGIT_SPACE;
if (tens != 0) {
- drawDigit(tens, left, color, buffer, pixels);
+ drawDigit(tens, left, color, *canvas);
}
left += DIGIT_WIDTH + DIGIT_SPACE;
- drawDigit(ones, left, color, buffer, pixels);
+ drawDigit(ones, left, color, *canvas);
left += DIGIT_WIDTH + DIGIT_SPACE;
if (showSpinner) {
switch (i) {
case 0:
- drawSegment(Segment::Upper, left, color, buffer, pixels);
+ drawSegment(Segment::Upper, left, color, *canvas);
break;
case 1:
- drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ drawSegment(Segment::UpperRight, left, color, *canvas);
break;
case 2:
- drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ drawSegment(Segment::LowerRight, left, color, *canvas);
break;
case 3:
- drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ drawSegment(Segment::Bottom, left, color, *canvas);
break;
case 4:
- drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ drawSegment(Segment::LowerLeft, left, color, *canvas);
break;
case 5:
- drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ drawSegment(Segment::UpperLeft, left, color, *canvas);
break;
}
}
+ void* pixels = nullptr;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ const SkImageInfo& imageInfo = surface->imageInfo();
+ size_t dstRowBytes = buffer->getStride() * imageInfo.bytesPerPixel();
+ canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
buffer->unlock();
buffers.emplace_back(buffer);
}
@@ -186,35 +192,49 @@
}
bool RefreshRateOverlay::createLayer() {
- int32_t layerId;
- const status_t ret =
- mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
- SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
- &mIBinder, &mGbp, nullptr, &layerId);
- if (ret) {
+ mSurfaceControl =
+ SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), SevenSegmentDrawer::getWidth(),
+ SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState);
+
+ if (!mSurfaceControl) {
ALOGE("failed to create buffer state layer");
return false;
}
- mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
- mLayer->setIsAtRoot(true);
-
- // setting Layer's Z requires resorting layersSortedByZ
- ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer);
- if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
- mFlinger.mDrawingState.layersSortedByZ.removeAt(idx);
- mFlinger.mDrawingState.layersSortedByZ.add(mLayer);
- }
+ SurfaceComposerClient::Transaction()
+ .setFrameRate(mSurfaceControl, 0.0f,
+ static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote),
+ static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless))
+ .setLayer(mSurfaceControl, INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl, true)
+ .apply();
return true;
}
-const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
-RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
- if (mBufferCache.find(fps) == mBufferCache.end()) {
+const std::vector<sp<GraphicBuffer>>& RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
+ ui::Transform::RotationFlags transformHint =
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint());
+ // Tell SurfaceFlinger about the pre-rotation on the buffer.
+ const auto transform = [&] {
+ switch (transformHint) {
+ case ui::Transform::ROT_90:
+ return ui::Transform::ROT_270;
+ case ui::Transform::ROT_270:
+ return ui::Transform::ROT_90;
+ default:
+ return ui::Transform::ROT_0;
+ }
+ }();
+
+ SurfaceComposerClient::Transaction t;
+ t.setTransform(mSurfaceControl, transform);
+ t.apply();
+
+ if (mBufferCache.find(transformHint) == mBufferCache.end() ||
+ mBufferCache.at(transformHint).find(fps) == mBufferCache.at(transformHint).end()) {
// Ensure the range is > 0, so we don't divide by 0.
const auto rangeLength = std::max(1u, mHighFps - mLowFps);
// Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
@@ -222,25 +242,18 @@
fps = std::max(fps, mLowFps);
fps = std::min(fps, mHighFps);
const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
- half4 color;
- color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
- color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
- color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
- color.a = ALPHA;
- auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
- std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
- std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
- [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
- return std::make_shared<
- renderengine::ExternalTexture>(buffer,
- mFlinger.getRenderEngine(),
- renderengine::ExternalTexture::
- Usage::READABLE);
- });
- mBufferCache.emplace(fps, textures);
+ SkColor4f colorBase = SkColor4f::FromColor(HIGH_FPS_COLOR) * fpsScale;
+ SkColor4f lowFpsColor = SkColor4f::FromColor(LOW_FPS_COLOR) * (1 - fpsScale);
+ colorBase.fR = colorBase.fR + lowFpsColor.fR;
+ colorBase.fG = colorBase.fG + lowFpsColor.fG;
+ colorBase.fB = colorBase.fB + lowFpsColor.fB;
+ colorBase.fA = ALPHA;
+ SkColor color = colorBase.toSkColor();
+ auto buffers = SevenSegmentDrawer::draw(fps, color, transformHint, mShowSpinner);
+ mBufferCache[transformHint].emplace(fps, buffers);
}
- return mBufferCache[fps];
+ return mBufferCache[transformHint][fps];
}
void RefreshRateOverlay::setViewport(ui::Size viewport) {
@@ -250,44 +263,37 @@
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- layer_state_t::matrix22_t matrix;
- matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
- matrix.dtdx = 0;
- matrix.dtdy = 0;
- matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
- mLayer->setMatrix(matrix, true);
- mLayer->setPosition(frame.left, frame.top);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setMatrix(mSurfaceControl,
+ frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth()), 0, 0,
+ frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight()));
+ t.setPosition(mSurfaceControl, frame.left, frame.top);
+ t.apply();
}
-void RefreshRateOverlay::setLayerStack(uint32_t stack) {
- mLayer->setLayerStack(stack);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
+ SurfaceComposerClient::Transaction t;
+ t.setLayerStack(mSurfaceControl, stack);
+ t.apply();
}
-void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps.getIntValue();
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setBuffer(mSurfaceControl, buffer);
+ t.apply();
}
-void RefreshRateOverlay::onInvalidate() {
+void RefreshRateOverlay::animate() {
if (!mCurrentFps.has_value()) return;
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ SurfaceComposerClient::Transaction t;
+ t.setBuffer(mSurfaceControl, buffer);
+ t.apply();
}
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index f9baa89..381df37 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,15 +16,18 @@
#pragma once
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <unordered_map>
+
#include <math/vec4.h>
#include <renderengine/RenderEngine.h>
+#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Size.h>
#include <utils/StrongPointer.h>
-#include <unordered_map>
-
-#include "Fps.h"
+#include <scheduler/Fps.h>
namespace android {
@@ -34,33 +37,30 @@
class IGraphicBufferProducer;
class Layer;
class SurfaceFlinger;
+class SurfaceControl;
class RefreshRateOverlay {
public:
RefreshRateOverlay(SurfaceFlinger&, uint32_t lowFps, uint32_t highFps, bool showSpinner);
- void setLayerStack(uint32_t stack);
+ void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(const Fps&);
- void onInvalidate();
+ void changeRefreshRate(Fps);
+ void animate();
private:
class SevenSegmentDrawer {
public:
- static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color,
- bool showSpinner);
+ static std::vector<sp<GraphicBuffer>> draw(int number, SkColor& color,
+ ui::Transform::RotationFlags, bool showSpinner);
static uint32_t getHeight() { return BUFFER_HEIGHT; }
static uint32_t getWidth() { return BUFFER_WIDTH; }
private:
- enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+ enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
- static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
- uint8_t* pixels);
- static void drawSegment(Segment segment, int left, const half4& color,
- const sp<GraphicBuffer>& buffer, uint8_t* pixels);
- static void drawDigit(int digit, int left, const half4& color,
- const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+ static void drawSegment(Segment segment, int left, SkColor& color, SkCanvas& canvas);
+ static void drawDigit(int digit, int left, SkColor& color, SkCanvas& canvas);
static constexpr uint32_t DIGIT_HEIGHT = 100;
static constexpr uint32_t DIGIT_WIDTH = 64;
@@ -71,28 +71,30 @@
};
bool createLayer();
- const std::vector<std::shared_ptr<renderengine::ExternalTexture>>& getOrCreateBuffers(
- uint32_t fps);
+
+ const std::vector<sp<GraphicBuffer>>& getOrCreateBuffers(uint32_t fps);
SurfaceFlinger& mFlinger;
const sp<Client> mClient;
- sp<Layer> mLayer;
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- std::unordered_map<int, std::vector<std::shared_ptr<renderengine::ExternalTexture>>>
+ std::unordered_map<ui::Transform::RotationFlags,
+ std::unordered_map<int, std::vector<sp<GraphicBuffer>>>>
mBufferCache;
std::optional<int> mCurrentFps;
int mFrame = 0;
static constexpr float ALPHA = 0.8f;
- const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
- const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
+ const SkColor LOW_FPS_COLOR = SK_ColorRED;
+ const SkColor HIGH_FPS_COLOR = SK_ColorGREEN;
const bool mShowSpinner;
// Interpolate the colors between these values.
const uint32_t mLowFps;
const uint32_t mHighFps;
+
+ sp<SurfaceControl> mSurfaceControl;
};
} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index aa2fec5..da8c3e0 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -30,7 +30,6 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <cutils/properties.h>
#include <ftl/future.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Trace.h>
@@ -150,7 +149,7 @@
if (mSampleRequestTime.has_value()) {
ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
mSampleRequestTime.reset();
- mFlinger.scheduleRegionSamplingThread();
+ mFlinger.scheduleSample();
}
}
@@ -356,10 +355,13 @@
renderengine::ExternalTexture::Usage::WRITEABLE);
}
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
- true /* regionSampling */, false /* grayscale */, captureListener);
- ScreenCaptureResults captureResults = captureListener->waitForResults();
+ auto captureScreenResultFuture =
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ true /* regionSampling */, false /* grayscale */, nullptr);
+ auto& captureScreenResult = captureScreenResultFuture.get();
+ if (captureScreenResult.drawFence.ok()) {
+ sync_wait(captureScreenResult.drawFence.get(), -1);
+ }
std::vector<Descriptor> activeDescriptors;
for (const auto& descriptor : descriptors) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 2231853..686b4b1 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/thread_annotations.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <binder/IBinder.h>
#include <renderengine/ExternalTexture.h>
#include <ui/GraphicBuffer.h>
@@ -30,15 +31,17 @@
#include <unordered_map>
#include "Scheduler/OneShotTimer.h"
+#include "WpHash.h"
namespace android {
-class IRegionSamplingListener;
class Layer;
class Scheduler;
class SurfaceFlinger;
struct SamplingOffsetCallback;
+using gui::IRegionSamplingListener;
+
float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
uint32_t orientation, const Rect& area);
@@ -88,11 +91,6 @@
sp<IRegionSamplingListener> listener;
};
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
std::vector<float> sampleBuffer(
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 9a6c853..5fea521 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include "RenderArea.h"
namespace android {
@@ -33,6 +29,3 @@
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
new file mode 100644
index 0000000..409d098
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -0,0 +1,35 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_defaults {
+ name: "libscheduler_defaults",
+ defaults: ["surfaceflinger_defaults"],
+ cflags: [
+ "-DLOG_TAG=\"Scheduler\"",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ ],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+}
+
+cc_library_headers {
+ name: "libscheduler_headers",
+ defaults: ["libscheduler_defaults"],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libscheduler",
+ defaults: ["libscheduler_defaults"],
+ srcs: [],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 2bdcaf6..627c49a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -170,20 +170,21 @@
mEventThread->registerDisplayEventConnection(this);
}
-status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
+binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
outChannel->setReceiveFd(mChannel.moveReceiveFd());
outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
- return NO_ERROR;
+ return binder::Status::ok();
}
-status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
- mEventThread->setVsyncRate(rate, this);
- return NO_ERROR;
+binder::Status EventThreadConnection::setVsyncRate(int rate) {
+ mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this);
+ return binder::Status::ok();
}
-void EventThreadConnection::requestNextVsync() {
+binder::Status EventThreadConnection::requestNextVsync() {
ATRACE_NAME("requestNextVsync");
mEventThread->requestNextVsync(this);
+ return binder::Status::ok();
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -355,14 +356,7 @@
std::lock_guard<std::mutex> lock(mMutex);
LOG_FATAL_IF(!mVSyncState);
- const int64_t vsyncId = [&] {
- if (mTokenManager != nullptr) {
- return mTokenManager->generateTokenForPredictions(
- {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
- }
- return FrameTimelineInfo::INVALID_VSYNC_ID;
- }();
-
+ const int64_t vsyncId = generateToken(timestamp, deadlineTimestamp, expectedVSyncTimestamp);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
expectedVSyncTimestamp, deadlineTimestamp, vsyncId));
mCondition.notify_all();
@@ -567,12 +561,48 @@
}
}
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {timestamp, deadlineTimestamp, expectedVSyncTimestamp});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+}
+
+void EventThread::generateFrameTimeline(DisplayEventReceiver::Event& event) const {
+ // Add 1 to ensure the preferredFrameTimelineIndex entry (when multiplier == 0) is included.
+ for (int multiplier = -DisplayEventReceiver::kFrameTimelinesLength + 1, currentIndex = 0;
+ currentIndex < DisplayEventReceiver::kFrameTimelinesLength; multiplier++) {
+ nsecs_t deadline = event.vsync.deadlineTimestamp + multiplier * event.vsync.frameInterval;
+ // Valid possible frame timelines must have future values.
+ if (deadline > event.header.timestamp) {
+ if (multiplier == 0) {
+ event.vsync.preferredFrameTimelineIndex = currentIndex;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = event.vsync.vsyncId,
+ .deadlineTimestamp = event.vsync.deadlineTimestamp,
+ .expectedVSyncTimestamp = event.vsync.expectedVSyncTimestamp};
+ } else {
+ nsecs_t expectedVSync =
+ event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
+ event.vsync.frameTimelines[currentIndex] =
+ {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
+ .deadlineTimestamp = deadline,
+ .expectedVSyncTimestamp = expectedVSync};
+ }
+ currentIndex++;
+ }
+ }
+}
+
void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event,
const DisplayEventConsumers& consumers) {
for (const auto& consumer : consumers) {
DisplayEventReceiver::Event copy = event;
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
copy.vsync.frameInterval = mGetVsyncPeriodFunction(consumer->mOwnerUid);
+ generateFrameTimeline(copy);
}
switch (consumer->postEvent(copy)) {
case NO_ERROR:
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9265a25..fa9af09 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -17,8 +17,8 @@
#pragma once
#include <android-base/thread_annotations.h>
+#include <android/gui/BnDisplayEventConnection.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
#include <sys/types.h>
#include <utils/Errors.h>
@@ -80,7 +80,7 @@
virtual void dump(std::string& result) const = 0;
};
-class EventThreadConnection : public BnDisplayEventConnection {
+class EventThreadConnection : public gui::BnDisplayEventConnection {
public:
EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
@@ -88,9 +88,9 @@
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
- status_t stealReceiveChannel(gui::BitTube* outChannel) override;
- status_t setVsyncRate(uint32_t rate) override;
- void requestNextVsync() override; // asynchronous
+ binder::Status stealReceiveChannel(gui::BitTube* outChannel) override;
+ binder::Status setVsyncRate(int rate) override;
+ binder::Status requestNextVsync() override; // asynchronous
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
@@ -204,6 +204,10 @@
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
nsecs_t deadlineTimestamp) override;
+ int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const;
+ void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
+
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 84e3548..74a2ca7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,42 +84,38 @@
void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
std::lock_guard lock(mLock);
- for (const auto& info : mLayerInfos) {
- LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
- }
+ LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first !=
+ LayerHistory::layerStatus::NotFound,
+ "%s already registered", layer->getName().c_str());
auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
- mLayerInfos.emplace_back(layer, std::move(info));
+
+ // The layer can be placed on either map, it is assumed that partitionLayers() will be called
+ // to correct them.
+ mInactiveLayerInfos.insert({layer->getSequence(), std::make_pair(layer, std::move(info))});
}
void LayerHistory::deregisterLayer(Layer* layer) {
std::lock_guard lock(mLock);
-
- const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
- [layer](const auto& pair) { return pair.first == layer; });
- LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
- const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
- if (i < mActiveLayersEnd) {
- mActiveLayersEnd--;
+ if (!mActiveLayerInfos.erase(layer->getSequence())) {
+ if (!mInactiveLayerInfos.erase(layer->getSequence())) {
+ LOG_ALWAYS_FATAL("%s: unknown layer %p", __FUNCTION__, layer);
+ }
}
- const size_t last = mLayerInfos.size() - 1;
- std::swap(mLayerInfos[i], mLayerInfos[last]);
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
}
void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
LayerUpdateType updateType) {
std::lock_guard lock(mLock);
+ auto id = layer->getSequence();
- const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
- [layer](const auto& pair) { return pair.first == layer; });
- if (it == mLayerInfos.end()) {
+ auto [found, layerPair] = findLayer(id);
+ if (found == LayerHistory::layerStatus::NotFound) {
// Offscreen layer
ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
return;
}
- const auto& info = it->second;
+ const auto& info = layerPair->second;
const auto layerProps = LayerInfo::LayerProps{
.visible = layer->isVisible(),
.bounds = layer->getBounds(),
@@ -131,9 +127,10 @@
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Activate layer if inactive.
- if (const auto end = activeLayers().end(); it >= end) {
- std::iter_swap(it, end);
- mActiveLayersEnd++;
+ if (found == LayerHistory::layerStatus::LayerInInactiveMap) {
+ mActiveLayerInfos.insert(
+ {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+ mInactiveLayerInfos.erase(id);
}
}
@@ -145,7 +142,8 @@
partitionLayers(now);
- for (const auto& [layer, info] : activeLayers()) {
+ for (const auto& [key, value] : mActiveLayerInfos) {
+ auto& info = value.second;
const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
@@ -179,12 +177,29 @@
void LayerHistory::partitionLayers(nsecs_t now) {
const nsecs_t threshold = getActiveLayerThreshold(now);
- // Collect expired and inactive layers after active layers.
- size_t i = 0;
- while (i < mActiveLayersEnd) {
- auto& [layerUnsafe, info] = mLayerInfos[i];
+ // iterate over inactive map
+ LayerInfos::iterator it = mInactiveLayerInfos.begin();
+ while (it != mInactiveLayerInfos.end()) {
+ auto& [layerUnsafe, info] = it->second;
if (isLayerActive(*info, threshold)) {
- i++;
+ // move this to the active map
+
+ mActiveLayerInfos.insert({it->first, std::move(it->second)});
+ it = mInactiveLayerInfos.erase(it);
+ } else {
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ info->onLayerInactive(now);
+ it++;
+ }
+ }
+
+ // iterate over active map
+ it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ auto& [layerUnsafe, info] = it->second;
+ if (isLayerActive(*info, threshold)) {
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
const auto voteType = [&]() {
@@ -206,30 +221,68 @@
} else {
info->resetLayerVote();
}
- continue;
- }
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ it++;
+ } else {
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ info->onLayerInactive(now);
+ // move this to the inactive map
+ mInactiveLayerInfos.insert({it->first, std::move(it->second)});
+ it = mActiveLayerInfos.erase(it);
}
-
- info->onLayerInactive(now);
- std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
}
void LayerHistory::clear() {
std::lock_guard lock(mLock);
-
- for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory(systemTime());
+ for (const auto& [key, value] : mActiveLayerInfos) {
+ value.second->clearHistory(systemTime());
}
}
std::string LayerHistory::dump() const {
std::lock_guard lock(mLock);
- return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
- mActiveLayersEnd);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+ mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
+ mActiveLayerInfos.size());
+}
+
+float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
+ std::lock_guard lock(mLock);
+ auto [found, layerPair] = findLayer(id);
+ if (found != LayerHistory::layerStatus::NotFound) {
+ return layerPair->second->getFps(now).getValue();
+ }
+ return 0.f;
+}
+
+std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) {
+ // the layer could be in either the active or inactive map, try both
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+ }
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+ }
+ return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
+}
+
+std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer(
+ int32_t id) const {
+ // the layer could be in either the active or inactive map, try both
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+ }
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+ }
+ return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 92236f5..cc55700 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -20,6 +20,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <map>
#include <memory>
#include <mutex>
#include <string>
@@ -31,11 +32,9 @@
namespace android {
class Layer;
-class TestableScheduler;
namespace scheduler {
-class LayerHistoryTest;
class LayerInfo;
class LayerHistory {
@@ -74,34 +73,43 @@
void deregisterLayer(Layer*);
std::string dump() const;
+ // return the frames per second of the layer with the given sequence id.
+ float getLayerFramerate(nsecs_t now, int32_t id) const;
+
private:
- friend LayerHistoryTest;
- friend TestableScheduler;
+ friend class LayerHistoryTest;
+ friend class TestableScheduler;
using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
- using LayerInfos = std::vector<LayerPair>;
+ // keyed by id as returned from Layer::getSequence()
+ using LayerInfos = std::unordered_map<int32_t, LayerPair>;
- struct ActiveLayers {
- LayerInfos& infos;
- const size_t index;
+ // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
+ // layers to mInactiveLayerInfos.
+ // worst case time complexity is O(2 * inactive + active)
+ void partitionLayers(nsecs_t now) REQUIRES(mLock);
- auto begin() { return infos.begin(); }
- auto end() { return begin() + static_cast<long>(index); }
+ enum class layerStatus {
+ NotFound,
+ LayerInActiveMap,
+ LayerInInactiveMap,
};
- ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
- // Iterates over layers in a single pass, swapping pairs such that active layers precede
- // inactive layers, and inactive layers precede expired layers. Removes expired layers by
- // truncating after inactive layers.
- void partitionLayers(nsecs_t now) REQUIRES(mLock);
+ // looks up a layer by sequence id in both layerInfo maps.
+ // The first element indicates if and where the item was found
+ std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
+ std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const
+ REQUIRES(mLock);
mutable std::mutex mLock;
- // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
- // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
- LayerInfos mLayerInfos GUARDED_BY(mLock);
- size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+ // Partitioned into two maps to facility two kinds of retrieval:
+ // 1. retrieval of a layer by id (attempt lookup in both maps)
+ // 2. retrieval of all active layers (iterate that map)
+ // The partitioning is allowed to become out of date but calling partitionLayers refreshes the
+ // validity of each map.
+ LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
+ LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
uint32_t mDisplayArea = 0;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 8a45b66..943615c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -28,6 +28,7 @@
#include <cutils/compiler.h>
#include <cutils/trace.h>
+#include <ftl/enum.h>
#undef LOG_TAG
#define LOG_TAG "LayerInfo"
@@ -41,7 +42,7 @@
: mName(name),
mOwnerUid(ownerUid),
mDefaultVote(defaultVote),
- mLayerVote({defaultVote, Fps(0.0f)}),
+ mLayerVote({defaultVote, Fps()}),
mRefreshRateHistory(name) {}
void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
@@ -74,12 +75,16 @@
}
bool LayerInfo::isFrequent(nsecs_t now) const {
+ using fps_approx_ops::operator>=;
// If we know nothing about this layer we consider it as frequent as it might be the start
// of an animation.
if (mFrameTimes.size() < kFrequentLayerWindowSize) {
return true;
}
+ return getFps(now) >= kMinFpsForFrequentLayer;
+}
+Fps LayerInfo::getFps(nsecs_t now) const {
// Find the first active frame
auto it = mFrameTimes.begin();
for (; it != mFrameTimes.end(); ++it) {
@@ -90,13 +95,12 @@
const auto numFrames = std::distance(it, mFrameTimes.end());
if (numFrames < kFrequentLayerWindowSize) {
- return false;
+ return Fps();
}
// Layer is considered frequent if the average frame rate is higher than the threshold
const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return Fps::fromPeriodNsecs(totalTime / (numFrames - 1))
- .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer);
+ return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
}
bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -191,17 +195,17 @@
return std::nullopt;
}
- const auto averageFrameTime = calculateAverageFrameTime();
- if (averageFrameTime.has_value()) {
+ if (const auto averageFrameTime = calculateAverageFrameTime()) {
const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime);
const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
if (refreshRateConsistent) {
const auto knownRefreshRate = refreshRateConfigs.findClosestKnownFrameRate(refreshRate);
- // To avoid oscillation, use the last calculated refresh rate if it is
- // close enough
+ using fps_approx_ops::operator!=;
+
+ // To avoid oscillation, use the last calculated refresh rate if it is close enough.
if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) >
MARGIN &&
- !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) {
+ mLastRefreshRate.reported != knownRefreshRate) {
mLastRefreshRate.calculated = refreshRate;
mLastRefreshRate.reported = knownRefreshRate;
}
@@ -228,7 +232,7 @@
if (isAnimating(now)) {
ALOGV("%s is animating", mName.c_str());
mLastRefreshRate.animatingOrInfrequent = true;
- return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Max, Fps()};
}
if (!isFrequent(now)) {
@@ -236,7 +240,7 @@
mLastRefreshRate.animatingOrInfrequent = true;
// Infrequent layers vote for mininal refresh rate for
// battery saving purposes and also to prevent b/135718869.
- return {LayerHistory::LayerVoteType::Min, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Min, Fps()};
}
// If the layer was previously tagged as animating or infrequent, we clear
@@ -253,13 +257,13 @@
}
ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
- return {LayerHistory::LayerVoteType::Max, Fps(0.0f)};
+ return {LayerHistory::LayerVoteType::Max, Fps()};
}
-const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
if (mTraceTags.count(type) == 0) {
- const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
- mTraceTags.emplace(type, tag);
+ auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
+ mTraceTags.emplace(type, std::move(tag));
}
return mTraceTags.at(type).c_str();
@@ -300,9 +304,13 @@
bool LayerInfo::RefreshRateHistory::isConsistent() const {
if (mRefreshRates.empty()) return true;
- const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
- const auto consistent =
+ const auto [min, max] =
+ std::minmax_element(mRefreshRates.begin(), mRefreshRates.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return isStrictlyLess(lhs.refreshRate, rhs.refreshRate);
+ });
+
+ const bool consistent =
max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS;
if (CC_UNLIKELY(sTraceEnabled)) {
@@ -321,4 +329,4 @@
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index ce9783c..2d88a4f 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,15 +16,19 @@
#pragma once
+#include <chrono>
+#include <deque>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
#include <ui/Transform.h>
#include <utils/Timers.h>
-#include <chrono>
-#include <deque>
+#include <scheduler/Seamlessness.h>
#include "LayerHistory.h"
#include "RefreshRateConfigs.h"
-#include "Scheduler/Seamlessness.h"
#include "SchedulerUtils.h"
namespace android {
@@ -51,7 +55,7 @@
// is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
// favor of a low refresh rate.
static constexpr size_t kFrequentLayerWindowSize = 3;
- static constexpr Fps kMinFpsForFrequentLayer{10.0f};
+ static constexpr Fps kMinFpsForFrequentLayer = 10_Hz;
static constexpr auto kMaxPeriodForFrequentLayerNs =
std::chrono::nanoseconds(kMinFpsForFrequentLayer.getPeriodNsecs()) + 1ms;
@@ -62,7 +66,7 @@
// Holds information about the layer vote
struct LayerVote {
LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
- Fps fps{0.0f};
+ Fps fps;
Seamlessness seamlessness = Seamlessness::Default;
};
@@ -78,6 +82,8 @@
NoVote, // Layer doesn't have any requirements for the refresh rate and
// should not be considered when the display refresh rate is determined.
+
+ ftl_last = NoVote
};
// Encapsulates the frame rate and compatibility of the layer. This information will be used
@@ -86,19 +92,17 @@
using Seamlessness = scheduler::Seamlessness;
Fps rate;
- FrameRateCompatibility type;
- Seamlessness seamlessness;
+ FrameRateCompatibility type = FrameRateCompatibility::Default;
+ Seamlessness seamlessness = Seamlessness::Default;
- FrameRate()
- : rate(0),
- type(FrameRateCompatibility::Default),
- seamlessness(Seamlessness::Default) {}
+ FrameRate() = default;
+
FrameRate(Fps rate, FrameRateCompatibility type,
Seamlessness seamlessness = Seamlessness::OnlySeamless)
: rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
bool operator==(const FrameRate& other) const {
- return rate.equalsWithMargin(other.rate) && type == other.type &&
+ return isApproxEqual(rate, other.rate) && type == other.type &&
seamlessness == other.seamlessness;
}
@@ -151,7 +155,7 @@
void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
// Resets the layer vote to its default.
- void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(0.0f), Seamlessness::Default}; }
+ void resetLayerVote() { mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default}; }
std::string getName() const { return mName; }
@@ -174,6 +178,9 @@
// Returns a C string for tracing a vote
const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+ // Return the framerate of this layer.
+ Fps getFps(nsecs_t now) const;
+
void onLayerInactive(nsecs_t now) {
// Mark mFrameTimeValidSince to now to ignore all previous frame times.
// We are not deleting the old frame to keep track of whether we should treat the first
@@ -201,9 +208,9 @@
// Holds information about the calculated and reported refresh rate
struct RefreshRateHeuristicData {
// Rate calculated on the layer
- Fps calculated{0.0f};
+ Fps calculated;
// Last reported rate for LayerInfo::getRefreshRate()
- Fps reported{0.0f};
+ Fps reported;
// Whether the last reported rate for LayerInfo::getRefreshRate()
// was due to animation or infrequent updates
bool animatingOrInfrequent = false;
@@ -229,14 +236,8 @@
// Holds the refresh rate when it was calculated
struct RefreshRateData {
- Fps refreshRate{0.0f};
+ Fps refreshRate;
nsecs_t timestamp = 0;
-
- bool operator<(const RefreshRateData& other) const {
- // We don't need comparison with margins since we are using
- // this to find the min and max refresh rates.
- return refreshRate.getValue() < other.refreshRate.getValue();
- }
};
// Holds tracing strings
@@ -268,7 +269,7 @@
// Used for sanitizing the heuristic data. If two frames are less than
// this period apart from each other they'll be considered as duplicates.
- static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs();
+ static constexpr nsecs_t kMinPeriodBetweenFrames = (240_Hz).getPeriodNsecs();
// Used for sanitizing the heuristic data. If two frames are more than
// this period apart from each other, the interval between them won't be
// taken into account when calculating average frame rate.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 4d51125..a020e2c 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -27,49 +27,44 @@
#include "EventThread.h"
#include "FrameTimeline.h"
#include "MessageQueue.h"
-#include "SurfaceFlinger.h"
namespace android::impl {
-void MessageQueue::Handler::dispatchRefresh() {
- if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) {
- mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
- }
-}
-
-void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) {
- if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) {
+void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) {
+ if (!mFramePending.exchange(true)) {
mVsyncId = vsyncId;
- mExpectedVSyncTime = expectedVSyncTimestamp;
- mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+ mExpectedVsyncTime = expectedVsyncTime;
+ mQueue.mLooper->sendMessage(this, Message());
}
}
-bool MessageQueue::Handler::invalidatePending() {
- constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh;
- return (mEventMask.load() & pendingMask) != 0;
+bool MessageQueue::Handler::isFramePending() const {
+ return mFramePending.load();
}
-void MessageQueue::Handler::handleMessage(const Message& message) {
- switch (message.what) {
- case INVALIDATE:
- mEventMask.fetch_and(~eventMaskInvalidate);
- mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
- break;
- case REFRESH:
- mEventMask.fetch_and(~eventMaskRefresh);
- mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime);
- break;
+void MessageQueue::Handler::handleMessage(const Message&) {
+ mFramePending.store(false);
+
+ const nsecs_t frameTime = systemTime();
+ auto& compositor = mQueue.mCompositor;
+
+ if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
+ return;
}
+
+ compositor.composite(frameTime);
+ compositor.sample();
}
-// ---------------------------------------------------------------------------
+MessageQueue::MessageQueue(ICompositor& compositor)
+ : MessageQueue(compositor, sp<Handler>::make(*this)) {}
-void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
- mFlinger = flinger;
- mLooper = new Looper(true);
- mHandler = new Handler(*this);
-}
+constexpr bool kAllowNonCallbacks = true;
+
+MessageQueue::MessageQueue(ICompositor& compositor, sp<Handler> handler)
+ : mCompositor(compositor),
+ mLooper(sp<Looper>::make(kAllowNonCallbacks)),
+ mHandler(std::move(handler)) {}
// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly
// and remove the EventThread from MessageQueue
@@ -110,11 +105,13 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
- mVsync.scheduled = false;
+ mVsync.scheduledFrameTime.reset();
}
- mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
- {targetWakeupTime, readyTime, vsyncTime}),
- vsyncTime);
+
+ const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
+ {targetWakeupTime, readyTime, vsyncTime});
+
+ mHandler->dispatchFrame(vsyncId, vsyncTime);
}
void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
@@ -135,8 +132,8 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
- if (mVsync.scheduled) {
- mVsync.expectedWakeupTime = mVsync.registration->schedule(
+ if (mVsync.scheduledFrameTime) {
+ mVsync.scheduledFrameTime = mVsync.registration->schedule(
{mVsync.workDuration.get().count(),
/*readyDuration=*/0, mVsync.lastCallbackTime.count()});
}
@@ -168,7 +165,7 @@
mLooper->sendMessage(handler, Message());
}
-void MessageQueue::invalidate() {
+void MessageQueue::scheduleFrame() {
ATRACE_CALL();
{
@@ -181,41 +178,34 @@
}
std::lock_guard lock(mVsync.mutex);
- mVsync.scheduled = true;
- mVsync.expectedWakeupTime =
+ mVsync.scheduledFrameTime =
mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
.readyDuration = 0,
.earliestVsync = mVsync.lastCallbackTime.count()});
}
-void MessageQueue::refresh() {
- mHandler->dispatchRefresh();
-}
-
void MessageQueue::injectorCallback() {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId,
- buffer[i].vsync.expectedVSyncTimestamp);
+ auto& vsync = buffer[i].vsync;
+ mHandler->dispatchFrame(vsync.vsyncId, vsync.expectedVSyncTimestamp);
break;
}
}
}
}
-std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() {
- if (mHandler->invalidatePending()) {
- return std::chrono::steady_clock::now();
+auto MessageQueue::getScheduledFrameTime() const -> std::optional<Clock::time_point> {
+ if (mHandler->isFramePending()) {
+ return Clock::now();
}
std::lock_guard lock(mVsync.mutex);
- if (mVsync.scheduled) {
- LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled");
- const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime);
- return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime);
+ if (const auto time = mVsync.scheduledFrameTime) {
+ return Clock::time_point(std::chrono::nanoseconds(*time));
}
return std::nullopt;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 58ce9b9..9532e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -22,7 +22,7 @@
#include <utility>
#include <android-base/thread_annotations.h>
-#include <gui/IDisplayEventConnection.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
#include <utils/Looper.h>
#include <utils/Timers.h>
@@ -33,7 +33,14 @@
namespace android {
-class SurfaceFlinger;
+struct ICompositor {
+ virtual bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) = 0;
+ virtual void composite(nsecs_t frameTime) = 0;
+ virtual void sample() = 0;
+
+protected:
+ ~ICompositor() = default;
+};
template <typename F>
class Task : public MessageHandler {
@@ -56,65 +63,60 @@
class MessageQueue {
public:
- enum {
- INVALIDATE = 0,
- REFRESH = 1,
- };
-
virtual ~MessageQueue() = default;
- virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) = 0;
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
virtual void setInjector(sp<EventThreadConnection>) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
- virtual void invalidate() = 0;
- virtual void refresh() = 0;
- virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0;
-};
+ virtual void scheduleFrame() = 0;
-// ---------------------------------------------------------------------------
+ using Clock = std::chrono::steady_clock;
+ virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
+};
namespace impl {
class MessageQueue : public android::MessageQueue {
protected:
class Handler : public MessageHandler {
- enum : uint32_t {
- eventMaskInvalidate = 0x1,
- eventMaskRefresh = 0x2,
- eventMaskTransaction = 0x4
- };
MessageQueue& mQueue;
- std::atomic<uint32_t> mEventMask;
- std::atomic<int64_t> mVsyncId;
- std::atomic<nsecs_t> mExpectedVSyncTime;
+ std::atomic_bool mFramePending = false;
+ std::atomic<int64_t> mVsyncId = 0;
+ std::atomic<nsecs_t> mExpectedVsyncTime = 0;
public:
- explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
+ explicit Handler(MessageQueue& queue) : mQueue(queue) {}
void handleMessage(const Message& message) override;
- virtual void dispatchRefresh();
- virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp);
- virtual bool invalidatePending();
+
+ bool isFramePending() const;
+
+ virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime);
};
friend class Handler;
- sp<SurfaceFlinger> mFlinger;
- sp<Looper> mLooper;
+ // For tests.
+ MessageQueue(ICompositor&, sp<Handler>);
+
+ void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
+
+private:
+ ICompositor& mCompositor;
+ const sp<Looper> mLooper;
+ const sp<Handler> mHandler;
struct Vsync {
frametimeline::TokenManager* tokenManager = nullptr;
std::unique_ptr<scheduler::VSyncCallbackRegistration> registration;
- std::mutex mutex;
+ mutable std::mutex mutex;
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
- bool scheduled GUARDED_BY(mutex) = false;
- std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex);
+ std::optional<nsecs_t> scheduledFrameTime GUARDED_BY(mutex);
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
@@ -127,14 +129,11 @@
Vsync mVsync;
Injector mInjector;
- sp<Handler> mHandler;
-
- void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);
void injectorCallback();
public:
- ~MessageQueue() override = default;
- void init(const sp<SurfaceFlinger>& flinger) override;
+ explicit MessageQueue(ICompositor&);
+
void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
std::chrono::nanoseconds workDuration) override;
void setDuration(std::chrono::nanoseconds workDuration) override;
@@ -143,13 +142,9 @@
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
- // sends INVALIDATE message at next VSYNC
- void invalidate() override;
+ void scheduleFrame() override;
- // sends REFRESH message at next VSYNC
- void refresh() override;
-
- std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override;
+ std::optional<Clock::time_point> getScheduledFrameTime() const override;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0ee9fb3..71d5631 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -21,40 +21,43 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wextra"
-#include "RefreshRateConfigs.h"
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <utils/Trace.h>
#include <chrono>
#include <cmath>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <utils/Trace.h>
+
#include "../SurfaceFlingerProperties.h"
+#include "RefreshRateConfigs.h"
#undef LOG_TAG
#define LOG_TAG "RefreshRateConfigs"
namespace android::scheduler {
namespace {
+
std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
- RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight,
- toString(layer.seamlessness).c_str(),
+ ftl::enum_string(layer.vote).c_str(), weight,
+ ftl::enum_string(layer.seamlessness).c_str(),
to_string(layer.desiredRefreshRate).c_str());
}
std::vector<Fps> constructKnownFrameRates(const DisplayModes& modes) {
- std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)};
+ std::vector<Fps> knownFrameRates = {24_Hz, 30_Hz, 45_Hz, 60_Hz, 72_Hz};
knownFrameRates.reserve(knownFrameRates.size() + modes.size());
- // Add all supported refresh rates to the set
+ // Add all supported refresh rates.
for (const auto& mode : modes) {
- const auto refreshRate = Fps::fromPeriodNsecs(mode->getVsyncPeriod());
- knownFrameRates.emplace_back(refreshRate);
+ knownFrameRates.push_back(Fps::fromPeriodNsecs(mode->getVsyncPeriod()));
}
- // Sort and remove duplicates
- std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess);
+ // Sort and remove duplicates.
+ std::sort(knownFrameRates.begin(), knownFrameRates.end(), isStrictlyLess);
knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
- Fps::EqualsWithMargin()),
+ isApproxEqual),
knownFrameRates.end());
return knownFrameRates;
}
@@ -64,31 +67,17 @@
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
+bool RefreshRate::inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
+ using fps_approx_ops::operator<=;
+ return minRefreshRate <= getFps() && getFps() <= maxRefreshRate;
+}
+
std::string RefreshRate::toString() const {
return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}",
getModeId().value(), mode->getHwcId(), getFps().getValue(),
mode->getWidth(), mode->getHeight(), getModeGroup());
}
-std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
- switch (vote) {
- case LayerVoteType::NoVote:
- return "NoVote";
- case LayerVoteType::Min:
- return "Min";
- case LayerVoteType::Max:
- return "Max";
- case LayerVoteType::Heuristic:
- return "Heuristic";
- case LayerVoteType::ExplicitDefault:
- return "ExplicitDefault";
- case LayerVoteType::ExplicitExactOrMultiple:
- return "ExplicitExactOrMultiple";
- case LayerVoteType::ExplicitExact:
- return "ExplicitExact";
- }
-}
-
std::string RefreshRateConfigs::Policy::toString() const {
return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
", primary range: %s, app request range: %s",
@@ -110,14 +99,14 @@
bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer,
const RefreshRate& refreshRate) const {
+ using namespace fps_approx_ops;
+
switch (layer.vote) {
case LayerVoteType::ExplicitExactOrMultiple:
case LayerVoteType::Heuristic:
if (mConfig.frameRateMultipleThreshold != 0 &&
- refreshRate.getFps().greaterThanOrEqualWithMargin(
- Fps(mConfig.frameRateMultipleThreshold)) &&
- layer.desiredRefreshRate.lessThanWithMargin(
- Fps(mConfig.frameRateMultipleThreshold / 2))) {
+ refreshRate.getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold) &&
+ layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) {
// Don't vote high refresh rates past the threshold for layers with a low desired
// refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for
// 120 Hz, but desired 60 fps should have a vote.
@@ -134,6 +123,68 @@
return true;
}
+float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(
+ const LayerRequirement& layer, const RefreshRate& refreshRate) const {
+ constexpr float kScoreForFractionalPairs = .8f;
+
+ const auto displayPeriod = refreshRate.getVsyncPeriod();
+ const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
+ if (layer.vote == LayerVoteType::ExplicitDefault) {
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal 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));
+ }
+
+ if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+ layer.vote == LayerVoteType::Heuristic) {
+ if (isFractionalPairOrMultiple(refreshRate.getFps(), layer.desiredRefreshRate)) {
+ return kScoreForFractionalPairs;
+ }
+
+ // Calculate how many display vsyncs we need to present a single frame for this
+ // layer
+ const auto [displayFramesQuotient, displayFramesRemainder] =
+ getDisplayFrames(layerPeriod, displayPeriod);
+ static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+ if (displayFramesRemainder == 0) {
+ // Layer desired refresh rate matches the display rate.
+ return 1.0f;
+ }
+
+ if (displayFramesQuotient == 0) {
+ // Layer desired refresh rate is higher than the display rate.
+ return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+ (1.0f / (MAX_FRAMES_TO_FIT + 1));
+ }
+
+ // Layer desired refresh rate is lower than the display rate. Check how well it fits
+ // the cadence.
+ auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
+ int iter = 2;
+ while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+ diff = diff - (displayPeriod - diff);
+ iter++;
+ }
+
+ return (1.0f / iter);
+ }
+
+ return 0;
+}
+
float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer,
const RefreshRate& refreshRate,
bool isSeamlessSwitch) const {
@@ -153,51 +204,6 @@
return ratio * ratio;
}
- const auto displayPeriod = refreshRate.getVsyncPeriod();
- const auto layerPeriod = layer.desiredRefreshRate.getPeriodNsecs();
- if (layer.vote == LayerVoteType::ExplicitDefault) {
- // Find the actual rate the layer will render, assuming
- // that layerPeriod is the minimal time to render a frame
- auto actualLayerPeriod = displayPeriod;
- int multiplier = 1;
- while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
- multiplier++;
- actualLayerPeriod = displayPeriod * multiplier;
- }
- return std::min(1.0f,
- static_cast<float>(layerPeriod) / static_cast<float>(actualLayerPeriod));
- }
-
- if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
- layer.vote == LayerVoteType::Heuristic) {
- // Calculate how many display vsyncs we need to present a single frame for this
- // layer
- const auto [displayFramesQuotient, displayFramesRemainder] =
- getDisplayFrames(layerPeriod, displayPeriod);
- static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
- if (displayFramesRemainder == 0) {
- // Layer desired refresh rate matches the display rate.
- return 1.0f * seamlessness;
- }
-
- if (displayFramesQuotient == 0) {
- // Layer desired refresh rate is higher than the display rate.
- return (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
- (1.0f / (MAX_FRAMES_TO_FIT + 1));
- }
-
- // Layer desired refresh rate is lower than the display rate. Check how well it fits
- // the cadence.
- auto diff = std::abs(displayFramesRemainder - (displayPeriod - displayFramesRemainder));
- int iter = 2;
- while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
- diff = diff - (displayPeriod - diff);
- iter++;
- }
-
- return (1.0f / iter) * seamlessness;
- }
-
if (layer.vote == LayerVoteType::ExplicitExact) {
const int divider = getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate);
if (mSupportsFrameRateOverride) {
@@ -210,7 +216,18 @@
return divider == 1;
}
- return 0;
+ // If the layer frame rate is a divider of the refresh rate it should score
+ // the highest score.
+ if (getFrameRateDivider(refreshRate.getFps(), layer.desiredRefreshRate) > 0) {
+ return 1.0f * seamlessness;
+ }
+
+ // The layer frame rate is not a divider of the refresh rate,
+ // there is a small penalty attached to the score to favor the frame rates
+ // the exactly matches the display refresh rate or a multiple.
+ constexpr float kNonExactMatchingPenalty = 0.99f;
+ return calculateNonExactMatchingLayerScoreLocked(layer, refreshRate) * seamlessness *
+ kNonExactMatchingPenalty;
}
struct RefreshRateScore {
@@ -219,7 +236,7 @@
};
RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
std::lock_guard lock(mLock);
@@ -241,7 +258,7 @@
}
std::optional<RefreshRate> RefreshRateConfigs::getCachedBestRefreshRate(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
const bool sameAsLastCall = lastBestRefreshRateInvocation &&
lastBestRefreshRateInvocation->layerRequirements == layers &&
@@ -258,7 +275,7 @@
}
RefreshRate RefreshRateConfigs::getBestRefreshRateLocked(
- const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ const std::vector<LayerRequirement>& layers, GlobalSignals globalSignals,
GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
ALOGV("getBestRefreshRate %zu layers", layers.size());
@@ -319,20 +336,30 @@
const bool hasExplicitVoteLayers = explicitDefaultVoteLayers > 0 ||
explicitExactOrMultipleVoteLayers > 0 || explicitExact > 0;
+ const Policy* policy = getCurrentPolicyLocked();
+ const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
+ // If the default mode group is different from the group of current mode,
+ // this means a layer requesting a seamed mode switch just disappeared and
+ // we should switch back to the default group.
+ // However if a seamed layer is still present we anchor around the group
+ // of the current mode, in order to prevent unnecessary seamed mode switches
+ // (e.g. when pausing a video playback).
+ const auto anchorGroup = seamedFocusedLayers > 0 ? mCurrentRefreshRate->getModeGroup()
+ : defaultMode->getModeGroup();
+
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost.
if (globalSignals.touch && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
setTouchConsidered();
- return getMaxRefreshRateByPolicyLocked();
+ return getMaxRefreshRateByPolicyLocked(anchorGroup);
}
// 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 Policy* policy = getCurrentPolicyLocked();
const bool primaryRangeIsSingleRate =
- policy->primaryRange.min.equalsWithMargin(policy->primaryRange.max);
+ isApproxEqual(policy->primaryRange.min, policy->primaryRange.max);
if (!globalSignals.touch && globalSignals.idle &&
!(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
@@ -342,7 +369,9 @@
}
if (layers.empty() || noVoteLayers == layers.size()) {
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("no layers with votes - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
}
// Only if all layers want Min we should return Min
@@ -359,11 +388,9 @@
scores.emplace_back(RefreshRateScore{refreshRate, 0.0f});
}
- const auto& defaultMode = mRefreshRates.at(policy->defaultMode);
-
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
- layerVoteTypeString(layer.vote).c_str(), layer.weight,
+ ftl::enum_string(layer.vote).c_str(), layer.weight,
layer.desiredRefreshRate.getValue());
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
@@ -398,10 +425,7 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = seamedFocusedLayers > 0
- ? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
- : scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
-
+ const bool isInPolicyForDefault = scores[i].refreshRate->getModeGroup() == anchorGroup;
if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->toString().c_str(),
@@ -422,7 +446,7 @@
const auto layerScore =
calculateLayerScoreLocked(layer, *scores[i].refreshRate, isSeamlessSwitch);
- ALOGV("%s gives %s score of %.2f", formatLayerInfo(layer, weight).c_str(),
+ ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->getName().c_str(), layerScore);
scores[i].score += weight * layerScore;
}
@@ -440,9 +464,9 @@
// range instead of picking a random score from the app range.
if (std::all_of(scores.begin(), scores.end(),
[](RefreshRateScore score) { return score.score == 0; })) {
- ALOGV("layers not scored - choose %s",
- getMaxRefreshRateByPolicyLocked().getName().c_str());
- return getMaxRefreshRateByPolicyLocked();
+ const auto& refreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
+ ALOGV("layers not scored - choose %s", refreshRate.getName().c_str());
+ return refreshRate;
} else {
return *bestRefreshRate;
}
@@ -452,7 +476,7 @@
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
- const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+ const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(anchorGroup);
const bool touchBoostForExplicitExact = [&] {
if (mSupportsFrameRateOverride) {
@@ -463,8 +487,11 @@
return explicitExact == 0;
}
}();
+
+ using fps_approx_ops::operator<;
+
if (globalSignals.touch && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- bestRefreshRate->getFps().lessThanWithMargin(touchRefreshRate.getFps())) {
+ bestRefreshRate->getFps() < touchRefreshRate.getFps()) {
setTouchConsidered();
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
@@ -517,7 +544,8 @@
}
RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverrides(
- const std::vector<LayerRequirement>& layers, Fps displayFrameRate, bool touch) const {
+ const std::vector<LayerRequirement>& layers, Fps displayFrameRate,
+ GlobalSignals globalSignals) const {
ATRACE_CALL();
if (!mSupportsFrameRateOverride) return {};
@@ -535,7 +563,7 @@
return layer->vote == LayerVoteType::ExplicitExactOrMultiple;
});
- if (touch && hasExplicitExactOrMultiple) {
+ if (globalSignals.touch && hasExplicitExactOrMultiple) {
continue;
}
@@ -583,7 +611,7 @@
template <typename Iter>
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
- constexpr auto EPSILON = 0.001f;
+ constexpr auto kEpsilon = 0.0001f;
const RefreshRate* bestRefreshRate = begin->refreshRate;
float max = begin->score;
for (auto i = begin; i != end; ++i) {
@@ -592,7 +620,7 @@
ATRACE_INT(refreshRate->getName().c_str(), round<int>(score * 100));
- if (score > max * (1 + EPSILON)) {
+ if (score > max * (1 + kEpsilon)) {
max = score;
bestRefreshRate = refreshRate;
}
@@ -635,10 +663,10 @@
return getMaxRefreshRateByPolicyLocked();
}
-const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked(int anchorGroup) const {
for (auto it = mPrimaryRefreshRates.rbegin(); it != mPrimaryRefreshRates.rend(); it++) {
const auto& refreshRate = (**it);
- if (mCurrentRefreshRate->getModeGroup() == refreshRate.getModeGroup()) {
+ if (anchorGroup == refreshRate.getModeGroup()) {
return refreshRate;
}
}
@@ -762,8 +790,10 @@
ALOGE("Default mode is not in the primary range.");
return false;
}
- return policy.appRequestRange.min.lessThanOrEqualWithMargin(policy.primaryRange.min) &&
- policy.appRequestRange.max.greaterThanOrEqualWithMargin(policy.primaryRange.max);
+
+ using namespace fps_approx_ops;
+ return policy.appRequestRange.min <= policy.primaryRange.min &&
+ policy.appRequestRange.max >= policy.primaryRange.max;
}
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
@@ -889,19 +919,21 @@
}
Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const {
- if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) {
- return *mKnownFrameRates.begin();
+ using namespace fps_approx_ops;
+
+ if (frameRate <= mKnownFrameRates.front()) {
+ return mKnownFrameRates.front();
}
- if (frameRate.greaterThanOrEqualWithMargin(*std::prev(mKnownFrameRates.end()))) {
- return *std::prev(mKnownFrameRates.end());
+ if (frameRate >= mKnownFrameRates.back()) {
+ return mKnownFrameRates.back();
}
auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate,
- Fps::comparesLess);
+ isStrictlyLess);
- const auto distance1 = std::abs((frameRate.getValue() - lowerBound->getValue()));
- const auto distance2 = std::abs((frameRate.getValue() - std::prev(lowerBound)->getValue()));
+ const auto distance1 = std::abs(frameRate.getValue() - lowerBound->getValue());
+ const auto distance2 = std::abs(frameRate.getValue() - std::prev(lowerBound)->getValue());
return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}
@@ -920,7 +952,7 @@
}
if (minByPolicy == maxByPolicy) {
// when min primary range in display manager policy is below device min turn on the timer.
- if (currentPolicy->primaryRange.min.lessThanWithMargin(deviceMin.getFps())) {
+ if (isApproxLess(currentPolicy->primaryRange.min, deviceMin.getFps())) {
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
@@ -945,6 +977,17 @@
return static_cast<int>(numPeriodsRounded);
}
+bool RefreshRateConfigs::isFractionalPairOrMultiple(Fps smaller, Fps bigger) {
+ if (isStrictlyLess(bigger, smaller)) {
+ return isFractionalPairOrMultiple(bigger, smaller);
+ }
+
+ const auto multiplier = std::round(bigger.getValue() / smaller.getValue());
+ constexpr float kCoef = 1000.f / 1001.f;
+ return isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier / kCoef)) ||
+ isApproxEqual(bigger, Fps::fromValue(smaller.getValue() * multiplier * kCoef));
+}
+
void RefreshRateConfigs::dump(std::string& result) const {
std::lock_guard lock(mLock);
base::StringAppendF(&result, "DesiredDisplayModeSpecs (DisplayManager): %s\n\n",
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 21867cc..4bbdab6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -16,31 +16,32 @@
#pragma once
-#include <android-base/stringprintf.h>
-#include <gui/DisplayEventReceiver.h>
-
#include <algorithm>
#include <numeric>
#include <optional>
#include <type_traits>
+#include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/SchedulerUtils.h"
-#include "Scheduler/Seamlessness.h"
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
using namespace std::chrono_literals;
-enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
+enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
-inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) {
- using T = std::underlying_type_t<RefreshRateConfigEvent>;
- return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
+ using T = std::underlying_type_t<DisplayModeEvent>;
+ return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -76,19 +77,15 @@
// Checks whether the fps of this RefreshRate struct is within a given min and max refresh
// rate passed in. Margin of error is applied to the boundaries for approximation.
- bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const {
- return minRefreshRate.lessThanOrEqualWithMargin(getFps()) &&
- getFps().lessThanOrEqualWithMargin(maxRefreshRate);
- }
+ bool inPolicy(Fps minRefreshRate, Fps maxRefreshRate) const;
- bool operator!=(const RefreshRate& other) const { return mode != other.mode; }
+ bool operator==(const RefreshRate& other) const { return mode == other.mode; }
+ bool operator!=(const RefreshRate& other) const { return !operator==(other); }
bool operator<(const RefreshRate& other) const {
- return getFps().getValue() < other.getFps().getValue();
+ return isStrictlyLess(getFps(), other.getFps());
}
- bool operator==(const RefreshRate& other) const { return !(*this != other); }
-
std::string toString() const;
friend std::ostream& operator<<(std::ostream& os, const RefreshRate& refreshRate) {
return os << refreshRate.toString();
@@ -105,11 +102,11 @@
std::unordered_map<DisplayModeId, std::unique_ptr<const RefreshRate>>;
struct FpsRange {
- Fps min{0.0f};
- Fps max{std::numeric_limits<float>::max()};
+ Fps min = Fps::fromValue(0.f);
+ Fps max = Fps::fromValue(std::numeric_limits<float>::max());
bool operator==(const FpsRange& other) const {
- return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max);
+ return isApproxEqual(min, other.min) && isApproxEqual(max, other.max);
}
bool operator!=(const FpsRange& other) const { return !(*this == other); }
@@ -209,6 +206,7 @@
ExplicitExact, // Specific refresh rate that was provided by the app with
// Exact compatibility
+ ftl_last = ExplicitExact
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -221,7 +219,7 @@
// Layer vote type.
LayerVoteType vote = LayerVoteType::NoVote;
// Layer's desired refresh rate, if applicable.
- Fps desiredRefreshRate{0.0f};
+ Fps desiredRefreshRate;
// If a seamless mode switch is required.
Seamlessness seamlessness = Seamlessness::Default;
// Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
@@ -232,7 +230,7 @@
bool operator==(const LayerRequirement& other) const {
return name == other.name && vote == other.vote &&
- desiredRefreshRate.equalsWithMargin(other.desiredRefreshRate) &&
+ isApproxEqual(desiredRefreshRate, other.desiredRefreshRate) &&
seamlessness == other.seamlessness && weight == other.weight &&
focused == other.focused;
}
@@ -247,18 +245,14 @@
// True if the system hasn't seen any buffers posted to layers recently.
bool idle = false;
- bool operator==(const GlobalSignals& other) const {
+ bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle;
}
};
- // Returns the refresh rate that fits best to the given layers.
- // layers - The layer requirements to consider.
- // globalSignals - global state of touch and idle
- // outSignalsConsidered - An output param that tells the caller whether the refresh rate was
- // chosen based on touch boost and/or idle timer.
- RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ // Returns the refresh rate that best fits the given layers. outSignalsConsidered returns
+ // whether the refresh rate was chosen based on touch boost and/or idle timer.
+ RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>&, GlobalSignals,
GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
@@ -292,9 +286,6 @@
// Stores the current modeId the device operates at
void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
- // Returns a string that represents the layer vote type
- static std::string layerVoteTypeString(LayerVoteType vote);
-
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
@@ -344,14 +335,15 @@
// layer refresh rate.
static int getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate);
+ // Returns if the provided frame rates have a ratio t*1000/1001 or t*1001/1000
+ // for an integer t.
+ static bool isFractionalPairOrMultiple(Fps, Fps);
+
using UidToFrameRateOverride = std::map<uid_t, Fps>;
+
// Returns the frame rate override for each uid.
- //
- // @param layers list of visible layers
- // @param displayFrameRate the display frame rate
- // @param touch whether touch timer is active (i.e. user touched the screen recently)
- UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>& layers,
- Fps displayFrameRate, bool touch) const
+ UidToFrameRateOverride getFrameRateOverrides(const std::vector<LayerRequirement>&,
+ Fps displayFrameRate, GlobalSignals) const
EXCLUDES(mLock);
bool supportsKernelIdleTimer() const { return mConfig.supportKernelIdleTimer; }
@@ -404,13 +396,12 @@
const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock);
- std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ std::optional<RefreshRate> getCachedBestRefreshRate(const std::vector<LayerRequirement>&,
+ GlobalSignals,
GlobalSignals* outSignalsConsidered) const
REQUIRES(mLock);
- RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers,
- const GlobalSignals& globalSignals,
+ RefreshRate getBestRefreshRateLocked(const std::vector<LayerRequirement>&, GlobalSignals,
GlobalSignals* outSignalsConsidered) const REQUIRES(mLock);
// Returns the refresh rate with the highest score in the collection specified from begin
@@ -429,7 +420,11 @@
// Returns the highest refresh rate according to the current policy. May change at runtime. Only
// uses the primary range, not the app request range.
- const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
+ const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock) {
+ return getMaxRefreshRateByPolicyLocked(mCurrentRefreshRate->getModeGroup());
+ }
+
+ const RefreshRate& getMaxRefreshRateByPolicyLocked(int anchorGroup) const REQUIRES(mLock);
// Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
// the policy.
@@ -446,6 +441,9 @@
float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&,
bool isSeamlessSwitch) const REQUIRES(mLock);
+ float calculateNonExactMatchingLayerScoreLocked(const LayerRequirement&,
+ const RefreshRate&) const REQUIRES(mLock);
+
void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
void initializeIdleTimer();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 208a767..23ebb06 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -16,15 +16,18 @@
#pragma once
+#include <chrono>
#include <numeric>
-#include "Fps.h"
+#include <android-base/stringprintf.h>
+#include <ftl/small_map.h>
+#include <utils/Timers.h>
+
+#include <scheduler/Fps.h>
+
#include "Scheduler/SchedulerUtils.h"
#include "TimeStats/TimeStats.h"
-#include "android-base/stringprintf.h"
-#include "utils/Timers.h"
-
namespace android::scheduler {
/**
@@ -40,6 +43,7 @@
static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
public:
+ // TODO(b/185535769): Inject clock to avoid sleeping in tests.
RefreshRateStats(TimeStats& timeStats, Fps currentRefreshRate,
android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
: mTimeStats(timeStats),
@@ -58,7 +62,7 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
void setRefreshRate(Fps currRefreshRate) {
- if (mCurrentRefreshRate.equalsWithMargin(currRefreshRate)) {
+ if (isApproxEqual(mCurrentRefreshRate, currRefreshRate)) {
return;
}
mTimeStats.incrementRefreshRateSwitches();
@@ -66,25 +70,26 @@
mCurrentRefreshRate = currRefreshRate;
}
- // Returns a map between human readable refresh rate and number of seconds the device spent in
- // that mode.
- std::unordered_map<std::string, int64_t> getTotalTimes() {
+ // Maps stringified refresh rate to total time spent in that mode.
+ using TotalTimes = ftl::SmallMap<std::string, std::chrono::milliseconds, 3>;
+
+ TotalTimes getTotalTimes() {
// If the power mode is on, then we are probably switching between the config modes. If
// it's not then the screen is probably off. Make sure to flush times before printing
// them.
flushTime();
- std::unordered_map<std::string, int64_t> totalTime;
- // Multiple configs may map to the same name, e.g. "60fps". Add the
- // times for such configs together.
- for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[to_string(configId)] = 0;
+ TotalTimes totalTimes = ftl::init::map("ScreenOff", mScreenOffTime);
+ const auto zero = std::chrono::milliseconds::zero();
+
+ // Sum the times for modes that map to the same name, e.g. "60 Hz".
+ for (const auto& [fps, time] : mFpsTotalTimes) {
+ const auto string = to_string(fps);
+ const auto total = std::as_const(totalTimes).get(string).value_or(std::cref(zero));
+ totalTimes.emplace_or_replace(string, total.get() + time);
}
- for (const auto& [configId, time] : mConfigModesTotalTime) {
- totalTime[to_string(configId)] += time;
- }
- totalTime["ScreenOff"] = mScreenOffTime;
- return totalTime;
+
+ return totalTimes;
}
// Traverses through the map of config modes and returns how long they've been running in easy
@@ -102,28 +107,32 @@
// Calculates the time that passed in ms between the last time we recorded time and the time
// this method was called.
void flushTime() {
- nsecs_t currentTime = systemTime();
- nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
- int64_t timeElapsedMs = ns2ms(timeElapsed);
+ const nsecs_t currentTime = systemTime();
+ const nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
mPreviousRecordedTime = currentTime;
+ const auto duration = std::chrono::milliseconds{ns2ms(timeElapsed)};
+ const auto zero = std::chrono::milliseconds::zero();
+
uint32_t fps = 0;
+
if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
// Normal power mode is counted under different config modes.
- if (mConfigModesTotalTime.find(mCurrentRefreshRate) == mConfigModesTotalTime.end()) {
- mConfigModesTotalTime[mCurrentRefreshRate] = 0;
- }
- mConfigModesTotalTime[mCurrentRefreshRate] += timeElapsedMs;
+ const auto total = std::as_const(mFpsTotalTimes)
+ .get(mCurrentRefreshRate)
+ .value_or(std::cref(zero));
+ mFpsTotalTimes.emplace_or_replace(mCurrentRefreshRate, total.get() + duration);
+
fps = static_cast<uint32_t>(mCurrentRefreshRate.getIntValue());
} else {
- mScreenOffTime += timeElapsedMs;
+ mScreenOffTime += duration;
}
mTimeStats.recordRefreshRate(fps, timeElapsed);
}
// Formats the time in milliseconds into easy to read format.
- static std::string getDateFormatFromMs(int64_t timeMs) {
- auto [days, dayRemainderMs] = std::div(timeMs, MS_PER_DAY);
+ static std::string getDateFormatFromMs(std::chrono::milliseconds time) {
+ auto [days, dayRemainderMs] = std::div(static_cast<int64_t>(time.count()), MS_PER_DAY);
auto [hours, hourRemainderMs] = std::div(dayRemainderMs, MS_PER_HOUR);
auto [mins, minsRemainderMs] = std::div(hourRemainderMs, MS_PER_MIN);
auto [sec, secRemainderMs] = std::div(minsRemainderMs, MS_PER_S);
@@ -138,9 +147,8 @@
Fps mCurrentRefreshRate;
android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
- std::unordered_map<Fps, int64_t /* duration in ms */, std::hash<Fps>, Fps::EqualsInBuckets>
- mConfigModesTotalTime;
- int64_t mScreenOffTime = 0;
+ ftl::SmallMap<Fps, std::chrono::milliseconds, 2, FpsApproxEqual> mFpsTotalTimes;
+ std::chrono::milliseconds mScreenOffTime = std::chrono::milliseconds::zero();
nsecs_t mPreviousRecordedTime = systemTime();
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1989d57..cbe4552 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -46,11 +46,8 @@
#include "OneShotTimer.h"
#include "SchedulerUtils.h"
#include "SurfaceFlingerProperties.h"
-#include "Timer.h"
-#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
-#include "VsyncController.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -60,74 +57,14 @@
} \
} while (false)
-using namespace std::string_literals;
+namespace android::scheduler {
-namespace android {
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
+ : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
-using gui::WindowInfo;
-
-namespace {
-
-std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
- // TODO(b/144707443): Tune constants.
- constexpr int kDefaultRate = 60;
- constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
- constexpr nsecs_t idealPeriod =
- std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
- constexpr size_t vsyncTimestampHistorySize = 20;
- constexpr size_t minimumSamplesForPrediction = 6;
- constexpr uint32_t discardOutlierPercent = 20;
- return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
- minimumSamplesForPrediction,
- discardOutlierPercent);
-}
-
-std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
- // TODO(b/144707443): Tune constants.
- constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
- constexpr std::chrono::nanoseconds timerSlack = 500us;
- return std::make_unique<
- scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
- timerSlack.count(), vsyncMoveThreshold.count());
-}
-
-const char* toContentDetectionString(bool useContentDetection) {
- return useContentDetection ? "on" : "off";
-}
-
-} // namespace
-
-class PredictedVsyncTracer {
-public:
- PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
- : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
- "PredictedVsyncTracer") {
- scheduleRegistration();
- }
-
-private:
- TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
- scheduler::VSyncCallbackRegistration mRegistration;
-
- void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
-
- void callback() {
- mParity = !mParity;
- scheduleRegistration();
- }
-};
-
-Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& callback)
- : Scheduler(configs, callback,
- {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
-}
-
-Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& callback, Options options)
- : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback,
- createLayerHistory(), options) {
+void Scheduler::startTimers() {
using namespace sysprop;
+ using namespace std::string_literals;
if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
@@ -147,22 +84,6 @@
}
}
-Scheduler::Scheduler(VsyncSchedule schedule,
- const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& schedulerCallback,
- std::unique_ptr<LayerHistory> layerHistory, Options options)
- : mOptions(options),
- mVsyncSchedule(std::move(schedule)),
- mLayerHistory(std::move(layerHistory)),
- mSchedulerCallback(schedulerCallback),
- mPredictedVsyncTracer(
- base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
- : nullptr) {
- setRefreshRateConfigs(configs);
- mSchedulerCallback.setVsyncEnabled(false);
-}
-
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
mDisplayPowerTimer.reset();
@@ -170,27 +91,20 @@
mRefreshRateConfigs.reset();
}
-Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
- auto clock = std::make_unique<scheduler::SystemClock>();
- auto tracker = createVSyncTracker();
- auto dispatch = createVSyncDispatch(*tracker);
-
- // TODO(b/144707443): Tune constants.
- constexpr size_t pendingFenceLimit = 20;
- auto controller =
- std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
- supportKernelTimer);
- return {std::move(controller), std::move(tracker), std::move(dispatch)};
+void Scheduler::run() {
+ while (true) {
+ waitMessage();
+ }
}
-std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() {
- return std::make_unique<scheduler::LayerHistory>();
+void Scheduler::createVsyncSchedule(FeatureFlags features) {
+ mVsyncSchedule.emplace(features);
}
std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
const char* name, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration, bool traceVsync) {
- return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+ return std::make_unique<scheduler::DispSyncSource>(getVsyncDispatch(), workDuration,
readyDuration, traceVsync, name);
}
@@ -206,14 +120,14 @@
{
const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
if (iter != mFrameRateOverridesFromBackdoor.end()) {
- return std::make_optional<Fps>(iter->second);
+ return iter->second;
}
}
{
const auto iter = mFrameRateOverridesByContent.find(uid);
if (iter != mFrameRateOverridesByContent.end()) {
- return std::make_optional<Fps>(iter->second);
+ return iter->second;
}
}
@@ -226,7 +140,7 @@
return true;
}
- return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+ return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -261,7 +175,7 @@
};
}
-Scheduler::ConnectionHandle Scheduler::createConnection(
+ConnectionHandle Scheduler::createConnection(
const char* connectionName, frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
@@ -275,7 +189,7 @@
return createConnection(std::move(eventThread));
}
-Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
+ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
@@ -362,24 +276,24 @@
void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
// Cache the last reported modes for primary display.
- mFeatures.cachedModeChangedParams = {handle, mode};
+ mPolicy.cachedModeChangedParams = {handle, mode};
// Invalidate content based refresh rate selection so it could be calculated
// again for the new refresh rate.
- mFeatures.contentRequirements.clear();
+ mPolicy.contentRequirements.clear();
}
onNonPrimaryDisplayModeChanged(handle, mode);
}
void Scheduler::dispatchCachedReportedMode() {
// Check optional fields first.
- if (!mFeatures.mode) {
+ if (!mPolicy.mode) {
ALOGW("No mode ID found, not dispatching cached mode.");
return;
}
- if (!mFeatures.cachedModeChangedParams.has_value()) {
+ if (!mPolicy.cachedModeChangedParams) {
ALOGW("No mode changed params found, not dispatching cached mode.");
return;
}
@@ -388,18 +302,18 @@
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
if (std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) {
+ mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mPolicy.mode) {
return;
}
// If there is no change from cached mode, there is no need to dispatch an event
- if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) {
+ if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
return;
}
- mFeatures.cachedModeChangedParams->mode = mFeatures.mode;
- onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
- mFeatures.cachedModeChangedParams->mode);
+ mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+ onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
+ mPolicy.cachedModeChangedParams->mode);
}
void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
@@ -440,12 +354,12 @@
}
DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
- const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
- const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+ const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
+ const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
}
-Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
if (mInjectVSyncs == enable) {
return {};
}
@@ -486,7 +400,7 @@
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mVsyncSchedule.tracker->resetModel();
+ mVsyncSchedule->getTracker().resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -539,10 +453,10 @@
void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
- mVsyncSchedule.controller->startPeriodTransition(period);
+ mVsyncSchedule->getController().startPeriodTransition(period);
if (!mPrimaryHWVsyncEnabled) {
- mVsyncSchedule.tracker->resetModel();
+ mVsyncSchedule->getTracker().resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -555,8 +469,9 @@
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
- periodFlushed);
+ needsHwVsync =
+ mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+ periodFlushed);
}
}
@@ -567,24 +482,23 @@
}
}
-void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
+void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
+ if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
-void Scheduler::setIgnorePresentFences(bool ignore) {
- mVsyncSchedule.controller->setIgnorePresentFences(ignore);
-}
-
void Scheduler::registerLayer(Layer* layer) {
+ using WindowType = gui::WindowInfo::Type;
+
scheduler::LayerHistory::LayerVoteType voteType;
- if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) {
+ if (!mFeatures.test(Feature::kContentDetection) ||
+ layer->getWindowType() == WindowType::STATUS_BAR) {
voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
- } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) {
+ } else if (layer->getWindowType() == WindowType::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
voteType = scheduler::LayerHistory::LayerVoteType::Min;
} else {
@@ -594,11 +508,11 @@
// If the content detection feature is off, we still keep the layer history,
// since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory->registerLayer(layer, voteType);
+ mLayerHistory.registerLayer(layer, voteType);
}
void Scheduler::deregisterLayer(Layer* layer) {
- mLayerHistory->deregisterLayer(layer);
+ mLayerHistory.deregisterLayer(layer);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
@@ -608,11 +522,11 @@
if (!mRefreshRateConfigs->canSwitch()) return;
}
- mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+ mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
void Scheduler::setModeChangePending(bool pending) {
- mLayerHistory->setModeChangePending(pending);
+ mLayerHistory.setModeChangePending(pending);
}
void Scheduler::chooseRefreshRateForContent() {
@@ -625,19 +539,19 @@
const auto refreshRateConfigs = holdRefreshRateConfigs();
scheduler::LayerHistory::Summary summary =
- mLayerHistory->summarize(*refreshRateConfigs, systemTime());
+ mLayerHistory.summarize(*refreshRateConfigs, systemTime());
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
DisplayModePtr newMode;
bool frameRateChanged;
bool frameRateOverridesChanged;
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.contentRequirements = summary;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.contentRequirements = summary;
newMode = calculateRefreshRateModeId(&consideredSignals);
frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
- if (mFeatures.mode == newMode) {
+ if (mPolicy.mode == newMode) {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
@@ -645,15 +559,16 @@
}
frameRateChanged = false;
} else {
- mFeatures.mode = newMode;
+ mPolicy.mode = newMode;
frameRateChanged = true;
}
}
if (frameRateChanged) {
- auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+ const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+
mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ModeEvent::None
- : ModeEvent::Changed);
+ consideredSignals.idle ? DisplayModeEvent::None
+ : DisplayModeEvent::Changed);
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -665,7 +580,7 @@
mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
}
-void Scheduler::notifyTouchEvent() {
+void Scheduler::onTouchHint() {
if (mTouchTimer) {
mTouchTimer->reset();
@@ -676,8 +591,8 @@
void Scheduler::setDisplayPowerState(bool normal) {
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.isDisplayPowerStateNormal = normal;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.isDisplayPowerStateNormal = normal;
}
if (mDisplayPowerTimer) {
@@ -686,7 +601,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory->clear();
+ mLayerHistory.clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -699,15 +614,16 @@
return mRefreshRateConfigs->getCurrentRefreshRate();
}();
- constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER{65.0f};
- if (state == TimerState::Reset &&
- refreshRate.getFps().greaterThanWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+ constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz;
+ using namespace fps_approx_ops;
+
+ if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
} else if (state == TimerState::Expired &&
- refreshRate.getFps().lessThanOrEqualWithMargin(FPS_THRESHOLD_FOR_KERNEL_TIMER)) {
+ refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the VsyncController model anyway.
@@ -718,7 +634,7 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state);
+ handleTimerStateChanged(&mPolicy.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
@@ -728,14 +644,14 @@
// Clear layer history to get fresh FPS detection.
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
- if (handleTimerStateChanged(&mFeatures.touch, touch)) {
- mLayerHistory->clear();
+ if (handleTimerStateChanged(&mPolicy.touch, touch)) {
+ mLayerHistory.clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
+ handleTimerStateChanged(&mPolicy.displayPowerTimer, state);
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
@@ -745,8 +661,8 @@
StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Content detection: %s %s\n\n",
- toContentDetectionString(mOptions.useContentDetection),
- mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+ mFeatures.test(Feature::kContentDetection) ? "on" : "off",
+ mLayerHistory.dump().c_str());
{
std::lock_guard lock(mFrameRateOverridesLock);
@@ -771,13 +687,8 @@
}
}
-void Scheduler::dumpVsync(std::string& s) const {
- using base::StringAppendF;
-
- StringAppendF(&s, "VSyncReactor:\n");
- mVsyncSchedule.controller->dump(s);
- StringAppendF(&s, "VSyncDispatch:\n");
- mVsyncSchedule.dispatch->dump(s);
+void Scheduler::dumpVsync(std::string& out) const {
+ mVsyncSchedule->dump(out);
}
bool Scheduler::updateFrameRateOverrides(
@@ -789,14 +700,13 @@
if (!consideredSignals.idle) {
const auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements,
- displayRefreshRate,
- consideredSignals.touch);
+ refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
+ displayRefreshRate, consideredSignals);
std::lock_guard lock(mFrameRateOverridesLock);
if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
frameRateOverrides.begin(), frameRateOverrides.end(),
- [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
- return a.first == b.first && a.second.equalsWithMargin(b.second);
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first == rhs.first && isApproxEqual(lhs.second, rhs.second);
})) {
mFrameRateOverridesByContent = frameRateOverrides;
return true;
@@ -813,31 +723,30 @@
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
const auto refreshRateConfigs = holdRefreshRateConfigs();
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
if (*currentState == newState) {
return false;
}
*currentState = newState;
newMode = calculateRefreshRateModeId(&consideredSignals);
frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
- if (mFeatures.mode == newMode) {
+ if (mPolicy.mode == newMode) {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
dispatchCachedReportedMode();
}
} else {
- mFeatures.mode = newMode;
+ mPolicy.mode = newMode;
refreshRateChanged = true;
}
}
if (refreshRateChanged) {
- const RefreshRate& newRefreshRate =
- refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+ const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ModeEvent::None
- : ModeEvent::Changed);
+ consideredSignals.idle ? DisplayModeEvent::None
+ : DisplayModeEvent::Changed);
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -854,32 +763,31 @@
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
if (mDisplayPowerTimer &&
- (!mFeatures.isDisplayPowerStateNormal ||
- mFeatures.displayPowerTimer == TimerState::Reset)) {
+ (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode();
}
- const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
- const bool idle = mFeatures.idleTimer == TimerState::Expired;
+ const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active;
+ const bool idle = mPolicy.idleTimer == TimerState::Expired;
return refreshRateConfigs
- ->getBestRefreshRate(mFeatures.contentRequirements,
- {.touch = touchActive, .idle = idle}, consideredSignals)
+ ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle},
+ consideredSignals)
.getMode();
}
DisplayModePtr Scheduler::getPreferredDisplayMode() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
// Make sure that the default mode ID is first updated, before returned.
- if (mFeatures.mode) {
- mFeatures.mode = calculateRefreshRateModeId();
+ if (mPolicy.mode) {
+ mPolicy.mode = calculateRefreshRateModeId();
}
- return mFeatures.mode;
+ return mPolicy.mode;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
if (timeline.refreshRequired) {
- mSchedulerCallback.repaintEverythingForHWC();
+ mSchedulerCallback.scheduleComposite(FrameHint::kNone);
}
std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
@@ -891,27 +799,27 @@
}
}
-void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
- bool callRepaint = false;
- {
+void Scheduler::onPostComposition(nsecs_t presentTime) {
+ const bool recomposite = [=] {
std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
- if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
- mLastVsyncPeriodChangeTimeline->refreshRequired = false;
- } else {
- // We need to send another refresh as refreshTimeNanos is still in the future
- callRepaint = true;
+ if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) {
+ // We need to composite again as refreshTimeNanos is still in the future.
+ return true;
}
- }
- }
- if (callRepaint) {
- mSchedulerCallback.repaintEverythingForHWC();
+ mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+ }
+ return false;
+ }();
+
+ if (recomposite) {
+ mSchedulerCallback.scheduleComposite(FrameHint::kNone);
}
}
void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) {
- mLayerHistory->setDisplayArea(displayArea);
+ mLayerHistory.setDisplayArea(displayArea);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
@@ -921,7 +829,8 @@
std::lock_guard lock(mFrameRateOverridesLock);
if (frameRateOverride.frameRateHz != 0.f) {
- mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] =
+ Fps::fromValue(frameRateOverride.frameRateHz);
} else {
mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
}
@@ -930,8 +839,8 @@
std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
nsecs_t expectedPresentTime) const {
const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
- const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod());
+ const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
}
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index bbbbca5..818f1ed 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -18,6 +18,7 @@
#include <atomic>
#include <functional>
+#include <future>
#include <memory>
#include <mutex>
#include <optional>
@@ -30,36 +31,37 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <scheduler/Features.h>
+
#include "EventThread.h"
#include "LayerHistory.h"
+#include "MessageQueue.h"
#include "OneShotTimer.h"
#include "RefreshRateConfigs.h"
#include "SchedulerUtils.h"
+#include "VsyncSchedule.h"
namespace android {
-using namespace std::chrono_literals;
-using scheduler::LayerHistory;
-
class FenceTime;
class InjectVSyncSource;
-class PredictedVsyncTracer;
-
-namespace scheduler {
-class VsyncController;
-class VSyncDispatch;
-class VSyncTracker;
-} // namespace scheduler
namespace frametimeline {
class TokenManager;
} // namespace frametimeline
+namespace scheduler {
+
struct ISchedulerCallback {
+ // Indicates frame activity, i.e. whether commit and/or composite is taking place.
+ enum class FrameHint { kNone, kActive };
+
+ using RefreshRate = RefreshRateConfigs::RefreshRate;
+ using DisplayModeEvent = scheduler::DisplayModeEvent;
+
+ virtual void scheduleComposite(FrameHint) = 0;
virtual void setVsyncEnabled(bool) = 0;
- virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent) = 0;
- virtual void repaintEverythingForHWC() = 0;
+ virtual void changeRefreshRate(const RefreshRate&, DisplayModeEvent) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
@@ -67,15 +69,33 @@
~ISchedulerCallback() = default;
};
-class Scheduler {
-public:
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
- using ModeEvent = scheduler::RefreshRateConfigEvent;
+class Scheduler : impl::MessageQueue {
+ using Impl = impl::MessageQueue;
- Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&);
+public:
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
~Scheduler();
- using ConnectionHandle = scheduler::ConnectionHandle;
+ void createVsyncSchedule(FeatureFlags);
+ void startTimers();
+ void run();
+
+ using Impl::initVsync;
+ using Impl::setInjector;
+
+ using Impl::getScheduledFrameTime;
+ using Impl::setDuration;
+
+ using Impl::scheduleFrame;
+
+ // Schedule an asynchronous or synchronous task on the main thread.
+ template <typename F, typename T = std::invoke_result_t<F>>
+ [[nodiscard]] std::future<T> schedule(F&& f) {
+ auto [task, future] = makeTask(std::move(f));
+ postMessage(std::move(task));
+ return std::move(future);
+ }
+
ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration,
@@ -87,7 +107,7 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock);
+ void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -120,8 +140,7 @@
// VsyncController detected that the vsync period changed, and false otherwise.
void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed);
- void addPresentFence(const std::shared_ptr<FenceTime>&);
- void setIgnorePresentFences(bool ignore);
+ void addPresentFence(std::shared_ptr<FenceTime>);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
@@ -135,12 +154,12 @@
void resetIdleTimer();
- // Function that resets the touch timer.
- void notifyTouchEvent();
+ // Indicates that touch interaction is taking place.
+ void onTouchHint();
void setDisplayPowerState(bool normal);
- scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+ VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
@@ -159,8 +178,8 @@
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
- // Notifies the scheduler when the display was refreshed
- void onDisplayRefreshed(nsecs_t timestamp);
+ // Notifies the scheduler post composition.
+ void onPostComposition(nsecs_t presentTime);
// Notifies the scheduler when the display size has changed. Called from SF's main thread
void onActiveDisplayAreaChanged(uint32_t displayArea);
@@ -179,7 +198,7 @@
std::optional<Fps> getFrameRateOverride(uid_t uid) const
EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
- void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs)
+ void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> refreshRateConfigs)
EXCLUDES(mRefreshRateConfigsLock) {
// We need to stop the idle timer on the previous RefreshRateConfigs instance
// and cleanup the scheduler's state before we switch to the other RefreshRateConfigs.
@@ -188,8 +207,8 @@
if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer();
}
{
- std::scoped_lock lock(mFeatureStateLock);
- mFeatures = {};
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
}
{
std::scoped_lock lock(mRefreshRateConfigsLock);
@@ -214,36 +233,20 @@
return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
}
+ // Returns the framerate of the layer with the given sequence ID
+ float getLayerFramerate(nsecs_t now, int32_t id) const {
+ return mLayerHistory.getLayerFramerate(now, id);
+ }
+
private:
friend class TestableScheduler;
- // In order to make sure that the features don't override themselves, we need a state machine
- // to keep track which feature requested the config change.
+ using FrameHint = ISchedulerCallback::FrameHint;
+
enum class ContentDetectionState { Off, On };
enum class TimerState { Reset, Expired };
enum class TouchState { Inactive, Active };
- struct Options {
- // Whether to use content detection at all.
- bool useContentDetection;
- };
-
- struct VsyncSchedule {
- std::unique_ptr<scheduler::VsyncController> controller;
- std::unique_ptr<scheduler::VSyncTracker> tracker;
- std::unique_ptr<scheduler::VSyncDispatch> dispatch;
- };
-
- // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
- Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options);
-
- // Used by tests to inject mocks.
- Scheduler(VsyncSchedule, const std::shared_ptr<scheduler::RefreshRateConfigs>&,
- ISchedulerCallback&, std::unique_ptr<LayerHistory>, Options);
-
- static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
- static std::unique_ptr<LayerHistory> createLayerHistory();
-
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(
@@ -265,19 +268,17 @@
// selection were initialized, prioritizes them, and calculates the DisplayModeId
// for the suggested refresh rate.
DisplayModePtr calculateRefreshRateModeId(
- scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
- REQUIRES(mFeatureStateLock);
+ RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock);
- void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock);
- bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
- Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
- EXCLUDES(mFrameRateOverridesLock);
+ void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+ bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
+ REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock);
impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
EXCLUDES(mRefreshRateConfigsLock);
impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
- std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const
+ std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
EXCLUDES(mRefreshRateConfigsLock) {
std::scoped_lock lock(mRefreshRateConfigsLock);
return mRefreshRateConfigs;
@@ -303,66 +304,63 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
- const Options mOptions;
- VsyncSchedule mVsyncSchedule;
+ const FeatureFlags mFeatures;
+ std::optional<VsyncSchedule> mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
- std::unique_ptr<LayerHistory> mLayerHistory;
+ LayerHistory mLayerHistory;
// Timer used to monitor touch events.
- std::optional<scheduler::OneShotTimer> mTouchTimer;
+ std::optional<OneShotTimer> mTouchTimer;
// Timer used to monitor display power mode.
- std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
+ std::optional<OneShotTimer> mDisplayPowerTimer;
ISchedulerCallback& mSchedulerCallback;
- // In order to make sure that the features don't override themselves, we need a state machine
- // to keep track which feature requested the config change.
- mutable std::mutex mFeatureStateLock;
+ mutable std::mutex mPolicyLock;
struct {
+ // Policy for choosing the display mode.
+ LayerHistory::Summary contentRequirements;
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
-
- DisplayModePtr mode;
- LayerHistory::Summary contentRequirements;
-
bool isDisplayPowerStateNormal = true;
- // Used to cache the last parameters of onPrimaryDisplayModeChanged
+ // Chosen display mode.
+ DisplayModePtr mode;
+
struct ModeChangedParams {
ConnectionHandle handle;
DisplayModePtr mode;
};
+ // Parameters for latest dispatch of mode change event.
std::optional<ModeChangedParams> cachedModeChangedParams;
- } mFeatures GUARDED_BY(mFeatureStateLock);
+ } mPolicy GUARDED_BY(mPolicyLock);
mutable std::mutex mRefreshRateConfigsLock;
- std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs
- GUARDED_BY(mRefreshRateConfigsLock);
+ std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
- const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
-
// The frame rate override lists need their own mutex as they are being read
// by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
mutable std::mutex mFrameRateOverridesLock;
// mappings between a UID and a preferred refresh rate that this app would
// run at.
- scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
+ RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
GUARDED_BY(mFrameRateOverridesLock);
- scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+ RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
GUARDED_BY(mFrameRateOverridesLock);
// Keeps track of whether the screen is acquired for debug
std::atomic<bool> mScreenAcquired = false;
};
+} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index c9c2d84..22c3a70 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -17,14 +17,18 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerTimer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <android-base/stringprintf.h>
-#include <log/log.h>
+
+#include <chrono>
+#include <cstdint>
+
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <log/log.h>
#include <utils/Trace.h>
-#include <chrono>
-#include <cstdint>
#include "SchedulerUtils.h"
#include "Timer.h"
@@ -215,26 +219,9 @@
mDebugState = state;
}
-const char* Timer::strDebugState(DebugState state) const {
- switch (state) {
- case DebugState::Reset:
- return "Reset";
- case DebugState::Running:
- return "Running";
- case DebugState::Waiting:
- return "Waiting";
- case DebugState::Reading:
- return "Reading";
- case DebugState::InCallback:
- return "InCallback";
- case DebugState::Terminated:
- return "Terminated";
- }
-}
-
void Timer::dump(std::string& result) const {
std::lock_guard lock(mMutex);
- StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
+ StringAppendF(&result, "\t\tDebugState: %s\n", ftl::enum_string(mDebugState).c_str());
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index 69ce079..628d800 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -37,11 +37,20 @@
void dump(std::string& result) const final;
private:
- enum class DebugState { Reset, Running, Waiting, Reading, InCallback, Terminated };
+ enum class DebugState {
+ Reset,
+ Running,
+ Waiting,
+ Reading,
+ InCallback,
+ Terminated,
+
+ ftl_last = Terminated
+ };
+
void reset();
void cleanup();
void setDebugState(DebugState state) EXCLUDES(mMutex);
- const char* strDebugState(DebugState state) const;
int mTimerFd = -1;
int mEpollFd = -1;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index ee973f7..1c9de1c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -47,7 +47,7 @@
VSyncReactor::~VSyncReactor() = default;
-bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
if (!fence) {
return false;
}
@@ -80,7 +80,7 @@
if (mPendingLimit == mUnfiredFences.size()) {
mUnfiredFences.erase(mUnfiredFences.begin());
}
- mUnfiredFences.push_back(fence);
+ mUnfiredFences.push_back(std::move(fence));
} else {
timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 449d4c3..a9d536b 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -37,7 +37,7 @@
bool supportKernelIdleTimer);
~VSyncReactor();
- bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+ bool addPresentFence(std::shared_ptr<FenceTime>) final;
void setIgnorePresentFences(bool ignore) final;
void startPeriodTransition(nsecs_t period) final;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 95750ad..76315d2 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,7 +17,9 @@
#pragma once
#include <utils/Timers.h>
-#include "Fps.h"
+
+#include <scheduler/Fps.h>
+
#include "VSyncDispatch.h"
namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index 43e0297..ff31651 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -48,14 +48,12 @@
}
PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
- const auto iter = mOffsetsCache.find(fps);
- if (iter != mOffsetsCache.end()) {
- return iter->second;
+ if (const auto offsets = mOffsetsCache.get(fps)) {
+ return offsets->get();
}
- const auto offset = constructOffsets(fps.getPeriodNsecs());
- mOffsetsCache[fps] = offset;
- return offset;
+ const auto [it, _] = mOffsetsCache.try_emplace(fps, constructOffsets(fps.getPeriodNsecs()));
+ return it->second;
}
void VsyncConfiguration::dump(std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 3e53b3f..02ebd70 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -17,13 +17,14 @@
#pragma once
#include <mutex>
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
+#include <optional>
+#include <string>
+#include <ftl/small_map.h>
#include <utils/Timers.h>
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
#include "VsyncModulator.h"
namespace android::scheduler {
@@ -88,9 +89,8 @@
VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
- mutable std::unordered_map<Fps, VsyncConfigSet, std::hash<Fps>, Fps::EqualsInBuckets>
- mOffsetsCache GUARDED_BY(mLock);
- std::atomic<Fps> mRefreshRateFps GUARDED_BY(mLock);
+ mutable ftl::SmallMap<Fps, VsyncConfigSet, 2, FpsApproxEqual> mOffsetsCache GUARDED_BY(mLock);
+ Fps mRefreshRateFps GUARDED_BY(mLock);
mutable std::mutex mLock;
};
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 0f0df22..59f6537 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -17,19 +17,15 @@
#pragma once
#include <cstddef>
+#include <memory>
+#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <ui/FenceTime.h>
-
-#include <memory>
-
namespace android::scheduler {
-class FenceTime;
-
class VsyncController {
public:
virtual ~VsyncController();
@@ -43,7 +39,7 @@
* an accurate prediction,
* False otherwise
*/
- virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+ virtual bool addPresentFence(std::shared_ptr<FenceTime>) = 0;
/*
* Adds a hw sync timestamp to the model. The controller will use the timestamp
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index b2b0451..2000c54 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,6 +25,8 @@
#include <binder/IBinder.h>
#include <utils/Timers.h>
+#include "../WpHash.h"
+
namespace android::scheduler {
// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
@@ -124,12 +126,6 @@
using Schedule = TransactionSchedule;
std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
-
std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
std::atomic<bool> mRefreshRateChangePending = false;
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
new file mode 100644
index 0000000..77d1223
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#include <scheduler/Fps.h>
+
+#include "VsyncSchedule.h"
+
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
+
+#include "../TracedOrdinal.h"
+
+namespace android::scheduler {
+
+class VsyncSchedule::PredictedVsyncTracer {
+ // Invoked from the thread of the VsyncDispatch owned by this VsyncSchedule.
+ constexpr auto makeVsyncCallback() {
+ return [this](nsecs_t, nsecs_t, nsecs_t) {
+ mParity = !mParity;
+ schedule();
+ };
+ }
+
+public:
+ explicit PredictedVsyncTracer(VsyncDispatch& dispatch)
+ : mRegistration(dispatch, makeVsyncCallback(), __func__) {
+ schedule();
+ }
+
+private:
+ void schedule() { mRegistration.schedule({0, 0, 0}); }
+
+ TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+ VSyncCallbackRegistration mRegistration;
+};
+
+VsyncSchedule::VsyncSchedule(FeatureFlags features)
+ : mTracker(createTracker()),
+ mDispatch(createDispatch(*mTracker)),
+ mController(createController(*mTracker, features)) {
+ if (features.test(Feature::kTracePredictedVsync)) {
+ mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch);
+ }
+}
+
+VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller)
+ : mTracker(std::move(tracker)),
+ mDispatch(std::move(dispatch)),
+ mController(std::move(controller)) {}
+
+VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
+VsyncSchedule::~VsyncSchedule() = default;
+
+void VsyncSchedule::dump(std::string& out) const {
+ out.append("VsyncController:\n");
+ mController->dump(out);
+
+ out.append("VsyncDispatch:\n");
+ mDispatch->dump(out);
+}
+
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() {
+ // TODO(b/144707443): Tune constants.
+ constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
+ constexpr size_t kHistorySize = 20;
+ constexpr size_t kMinSamplesForPrediction = 6;
+ constexpr uint32_t kDiscardOutlierPercent = 20;
+
+ return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction,
+ kDiscardOutlierPercent);
+}
+
+VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
+ using namespace std::chrono_literals;
+
+ // TODO(b/144707443): Tune constants.
+ constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
+ constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
+
+ return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ kGroupDispatchWithin.count(),
+ kSnapToSameVsyncWithin.count());
+}
+
+VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker,
+ FeatureFlags features) {
+ // TODO(b/144707443): Tune constants.
+ constexpr size_t kMaxPendingFences = 20;
+ const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer);
+
+ auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker,
+ kMaxPendingFences, hasKernelIdleTimer);
+
+ reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences));
+ return reactor;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
new file mode 100644
index 0000000..0d9b114
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <memory>
+#include <string>
+
+#include <scheduler/Features.h>
+
+namespace android::scheduler {
+
+// TODO(b/185535769): Rename classes, and remove aliases.
+class VSyncDispatch;
+class VSyncTracker;
+
+class VsyncController;
+using VsyncDispatch = VSyncDispatch;
+using VsyncTracker = VSyncTracker;
+
+// Schedule that synchronizes to hardware VSYNC of a physical display.
+class VsyncSchedule {
+public:
+ explicit VsyncSchedule(FeatureFlags);
+ VsyncSchedule(VsyncSchedule&&);
+ ~VsyncSchedule();
+
+ // TODO(b/185535769): Hide behind API.
+ const VsyncTracker& getTracker() const { return *mTracker; }
+ VsyncTracker& getTracker() { return *mTracker; }
+ VsyncController& getController() { return *mController; }
+
+ // TODO(b/185535769): Remove once VsyncSchedule owns all registrations.
+ VsyncDispatch& getDispatch() { return *mDispatch; }
+
+ void dump(std::string&) const;
+
+private:
+ friend class TestableScheduler;
+
+ using TrackerPtr = std::unique_ptr<VsyncTracker>;
+ using DispatchPtr = std::unique_ptr<VsyncDispatch>;
+ using ControllerPtr = std::unique_ptr<VsyncController>;
+
+ // For tests.
+ VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr);
+
+ static TrackerPtr createTracker();
+ static DispatchPtr createDispatch(VsyncTracker&);
+ static ControllerPtr createController(VsyncTracker&, FeatureFlags);
+
+ class PredictedVsyncTracer;
+ using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
+
+ // Effectively const except in move constructor.
+ TrackerPtr mTracker;
+ DispatchPtr mDispatch;
+ ControllerPtr mController;
+ TracerPtr mTracer;
+};
+
+} // namespace android::scheduler
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
similarity index 60%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/Scheduler/include/scheduler/Features.h
index d2996d1..0e96678 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,21 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+#pragma once
-namespace android::ui {
+#include <ftl/Flags.h>
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+#include <cstdint>
-} // namespace android::ui
+namespace android::scheduler {
+
+enum class Feature : std::uint8_t {
+ kPresentFences = 0b1,
+ kKernelIdleTimer = 0b10,
+ kContentDetection = 0b100,
+ kTracePredictedVsync = 0b1000,
+};
+
+using FeatureFlags = Flags<Feature>;
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
new file mode 100644
index 0000000..639b3e5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -0,0 +1,128 @@
+/*
+ * 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 <cmath>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+#include <android-base/stringprintf.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+// Frames per second, stored as floating-point frequency. Provides conversion from/to period in
+// nanoseconds, and relational operators with precision threshold.
+//
+// const Fps fps = 60_Hz;
+//
+// using namespace fps_approx_ops;
+// assert(fps == Fps::fromPeriodNsecs(16'666'667));
+//
+class Fps {
+public:
+ constexpr Fps() = default;
+
+ static constexpr Fps fromValue(float frequency) {
+ return frequency > 0.f ? Fps(frequency, static_cast<nsecs_t>(1e9f / frequency)) : Fps();
+ }
+
+ static constexpr Fps fromPeriodNsecs(nsecs_t period) {
+ return period > 0 ? Fps(1e9f / period, period) : Fps();
+ }
+
+ constexpr bool isValid() const { return mFrequency > 0.f; }
+
+ constexpr float getValue() const { return mFrequency; }
+ int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }
+
+ constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }
+
+private:
+ constexpr Fps(float frequency, nsecs_t period) : mFrequency(frequency), mPeriod(period) {}
+
+ float mFrequency = 0.f;
+ nsecs_t mPeriod = 0;
+};
+
+static_assert(std::is_trivially_copyable_v<Fps>);
+
+constexpr Fps operator""_Hz(unsigned long long frequency) {
+ return Fps::fromValue(static_cast<float>(frequency));
+}
+
+constexpr Fps operator""_Hz(long double frequency) {
+ return Fps::fromValue(static_cast<float>(frequency));
+}
+
+inline bool isStrictlyLess(Fps lhs, Fps rhs) {
+ return lhs.getValue() < rhs.getValue();
+}
+
+// Does not satisfy equivalence relation.
+inline bool isApproxEqual(Fps lhs, Fps rhs) {
+ // TODO(b/185536303): Replace with ULP distance.
+ return std::abs(lhs.getValue() - rhs.getValue()) < 0.001f;
+}
+
+// Does not satisfy strict weak order.
+inline bool isApproxLess(Fps lhs, Fps rhs) {
+ return isStrictlyLess(lhs, rhs) && !isApproxEqual(lhs, rhs);
+}
+
+namespace fps_approx_ops {
+
+inline bool operator==(Fps lhs, Fps rhs) {
+ return isApproxEqual(lhs, rhs);
+}
+
+inline bool operator<(Fps lhs, Fps rhs) {
+ return isApproxLess(lhs, rhs);
+}
+
+inline bool operator!=(Fps lhs, Fps rhs) {
+ return !isApproxEqual(lhs, rhs);
+}
+
+inline bool operator>(Fps lhs, Fps rhs) {
+ return isApproxLess(rhs, lhs);
+}
+
+inline bool operator<=(Fps lhs, Fps rhs) {
+ return !isApproxLess(rhs, lhs);
+}
+
+inline bool operator>=(Fps lhs, Fps rhs) {
+ return !isApproxLess(lhs, rhs);
+}
+
+} // namespace fps_approx_ops
+
+struct FpsApproxEqual {
+ bool operator()(Fps lhs, Fps rhs) const { return isApproxEqual(lhs, rhs); }
+};
+
+inline std::string to_string(Fps fps) {
+ return base::StringPrintf("%.2f Hz", fps.getValue());
+}
+
+inline std::ostream& operator<<(std::ostream& stream, Fps fps) {
+ return stream << to_string(fps);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
similarity index 67%
rename from services/surfaceflinger/Scheduler/Seamlessness.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
index 3e42a4d..93bf726 100644
--- a/services/surfaceflinger/Scheduler/Seamlessness.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
@@ -16,11 +16,11 @@
#pragma once
-#include <cstring>
#include <ostream>
-namespace android {
-namespace scheduler {
+#include <ftl/enum.h>
+
+namespace android::scheduler {
// The seamlessness requirement of a Layer.
enum class Seamlessness {
@@ -31,24 +31,14 @@
// Indicates no preference for seamlessness. For such layers the system will
// prefer seamless switches, but also non-seamless switches to the group of the
// default config are allowed.
- Default
+ Default,
+
+ ftl_last = Default
};
-inline std::string toString(Seamlessness seamlessness) {
- switch (seamlessness) {
- case Seamlessness::OnlySeamless:
- return "OnlySeamless";
- case Seamlessness::SeamedAndSeamless:
- return "SeamedAndSeamless";
- case Seamlessness::Default:
- return "Default";
- }
-}
-
// Used by gtest
-inline std::ostream& operator<<(std::ostream& os, Seamlessness val) {
- return os << toString(val);
+inline std::ostream& operator<<(std::ostream& os, Seamlessness s) {
+ return os << ftl::enum_string(s);
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 758cc70..4f38588 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android/configuration.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/types.h>
@@ -51,7 +52,6 @@
#include <ftl/future.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerMetadata.h>
@@ -67,6 +67,7 @@
#include <renderengine/RenderEngine.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>
@@ -93,6 +94,7 @@
#include <type_traits>
#include <unordered_map>
+#include "BackgroundExecutor.h"
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -109,6 +111,7 @@
#include "DisplayRenderArea.h"
#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
+#include "FlagManager.h"
#include "FpsReporter.h"
#include "FrameTimeline/FrameTimeline.h"
#include "FrameTracer/FrameTracer.h"
@@ -124,7 +127,6 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
@@ -168,6 +170,8 @@
using android::hardware::power::Boost;
using base::StringAppendF;
+using gui::DisplayInfo;
+using gui::IDisplayEventConnection;
using gui::IWindowInfosListener;
using gui::WindowInfo;
using ui::ColorMode;
@@ -261,14 +265,6 @@
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
}
-class FrameRateFlexibilityToken : public BBinder {
-public:
- FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {}
- virtual ~FrameRateFlexibilityToken() { mCallback(); }
-
-private:
- std::function<void()> mCallback;
-};
enum Permission {
ACCESS_SURFACE_FLINGER = 0x1,
@@ -332,15 +328,13 @@
uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::hasWideColorDisplay;
ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
-bool SurfaceFlinger::useColorManagement;
bool SurfaceFlinger::useContextPriority;
Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::useFrameRateApi;
bool SurfaceFlinger::enableSdrDimming;
-bool SurfaceFlinger::enableLatchUnsignaled;
+LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
@@ -366,11 +360,11 @@
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
+ mPid(getpid()),
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
- mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
- mEventQueue(mFactory.createMessageQueue()),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
@@ -396,11 +390,6 @@
maxGraphicsHeight = std::max(max_graphics_height(0), 0);
hasWideColorDisplay = has_wide_color_display(false);
-
- // Android 12 and beyond, color management in display pipeline is turned on
- // by default.
- useColorManagement = use_color_management(true);
-
mDefaultCompositionDataspace =
static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
mWideColorGamutCompositionDataspace = static_cast<ui::Dataspace>(wcg_composition_dataspace(
@@ -450,10 +439,7 @@
property_get("ro.build.type", value, "user");
mIsUserBuild = strcmp(value, "user") == 0;
- property_get("debug.sf.showupdates", value, "0");
- mDebugRegion = atoi(value);
-
- ALOGI_IF(mDebugRegion, "showupdates enabled");
+ mDebugFlashDelay = base::GetUintProperty("debug.sf.showupdates"s, 0u);
// DDMS debugging deprecated (b/120782499)
property_get("debug.sf.ddms", value, "0");
@@ -501,28 +487,38 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- useFrameRateApi = use_frame_rate_api(true);
-
mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
// Debug property overrides ro. property
enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
- enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false);
+ enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
+
+ mTransactionTracingEnabled =
+ !mIsUserBuild && property_get_bool("debug.sf.enable_transaction_tracing", true);
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.enable();
+ }
+}
+
+LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
+ if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
+ return LatchUnsignaledConfig::Always;
+ } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+ return LatchUnsignaledConfig::Auto;
+ } else {
+ return LatchUnsignaledConfig::Disabled;
+ }
}
SurfaceFlinger::~SurfaceFlinger() = default;
-void SurfaceFlinger::onFirstRef() {
- mEventQueue->init(this);
-}
-
void SurfaceFlinger::binderDied(const wp<IBinder>&) {
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
- // Sever the link to inputflinger since its gone as well.
- static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+ // Sever the link to inputflinger since it's gone as well.
+ static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; }));
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -532,16 +528,7 @@
}
void SurfaceFlinger::run() {
- while (true) {
- mEventQueue->waitMessage();
- }
-}
-
-template <typename F, typename T>
-inline std::future<T> SurfaceFlinger::schedule(F&& f) {
- auto [task, future] = makeTask(std::move(f));
- mEventQueue->postMessage(std::move(task));
- return std::move(future);
+ mScheduler->run();
}
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
@@ -650,17 +637,20 @@
}
std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIdsLocked() const {
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- return {};
- }
-
std::vector<PhysicalDisplayId> displayIds;
displayIds.reserve(mPhysicalDisplayTokens.size());
- displayIds.push_back(display->getPhysicalId());
+ const auto defaultDisplayId = [this]() REQUIRES(mStateLock) {
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ return display->getPhysicalId();
+ }
+
+ // fallback to the internal display id if the active display is unknown
+ return getInternalDisplayIdLocked();
+ }();
+ displayIds.push_back(defaultDisplayId);
for (const auto& [id, token] : mPhysicalDisplayTokens) {
- if (id != display->getPhysicalId()) {
+ if (id != defaultDisplayId) {
displayIds.push_back(id);
}
}
@@ -670,12 +660,7 @@
status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const {
Mutex::Autolock lock(mStateLock);
- const auto display = getInternalDisplayIdLocked();
- if (!display) {
- return NAME_NOT_FOUND;
- }
-
- *id = *display;
+ *id = getInternalDisplayIdLocked();
return NO_ERROR;
}
@@ -721,6 +706,8 @@
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
+ mFlagManager = std::make_unique<android::FlagManager>();
+ mPowerAdvisor.enablePowerHint(mFlagManager->use_adpf_cpu_hint());
mFrameTracer->initialize();
mFrameTimeline->onBootFinished();
@@ -742,7 +729,7 @@
sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
@@ -783,7 +770,7 @@
if (std::this_thread::get_id() == mMainThreadId) {
return genTextures();
} else {
- return schedule(genTextures).get();
+ return mScheduler->schedule(genTextures).get();
}
}
@@ -795,6 +782,25 @@
ATRACE_INT("TexturePoolSize", mTexturePool.size());
}
+static std::optional<renderengine::RenderEngine::RenderEngineType>
+chooseRenderEngineTypeViaSysProp() {
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+
+ 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) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ } else if (strcmp(prop, "skiaglthreaded") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+ } else {
+ ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
+ return {};
+ }
+}
+
// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
@@ -805,19 +811,21 @@
// Get a RenderEngine for the given display / config (can't fail)
// TODO(b/77156734): We need to stop casting and use HAL types when possible.
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
- mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
- .setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
- .setEnableProtectedContext(enable_protected_contents(false))
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
- .setContextPriority(
- useContextPriority
- ? renderengine::RenderEngine::ContextPriority::REALTIME
- : renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build()));
+ auto builder = renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+ .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+ .setUseColorManagerment(useColorManagement)
+ .setEnableProtectedContext(enable_protected_contents(false))
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mSupportsBlur)
+ .setContextPriority(
+ useContextPriority
+ ? renderengine::RenderEngine::ContextPriority::REALTIME
+ : renderengine::RenderEngine::ContextPriority::MEDIUM);
+ if (auto type = chooseRenderEngineTypeViaSysProp()) {
+ builder.setRenderEngineType(type.value());
+ }
+ mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
mMaxRenderTargetSize =
std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
@@ -838,10 +846,10 @@
// Process any initial hotplug and resulting display changes.
processDisplayHotplugEventsLocked();
const auto display = getDefaultDisplayDeviceLocked();
- LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback.");
+ LOG_ALWAYS_FATAL_IF(!display, "Missing primary display after registering composer callback.");
const auto displayId = display->getPhysicalId();
LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId),
- "Internal display is disconnected.");
+ "Primary display is disconnected.");
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -916,9 +924,8 @@
}
bool SurfaceFlinger::authenticateSurfaceTextureLocked(
- const sp<IGraphicBufferProducer>& bufferProducer) const {
- sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
- return mGraphicBufferProducerList.count(surfaceTextureBinder.get()) > 0;
+ const sp<IGraphicBufferProducer>& /* bufferProducer */) const {
+ return false;
}
status_t SurfaceFlinger::getSupportedFrameTimestamps(
@@ -1010,6 +1017,11 @@
return NAME_NOT_FOUND;
}
+ const auto displayId = PhysicalDisplayId::tryCast(display->getId());
+ if (!displayId) {
+ return INVALID_OPERATION;
+ }
+
info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value());
const auto& supportedModes = display->getSupportedModes();
@@ -1046,7 +1058,7 @@
outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue();
const auto vsyncConfigSet =
- mVsyncConfiguration->getConfigsForRefreshRate(Fps(outMode.refreshRate));
+ mVsyncConfiguration->getConfigsForRefreshRate(Fps::fromValue(outMode.refreshRate));
outMode.appVsyncOffset = vsyncConfigSet.late.appOffset;
outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset;
outMode.group = mode->getGroup();
@@ -1069,18 +1081,18 @@
}
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- const auto displayId = display->getPhysicalId();
- info->supportedColorModes = getDisplayColorModes(displayId);
-
+ info->supportedColorModes = getDisplayColorModes(*display);
info->hdrCapabilities = display->getHdrCapabilities();
+
info->autoLowLatencyModeSupported =
- getHwComposer().hasDisplayCapability(displayId,
+ getHwComposer().hasDisplayCapability(*displayId,
hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
std::vector<hal::ContentType> types;
- getHwComposer().getSupportedContentTypes(displayId, &types);
+ getHwComposer().getSupportedContentTypes(*displayId, &types);
info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
return type == hal::ContentType::GAME;
});
+
return NO_ERROR;
}
@@ -1107,8 +1119,8 @@
}
if (display->setDesiredActiveMode(info)) {
- // This will trigger HWC refresh without resetting the idle timer.
- repaintEverythingForHWC();
+ scheduleComposite(FrameHint::kNone);
+
// Start receiving vsync samples now, so that we can detect a period
// switch.
mScheduler->resyncToHardwareVsync(true, info.mode->getVsyncPeriod());
@@ -1121,14 +1133,14 @@
}
}
-status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
ATRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
}
- auto future = schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set allowed display modes for invalid display token %p",
@@ -1162,7 +1174,7 @@
return future.get();
}
-void SurfaceFlinger::setActiveModeInternal() {
+void SurfaceFlinger::updateInternalStateWithChangedMode() {
ATRACE_CALL();
const auto display = getDefaultDisplayDeviceLocked();
@@ -1198,7 +1210,7 @@
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
- if (upcomingModeInfo.event != Scheduler::ModeEvent::None) {
+ if (upcomingModeInfo.event != DisplayModeEvent::None) {
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
}
}
@@ -1217,9 +1229,10 @@
updatePhaseConfiguration(refreshRate);
}
-void SurfaceFlinger::performSetActiveMode() {
+void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
+
+ std::optional<PhysicalDisplayId> displayToUpdateImmediately;
for (const auto& iter : mDisplays) {
const auto& display = iter.second;
@@ -1254,8 +1267,7 @@
to_string(display->getId()).c_str());
if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
- // display is not valid or we are already in the requested mode
- // on both cases there is nothing left to do
+ // we are already in the requested mode, there is nothing left to do
desiredActiveModeChangeDone(display);
continue;
}
@@ -1266,7 +1278,7 @@
const auto displayModeAllowed =
display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
if (!displayModeAllowed) {
- desiredActiveModeChangeDone(display);
+ clearDesiredActiveModeState(display);
continue;
}
@@ -1286,13 +1298,31 @@
}
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
- // Scheduler will submit an empty frame to HWC if needed.
- mSetActiveModePending = true;
+ if (outTimeline.refreshRequired) {
+ // Scheduler will submit an empty frame to HWC.
+ mSetActiveModePending = true;
+ } else {
+ // Updating the internal state should be done outside the loop,
+ // because it can recreate a DisplayDevice and modify mDisplays
+ // which will invalidate the iterator.
+ displayToUpdateImmediately = display->getPhysicalId();
+ }
+ }
+
+ if (displayToUpdateImmediately) {
+ updateInternalStateWithChangedMode();
+
+ const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
+ const auto desiredActiveMode = display->getDesiredActiveMode();
+ if (desiredActiveMode &&
+ display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+ desiredActiveModeChangeDone(display);
+ }
}
}
void SurfaceFlinger::disableExpensiveRendering() {
- schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() MAIN_THREAD {
ATRACE_CALL();
if (mPowerAdvisor.isUsingExpensiveRendering()) {
const auto& displays = ON_MAIN_THREAD(mDisplays);
@@ -1301,18 +1331,20 @@
mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable);
}
}
- }).wait();
+ });
+
+ future.wait();
}
-std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) {
- auto modes = getHwComposer().getColorModes(displayId);
- bool isInternalDisplay = displayId == getInternalDisplayIdLocked();
+std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
+ auto modes = getHwComposer().getColorModes(display.getPhysicalId());
- // If it's built-in display and the configuration claims it's not wide color capable,
+ // If the display is internal and the configuration claims it's not wide color capable,
// filter out all wide color modes. The typical reason why this happens is that the
// hardware is not good enough to support GPU composition of wide color, and thus the
// OEMs choose to disable this capability.
- if (isInternalDisplay && !hasWideColorDisplay) {
+ if (display.getConnectionType() == ui::DisplayConnectionType::Internal &&
+ !hasWideColorDisplay) {
const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode);
modes.erase(newEnd, modes.end());
}
@@ -1336,54 +1368,63 @@
}
status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
- schedule([=]() MAIN_THREAD {
- const auto displayId = getPhysicalDisplayIdLocked(displayToken);
- if (!displayId) {
- ALOGE("Invalid display token %p", displayToken.get());
- return;
- }
- const auto modes = getDisplayColorModes(*displayId);
- bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
- if (mode < ColorMode::NATIVE || !exists) {
- ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
- decodeColorMode(mode).c_str(), mode, displayToken.get());
- return;
- }
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
decodeColorMode(mode).c_str(), mode, displayToken.get());
- } else if (display->isVirtual()) {
+ return NAME_NOT_FOUND;
+ }
+
+ if (display->isVirtual()) {
ALOGW("Attempt to set active color mode %s (%d) for virtual display",
decodeColorMode(mode).c_str(), mode);
- } else {
- display->getCompositionDisplay()->setColorProfile(
- compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
- RenderIntent::COLORIMETRIC,
- Dataspace::UNKNOWN});
+ return INVALID_OPERATION;
}
- }).wait();
+ const auto modes = getDisplayColorModes(*display);
+ const bool exists = std::find(modes.begin(), modes.end(), mode) != modes.end();
+
+ if (mode < ColorMode::NATIVE || !exists) {
+ ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p",
+ decodeColorMode(mode).c_str(), mode, displayToken.get());
+ return BAD_VALUE;
+ }
+
+ display->getCompositionDisplay()->setColorProfile(
+ {mode, Dataspace::UNKNOWN, RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN});
+
+ return NO_ERROR;
+ });
+
+ // TODO(b/195698395): Propagate error.
+ future.wait();
return NO_ERROR;
}
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
@@ -1442,17 +1483,18 @@
status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
- return schedule([=]() MAIN_THREAD -> status_t {
- if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
- return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
- componentMask,
- maxFrames);
- } else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
- return NAME_NOT_FOUND;
- }
- })
- .get();
+ const char* const whence = __func__;
+ auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+ return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
+ componentMask, maxFrames);
+ } else {
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ });
+
+ return future.get();
}
status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
@@ -1494,14 +1536,15 @@
}
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
- schedule([=] {
+ auto future = mScheduler->schedule([=] {
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
+ mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
}
- }).wait();
+ });
+ future.wait();
return NO_ERROR;
}
@@ -1517,12 +1560,14 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
outLayers->clear();
- schedule([=] {
+ auto future = mScheduler->schedule([=] {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
mDrawingState.traverseInZOrder([&](Layer* layer) {
outLayers->push_back(layer->getLayerDebugInfo(display.get()));
});
- }).wait();
+ });
+
+ future.wait();
return NO_ERROR;
}
@@ -1617,7 +1662,8 @@
return BAD_VALUE;
}
- return ftl::chain(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
if (enableSdrDimming) {
display->getCompositionDisplay()
@@ -1627,7 +1673,7 @@
return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
brightness.displayBrightness);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return ftl::yield<status_t>(NAME_NOT_FOUND);
}
}))
@@ -1683,7 +1729,7 @@
Boost powerBoost = static_cast<Boost>(boostId);
if (powerBoost == Boost::INTERACTION) {
- mScheduler->notifyTouchEvent();
+ mScheduler->onTouchHint();
}
return NO_ERROR;
@@ -1700,21 +1746,26 @@
return mScheduler->createDisplayEventConnection(handle, eventRegistration);
}
-void SurfaceFlinger::signalTransaction() {
- mScheduler->resetIdleTimer();
+void SurfaceFlinger::scheduleCommit(FrameHint hint) {
+ if (hint == FrameHint::kActive) {
+ mScheduler->resetIdleTimer();
+ }
mPowerAdvisor.notifyDisplayUpdateImminent();
- mEventQueue->invalidate();
+ mScheduler->scheduleFrame();
}
-void SurfaceFlinger::signalLayerUpdate() {
- mScheduler->resetIdleTimer();
- mPowerAdvisor.notifyDisplayUpdateImminent();
- mEventQueue->invalidate();
+void SurfaceFlinger::scheduleComposite(FrameHint hint) {
+ mMustComposite = true;
+ scheduleCommit(hint);
}
-void SurfaceFlinger::signalRefresh() {
- mRefreshPending = true;
- mEventQueue->refresh();
+void SurfaceFlinger::scheduleRepaint() {
+ mGeometryDirty = true;
+ scheduleComposite(FrameHint::kActive);
+}
+
+void SurfaceFlinger::scheduleSample() {
+ static_cast<void>(mScheduler->schedule([this] { sample(); }));
}
nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
@@ -1727,7 +1778,15 @@
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
- ATRACE_CALL();
+ const std::string tracePeriod = [vsyncPeriod]() {
+ if (ATRACE_ENABLED() && vsyncPeriod) {
+ std::stringstream ss;
+ ss << "(" << *vsyncPeriod << ")";
+ return ss.str();
+ }
+ return std::string();
+ }();
+ ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str());
Mutex::Autolock lock(mStateLock);
const auto displayId = getHwComposer().toPhysicalDisplayId(hwcDisplayId);
@@ -1760,28 +1819,10 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
- Scheduler::ModeEvent event) {
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display || mBootStage != BootStage::FINISHED) {
- return;
- }
- ATRACE_CALL();
-
- // Don't do any updating if the current fps is the same as the new one.
- if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
- ALOGV("Skipping mode %d as it is not part of allowed modes",
- refreshRate.getModeId().value());
- return;
- }
-
- setDesiredActiveMode({refreshRate.getMode(), event});
-}
-
void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
hal::Connection connection) {
- ALOGI("%s(%" PRIu64 ", %s)", __func__, hwcDisplayId,
- connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
+ const bool connected = connection == hal::Connection::CONNECTED;
+ ALOGI("%s HAL display %" PRIu64, connected ? "Connecting" : "Disconnecting", hwcDisplayId);
// Only lock if we're not on the main thread. This function is normally
// called on a hwbinder thread, but for the primary display it's called on
@@ -1812,14 +1853,14 @@
void SurfaceFlinger::onComposerHalRefresh(hal::HWDisplayId) {
Mutex::Autolock lock(mStateLock);
- repaintEverythingForHWC();
+ scheduleComposite(FrameHint::kNone);
}
void SurfaceFlinger::setVsyncEnabled(bool enabled) {
ATRACE_CALL();
// On main thread to avoid race conditions with display power state.
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
if (const auto display = getDefaultDisplayDeviceLocked();
@@ -1867,40 +1908,26 @@
: stats.vsyncTime + stats.vsyncPeriod;
}
-void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
- switch (what) {
- case MessageQueue::INVALIDATE: {
- onMessageInvalidate(vsyncId, expectedVSyncTime);
- break;
- }
- case MessageQueue::REFRESH: {
- onMessageRefresh();
- break;
- }
- }
-}
-
-void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) {
- const nsecs_t frameStart = systemTime();
+bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) {
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
- if (expectedVSyncTime >= frameStart) {
- mExpectedPresentTime = expectedVSyncTime;
+ if (expectedVsyncTime >= frameTime) {
+ mExpectedPresentTime = expectedVsyncTime;
} else {
- const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameStart);
+ const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(frameTime);
mExpectedPresentTime = calculateExpectedPresentTime(stats);
}
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
- mScheduledPresentTime = expectedVSyncTime;
+ mScheduledPresentTime = expectedVsyncTime;
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
}();
- ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn,
- mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)");
+ ATRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, vsyncId, vsyncIn,
+ mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)");
// When Backpressure propagation is enabled we want to give a small grace period
// for the present fence to fire instead of just giving up on this frame to handle cases
@@ -1947,59 +1974,72 @@
}
// If we are in the middle of a mode change and the fence hasn't
- // fired yet just wait for the next invalidate
+ // fired yet just wait for the next commit.
if (mSetActiveModePending) {
if (framePending) {
- mEventQueue->invalidate();
- return;
+ mScheduler->scheduleFrame();
+ return false;
}
// We received the present fence from the HWC, so we assume it successfully updated
// the mode, hence we update SF.
mSetActiveModePending = false;
- ON_MAIN_THREAD(setActiveModeInternal());
+ ON_MAIN_THREAD(updateInternalStateWithChangedMode());
}
if (framePending) {
if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
- signalLayerUpdate();
- return;
+ scheduleCommit(FrameHint::kNone);
+ return false;
}
}
if (mTracingEnabledChanged) {
- mTracingEnabled = mTracing.isEnabled();
+ mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
}
if (mRefreshRateOverlaySpinner) {
- if (Mutex::Autolock lock(mStateLock);
- const auto display = getDefaultDisplayDeviceLocked()) {
- if (display) {
- display->onInvalidate();
- } else {
- ALOGW("%s: default display is null", __func__);
- }
+ Mutex::Autolock lock(mStateLock);
+ if (const auto display = getDefaultDisplayDeviceLocked()) {
+ display->animateRefreshRateOverlay();
}
}
- bool refreshNeeded;
+ // Composite if transactions were committed, or if requested by HWC.
+ bool mustComposite = mMustComposite.exchange(false);
{
- mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
- mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
- mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
- const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
- ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
+ mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
- mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod));
-
- refreshNeeded = handleMessageTransaction();
- refreshNeeded |= handleMessageInvalidate();
- if (tracePreComposition) {
- if (mVisibleRegionsDirty) {
- mTracing.notifyLocked("visibleRegionsDirty");
- }
+ bool needsTraversal = false;
+ if (clearTransactionFlags(eTransactionFlushNeeded)) {
+ needsTraversal = flushTransactionQueues(vsyncId);
}
+
+ const bool shouldCommit =
+ (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
+ if (shouldCommit) {
+ commitTransactions();
+ }
+
+ if (transactionFlushNeeded()) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ }
+
+ mustComposite |= shouldCommit;
+ mustComposite |= latchBuffers();
+
+ // This has to be called after latchBuffers because we want to include the layers that have
+ // been latched in the commit callback
+ if (!needsTraversal) {
+ // Invoke empty transaction callbacks early.
+ mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
+ } else {
+ // Invoke OnCommit callbacks.
+ mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
+ }
+
+ updateLayerGeometry();
}
// Layers need to get updated (in the previous line) before we can use them for
@@ -2011,59 +2051,17 @@
mScheduler->chooseRefreshRateForContent();
}
- ON_MAIN_THREAD(performSetActiveMode());
+ ON_MAIN_THREAD(setActiveModeInHwcIfNeeded());
updateCursorAsync();
updateInputFlinger();
- refreshNeeded |= mRepaintEverything;
- if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
- // Signal a refresh if a transaction modified the window state,
- // a new buffer was latched, or if HWC has requested a full
- // repaint
- if (mFrameStartTime <= 0) {
- // We should only use the time of the first invalidate
- // message that signals a refresh as the beginning of the
- // frame. Otherwise the real frame time will be
- // underestimated.
- mFrameStartTime = frameStart;
- }
-
- // Run the refresh immediately after invalidate as there is no point going thru the message
- // queue again, and to ensure that we actually refresh the screen instead of handling
- // other messages that were queued us already in the MessageQueue.
- mRefreshPending = true;
- onMessageRefresh();
- }
- notifyRegionSamplingThread();
+ return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-bool SurfaceFlinger::handleMessageTransaction() {
+void SurfaceFlinger::composite(nsecs_t frameTime) {
ATRACE_CALL();
- if (getTransactionFlags(eTransactionFlushNeeded)) {
- flushTransactionQueues();
- }
- uint32_t transactionFlags = peekTransactionFlags();
- bool runHandleTransaction =
- ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal;
-
- if (runHandleTransaction) {
- handleTransaction(eTransactionMask);
- }
-
- if (transactionFlushNeeded()) {
- setTransactionFlags(eTransactionFlushNeeded);
- }
-
- return runHandleTransaction;
-}
-
-void SurfaceFlinger::onMessageRefresh() {
- ATRACE_CALL();
-
- mRefreshPending = false;
-
compositionengine::CompositionRefreshArgs refreshArgs;
const auto& displays = ON_MAIN_THREAD(mDisplays);
refreshArgs.outputs.reserve(displays.size());
@@ -2080,7 +2078,6 @@
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
- refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
refreshArgs.outputColorSetting = useColorManagement
? mDisplayColorSetting
: compositionengine::OutputColorSetting::kUnmanaged;
@@ -2088,7 +2085,7 @@
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
- refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+ refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
refreshArgs.blursAreExpensive = mBlursAreExpensive;
refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
@@ -2097,31 +2094,27 @@
mDrawingState.colorMatrixChanged = false;
}
- refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion;
+ refreshArgs.devOptForceClientComposition = mDebugDisableHWC;
- if (mDebugRegion != 0) {
- refreshArgs.devOptFlashDirtyRegionsDelay =
- std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
+ if (mDebugFlashDelay != 0) {
+ refreshArgs.devOptForceClientComposition = true;
+ refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
- refreshArgs.nextInvalidateTime = mEventQueue->nextExpectedInvalidate();
-
- mGeometryInvalid = false;
+ refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
const auto presentTime = systemTime();
mCompositionEngine->present(refreshArgs);
- mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
- // Reset the frame start time now that we've recorded this frame.
- mFrameStartTime = 0;
+ mTimeStats->recordFrameDuration(frameTime, systemTime());
- mScheduler->onDisplayRefreshed(presentTime);
+ mScheduler->onPostComposition(presentTime);
postFrame();
postComposition();
@@ -2151,12 +2144,12 @@
modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
- if (mTracingEnabled && mTracePostComposition) {
- // This may block if SurfaceTracing is running in sync mode.
+ if (mLayerTracingEnabled) {
+ // This will block and should only be used for debugging.
if (mVisibleRegionsDirty) {
- mTracing.notify("visibleRegionsDirty");
- } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
- mTracing.notify("bufferLatched");
+ mLayerTracing.notify("visibleRegionsDirty");
+ } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) {
+ mLayerTracing.notify("bufferLatched");
}
}
@@ -2164,16 +2157,12 @@
mVisibleRegionsDirty = false;
if (mCompositionEngine->needsAnotherUpdate()) {
- signalLayerUpdate();
+ scheduleCommit(FrameHint::kNone);
}
}
-bool SurfaceFlinger::handleMessageInvalidate() {
+void SurfaceFlinger::updateLayerGeometry() {
ATRACE_CALL();
- bool refreshNeeded = handlePageFlip();
-
- // Send on commit callbacks
- mTransactionCallbackInvoker.sendCallbacks();
if (mVisibleRegionsDirty) {
computeLayerBounds();
@@ -2185,7 +2174,6 @@
invalidateLayerStack(layer, visibleReg);
}
mLayersPendingRefresh.clear();
- return refreshNeeded;
}
void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -2294,13 +2282,9 @@
}
for (const auto& layer: mLayersWithQueuedFrames) {
- const bool frameLatched =
- layer->onPostComposition(display, glCompositionDoneFenceTime,
- mPreviousPresentFences[0].fenceTime, compositorTiming);
+ layer->onPostComposition(display, glCompositionDoneFenceTime,
+ mPreviousPresentFences[0].fenceTime, compositorTiming);
layer->releasePendingBuffer(/*dequeueReadyTime*/ now);
- if (frameLatched) {
- recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
- }
}
std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
@@ -2333,13 +2317,8 @@
int32_t maxArea = 0;
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
const auto layerFe = layer->getCompositionEngineLayerFE();
- if (layer->isVisible() && compositionDisplay->belongsInOutput(layerFe)) {
- const Dataspace transfer =
- static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
- const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
- transfer == Dataspace::TRANSFER_HLG);
-
- if (isHdr) {
+ if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
+ if (isHdrDataspace(layer->getDataSpace())) {
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
@@ -2363,7 +2342,8 @@
mVisibleRegionsWereDirtyThisFrame = false;
mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0].fence);
- mTransactionCallbackInvoker.sendCallbacks();
+ mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
+ mTransactionCallbackInvoker.clearCompletedTransactions();
if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
mPreviousPresentFences[0].fenceTime->isValid()) {
@@ -2495,29 +2475,26 @@
}
}
-void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) {
+void SurfaceFlinger::commitTransactions() {
ATRACE_CALL();
- // here we keep a copy of the drawing state (that is the state that's
- // going to be overwritten by handleTransactionLocked()) outside of
- // mStateLock so that the side-effects of the State assignment
- // don't happen with mStateLock held (which can cause deadlocks).
+ // Keep a copy of the drawing state (that is going to be overwritten
+ // by commitTransactionsLocked) outside of mStateLock so that the side
+ // effects of the State assignment don't happen with mStateLock held,
+ // which can cause deadlocks.
State drawingState(mDrawingState);
- Mutex::Autolock _l(mStateLock);
+ Mutex::Autolock lock(mStateLock);
mDebugInTransaction = systemTime();
// Here we're guaranteed that some transaction flags are set
- // so we can call handleTransactionLocked() unconditionally.
- // We call getTransactionFlags(), which will also clear the flags,
- // with mStateLock held to guarantee that mCurrentState won't change
- // until the transaction is committed.
+ // so we can call commitTransactionsLocked unconditionally.
+ // We clear the flags with mStateLock held to guarantee that
+ // mCurrentState won't change until the transaction is committed.
modulateVsync(&VsyncModulator::onTransactionCommit);
- transactionFlags = getTransactionFlags(eTransactionMask);
- handleTransactionLocked(transactionFlags);
+ commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
mDebugInTransaction = 0;
- // here the transaction has been committed
}
void SurfaceFlinger::loadDisplayModes(PhysicalDisplayId displayId, DisplayModes& outModes,
@@ -2754,6 +2731,7 @@
display->setProjection(state.orientation, state.layerStackSpaceRect,
state.orientedDisplaySpaceRect);
display->setDisplayName(state.displayName);
+ display->setFlags(state.flags);
return display;
}
@@ -2784,14 +2762,12 @@
compositionengine::DisplayCreationArgsBuilder builder;
if (const auto& physical = state.physical) {
builder.setId(physical->id);
- builder.setConnectionType(physical->type);
} else {
builder.setId(acquireVirtualDisplay(resolution, pixelFormat));
}
builder.setPixels(resolution);
builder.setIsSecure(state.isSecure);
- builder.setLayerStackId(state.layerStack);
builder.setPowerAdvisor(&mPowerAdvisor);
builder.setName(state.displayName);
auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
@@ -2851,7 +2827,7 @@
mDisplays.erase(displayToken);
if (display && display->isVirtual()) {
- static_cast<void>(schedule([display = std::move(display)] {
+ static_cast<void>(mScheduler->schedule([display = std::move(display)] {
// Destroy the display without holding the mStateLock.
// This is a temporary solution until we can manage transaction queues without
// holding the mStateLock.
@@ -2897,7 +2873,7 @@
setPowerModeInternal(display, hal::PowerMode::ON);
// TODO(b/175678251) Call a listener instead.
- if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+ if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
updateInternalDisplayVsyncLocked(display);
}
}
@@ -2977,14 +2953,13 @@
mDrawingState.displays = mCurrentState.displays;
}
-void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) {
- // Commit display transactions
+void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) {
+ // Commit display transactions.
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
if (displayTransactionNeeded) {
processDisplayChangesLocked();
processDisplayHotplugEventsLocked();
}
- mForceTraversal = false;
mForceTransactionDisplayChange = displayTransactionNeeded;
if (mSomeChildrenChanged) {
@@ -2992,18 +2967,9 @@
mSomeChildrenChanged = false;
}
- // Update transform hint
+ // Update transform hint.
if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
- // The transform hint might have changed for some layers
- // (either because a display has changed, or because a layer
- // as changed).
- //
- // Walk through all the layers in currentLayers,
- // and update their transform hint.
- //
- // If a layer is visible only on a single display, then that
- // display is used to calculate the hint, otherwise we use the
- // default display.
+ // Layers and/or displays have changed, so update the transform hint for each layer.
//
// NOTE: we do this here, rather than when presenting the display so that
// the hint is set before we acquire a buffer from the surface texture.
@@ -3014,30 +2980,29 @@
// (soon to become the drawing state list).
//
sp<const DisplayDevice> hintDisplay;
- uint32_t currentlayerStack = 0;
- bool first = true;
+ ui::LayerStack layerStack;
+
mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
// NOTE: we rely on the fact that layers are sorted by
// layerStack first (so we don't have to traverse the list
// of displays for every layer).
- uint32_t layerStack = layer->getLayerStack();
- if (first || currentlayerStack != layerStack) {
- currentlayerStack = layerStack;
- // figure out if this layerstack is mirrored
- // (more than one display) if so, pick the default display,
- // if not, pick the only display it's on.
+ if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) {
+ layerStack = filter.layerStack;
hintDisplay = nullptr;
+
+ // Find the display that includes the layer.
for (const auto& [token, display] : mDisplays) {
- if (display->getCompositionDisplay()
- ->belongsInOutput(layer->getLayerStack(),
- layer->getPrimaryDisplayOnly())) {
- if (hintDisplay) {
- hintDisplay = nullptr;
- break;
- } else {
- hintDisplay = display;
- }
+ if (!display->getCompositionDisplay()->includesLayer(filter)) {
+ continue;
}
+
+ // Pick the primary display if another display mirrors the layer.
+ if (hintDisplay) {
+ hintDisplay = nullptr;
+ break;
+ }
+
+ hintDisplay = display;
}
}
@@ -3051,20 +3016,10 @@
hintDisplay = getDefaultDisplayDeviceLocked();
}
- // could be null if there is no display available at all to get
- // the transform hint from.
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
- }
-
- first = false;
+ layer->updateTransformHint(hintDisplay->getTransformHint());
});
}
- /*
- * Perform our own transaction if needed
- */
-
if (mLayersAdded) {
mLayersAdded = false;
// Layers have been added.
@@ -3086,7 +3041,9 @@
});
}
- commitTransaction();
+ doCommitTransactions();
+ signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
+ mAnimTransactionPending = false;
}
void SurfaceFlinger::updateInputFlinger() {
@@ -3095,41 +3052,77 @@
return;
}
+ std::vector<WindowInfo> windowInfos;
+ std::vector<DisplayInfo> displayInfos;
+ bool updateWindowInfo = false;
if (mVisibleRegionsDirty || mInputInfoChanged) {
mInputInfoChanged = false;
- notifyWindowInfos();
- } else if (mInputWindowCommands.syncInputWindows) {
- // If the caller requested to sync input windows, but there are no
- // changes to input windows, notify immediately.
- windowInfosReported();
+ updateWindowInfo = true;
+ buildWindowInfos(windowInfos, displayInfos);
}
+ if (!updateWindowInfo && mInputWindowCommands.empty()) {
+ return;
+ }
+ BackgroundExecutor::getInstance().execute([updateWindowInfo,
+ windowInfos = std::move(windowInfos),
+ displayInfos = std::move(displayInfos),
+ inputWindowCommands =
+ std::move(mInputWindowCommands),
+ inputFlinger = mInputFlinger, this]() {
+ ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
+ if (updateWindowInfo) {
+ mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
+ inputWindowCommands.syncInputWindows);
+ } else if (inputWindowCommands.syncInputWindows) {
+ // If the caller requested to sync input windows, but there are no
+ // changes to input windows, notify immediately.
+ windowInfosReported();
+ }
+ for (const auto& focusRequest : inputWindowCommands.focusRequests) {
+ inputFlinger->setFocusedWindow(focusRequest);
+ }
+ });
- for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
- mInputFlinger->setFocusedWindow(focusRequest);
- }
mInputWindowCommands.clear();
}
-bool enablePerWindowInputRotation() {
- static bool value =
- android::base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
- return value;
-}
-
-void SurfaceFlinger::notifyWindowInfos() {
- std::vector<WindowInfo> windowInfos;
+void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
+ std::vector<DisplayInfo>& outDisplayInfos) {
+ std::unordered_map<uint32_t /*layerStackId*/,
+ std::pair<bool /* isSecure */, const ui::Transform>>
+ inputDisplayDetails;
+ for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ if (!display->receivesInput()) {
+ continue;
+ }
+ const uint32_t layerStackId = display->getLayerStack().id;
+ const auto& [info, transform] = display->getInputInfo();
+ const auto& [it, emplaced] =
+ inputDisplayDetails.try_emplace(layerStackId, display->isSecure(), transform);
+ if (!emplaced) {
+ ALOGE("Multiple displays claim to accept input for the same layer stack: %u",
+ layerStackId);
+ continue;
+ }
+ outDisplayInfos.emplace_back(info);
+ }
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- sp<DisplayDevice> display = enablePerWindowInputRotation()
- ? ON_MAIN_THREAD(getDisplayWithInputByLayer(layer))
- : nullptr;
- // When calculating the screen bounds we ignore the transparent region since it may
- // result in an unwanted offset.
- windowInfos.push_back(layer->fillInputInfo(display));
+
+ bool isSecure = true;
+ ui::Transform displayTransform = ui::Transform();
+
+ const uint32_t layerStackId = layer->getLayerStack().id;
+ const auto it = inputDisplayDetails.find(layerStackId);
+ if (it != inputDisplayDetails.end()) {
+ const auto& [secure, transform] = it->second;
+ isSecure = secure;
+ displayTransform = transform;
+ }
+
+ outWindowInfos.push_back(layer->fillInputInfo(displayTransform, isSecure));
});
- mWindowInfosListenerInvoker->windowInfosChanged(windowInfos,
- mInputWindowCommands.syncInputWindows);
}
void SurfaceFlinger::updateCursorAsync() {
@@ -3143,13 +3136,27 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) {
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, DisplayModeEvent event) {
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Scheduler::chooseRefreshRateForContent
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- changeRefreshRateLocked(refreshRate, event);
+
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display || mBootStage != BootStage::FINISHED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // Don't do any updating if the current fps is the same as the new one.
+ if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
+ ALOGV("Skipping mode %d as it is not part of allowed modes",
+ refreshRate.getModeId().value());
+ return;
+ }
+
+ setDesiredActiveMode({refreshRate.getMode(), event});
}
void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3177,8 +3184,35 @@
mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
- // start the EventThread
- mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this);
+ using Feature = scheduler::Feature;
+ scheduler::FeatureFlags features;
+
+ if (sysprop::use_content_detection_for_refresh_rate(false)) {
+ features |= Feature::kContentDetection;
+ }
+ if (base::GetBoolProperty("debug.sf.show_predicted_vsync"s, false)) {
+ features |= Feature::kTracePredictedVsync;
+ }
+ if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) &&
+ !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ features |= Feature::kPresentFences;
+ }
+
+ mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
+ static_cast<ISchedulerCallback&>(*this),
+ features);
+ {
+ auto configs = display->holdRefreshRateConfigs();
+ if (configs->supportsKernelIdleTimer()) {
+ features |= Feature::kKernelIdleTimer;
+ }
+
+ mScheduler->createVsyncSchedule(features);
+ mScheduler->setRefreshRateConfigs(std::move(configs));
+ }
+ setVsyncEnabled(false);
+ mScheduler->startTimers();
+
const auto configs = mVsyncConfiguration->getCurrentConfigs();
const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
mAppConnectionHandle =
@@ -3194,8 +3228,8 @@
mInterceptor->saveVSyncEvent(timestamp);
});
- mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
- configs.late.sfWorkDuration);
+ mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+ configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3208,11 +3242,6 @@
// classes from EventThread, and there should be no run-time binder cost
// anyway since there are no connected apps at this point.
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
- static auto ignorePresentFences =
- base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
- mScheduler->setIgnorePresentFences(
- ignorePresentFences ||
- getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3229,22 +3258,15 @@
mScheduler->setDuration(mSfConnectionHandle,
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
/*readyDuration=*/config.sfWorkDuration);
- mEventQueue->setDuration(config.sfWorkDuration);
+ mScheduler->setDuration(config.sfWorkDuration);
}
-void SurfaceFlinger::commitTransaction() {
+void SurfaceFlinger::doCommitTransactions() {
ATRACE_CALL();
- commitTransactionLocked();
- signalSynchronousTransactions(CountDownLatch::eSyncTransaction);
- mAnimTransactionPending = false;
-}
-void SurfaceFlinger::commitTransactionLocked() {
if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (const auto& l : mLayersPendingRemoval) {
- recordBufferingStats(l->getName(), l->getOccupancyHistory(true));
-
// Ensure any buffers set to display on any children are released.
if (l->isRemovedFromCurrentState()) {
l->latchAndReleaseBuffer();
@@ -3283,11 +3305,10 @@
void SurfaceFlinger::commitOffscreenLayers() {
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
- uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (!trFlags) return;
-
- layer->doTransaction(0);
- layer->commitChildList();
+ if (layer->clearTransactionFlags(eTransactionNeeded)) {
+ layer->doTransaction(0);
+ layer->commitChildList();
+ }
});
}
}
@@ -3295,17 +3316,16 @@
void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
auto display = displayDevice->getCompositionDisplay();
- if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
+ if (display->includesLayer(layer->getOutputFilter())) {
display->editState().dirtyRegion.orSelf(dirty);
}
}
}
-bool SurfaceFlinger::handlePageFlip() {
+bool SurfaceFlinger::latchBuffers() {
ATRACE_CALL();
- ALOGV("handlePageFlip");
- nsecs_t latchTime = systemTime();
+ const nsecs_t latchTime = systemTime();
bool visibleRegions = false;
bool frameQueued = false;
@@ -3323,14 +3343,14 @@
// Display is now waiting on Layer 1's frame, which is behind layer 0's
// second frame. But layer 0's second frame could be waiting on display.
mDrawingState.traverse([&](Layer* layer) {
- uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
- if (trFlags || mForceTransactionDisplayChange) {
- const uint32_t flags = layer->doTransaction(0);
- if (flags & Layer::eVisibleRegion)
- mVisibleRegionsDirty = true;
- }
+ if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
+ const uint32_t flags = layer->doTransaction(0);
+ if (flags & Layer::eVisibleRegion) {
+ mVisibleRegionsDirty = true;
+ }
+ }
- if (layer->hasReadyFrame()) {
+ if (layer->hasReadyFrame()) {
frameQueued = true;
if (layer->shouldPresentNow(expectedPresentTime)) {
mLayersWithQueuedFrames.emplace(layer);
@@ -3338,7 +3358,7 @@
ATRACE_NAME("!layer->shouldPresentNow()");
layer->useEmptyDamage();
}
- } else {
+ } else {
layer->useEmptyDamage();
}
});
@@ -3374,7 +3394,7 @@
// queued frame that shouldn't be displayed during this vsync period, wake
// up during the next vsync period to check again.
if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {
- signalLayerUpdate();
+ scheduleCommit(FrameHint::kNone);
}
// enter boot animation on first buffer latch
@@ -3391,26 +3411,16 @@
return !mLayersWithQueuedFrames.empty() && newDataLatched;
}
-void SurfaceFlinger::invalidateHwcGeometry() {
- mGeometryInvalid = true;
-}
-
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
- const sp<IBinder>& parentHandle,
- const sp<Layer>& parentLayer, bool addToRoot,
- uint32_t* outTransformHint) {
+ const sp<Layer>& lbc, const wp<Layer>& parent,
+ bool addToRoot, uint32_t* outTransformHint) {
if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
ISurfaceComposer::MAX_LAYERS);
return NO_MEMORY;
}
- wp<IBinder> initialProducer;
- if (gbc != nullptr) {
- initialProducer = IInterface::asBinder(gbc);
- }
- setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer, addToRoot);
+ setLayerCreatedState(handle, lbc, parent, addToRoot);
// Create a transaction includes the initial parent and producer.
Vector<ComposerState> states;
@@ -3426,68 +3436,66 @@
*outTransformHint = mActiveDisplayTransformHint;
}
// attach this layer to the client
- client->attachLayer(handle, lbc);
+ if (client != nullptr) {
+ client->attachLayer(handle, lbc);
+ }
+ int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
InputWindowCommands{}, -1 /* desiredPresentTime */,
true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
- 0 /* Undefined transactionId */);
+ transactionId);
}
-void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
- static_cast<void>(schedule([=] {
- Mutex::Autolock lock(mStateLock);
- mGraphicBufferProducerList.erase(binder);
- }));
-}
-
-uint32_t SurfaceFlinger::peekTransactionFlags() {
+uint32_t SurfaceFlinger::getTransactionFlags() const {
return mTransactionFlags;
}
-uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) {
- return mTransactionFlags.fetch_and(~flags) & flags;
+uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) {
+ return mTransactionFlags.fetch_and(~mask) & mask;
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
- return setTransactionFlags(flags, TransactionSchedule::Late);
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask) {
+ return setTransactionFlags(mask, TransactionSchedule::Late);
}
-uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule,
- const sp<IBinder>& token) {
- uint32_t old = mTransactionFlags.fetch_or(flags);
- modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, token);
- if ((old & flags) == 0) signalTransaction();
+uint32_t SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
+ const sp<IBinder>& applyToken) {
+ const uint32_t old = mTransactionFlags.fetch_or(mask);
+ modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+ if ((old & mask) == 0) scheduleCommit(FrameHint::kActive);
return old;
}
-void SurfaceFlinger::setTraversalNeeded() {
- mForceTraversal = true;
-}
-
-void SurfaceFlinger::flushTransactionQueues() {
+bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
- std::vector<const TransactionState> transactions;
+ std::vector<TransactionState> transactions;
// Layer handles that have transactions with buffers that are ready to be applied.
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
{
Mutex::Autolock _l(mStateLock);
{
Mutex::Autolock _l(mQueueLock);
+ // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto
+ // or always. auto: in this case we let buffer latch unsignaled if we have only one
+ // applyToken and if only first transaction is latch unsignaled. If more than one
+ // applyToken we don't latch unsignaled.
+ bool allowLatchUnsignaled = allowedLatchUnsignaled();
+ bool isFirstUnsignaledTransactionApplied = false;
// Collect transactions from pending transaction queue.
auto it = mPendingTransactionQueues.begin();
while (it != mPendingTransactionQueues.end()) {
auto& [applyToken, transactionQueue] = *it;
-
while (!transactionQueue.empty()) {
auto& transaction = transactionQueue.front();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- bufferLayersReadyToPresent)) {
+ bufferLayersReadyToPresent,
+ allowLatchUnsignaled)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
@@ -3496,6 +3504,14 @@
});
transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
+ if (allowLatchUnsignaled &&
+ enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+ // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto
+ // then we should have only one applyToken for processing.
+ // so we can stop further transactions on this applyToken.
+ isFirstUnsignaledTransactionApplied = true;
+ break;
+ }
}
if (transactionQueue.empty()) {
@@ -3507,48 +3523,123 @@
}
// Collect transactions from current transaction queue or queue to pending transactions.
- // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+ // Case 1: push to pending when transactionIsReadyToBeApplied is false
+ // or the first transaction was unsignaled.
// Case 2: push to pending when there exist a pending queue.
- // Case 3: others are ready to apply.
+ // Case 3: others are the transactions that are ready to apply.
while (!mTransactionQueue.empty()) {
auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
- if (pendingTransactions ||
+ if (isFirstUnsignaledTransactionApplied || pendingTransactions ||
!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- bufferLayersReadyToPresent)) {
+ bufferLayersReadyToPresent,
+ allowLatchUnsignaled)) {
mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
bufferLayersReadyToPresent.insert(state.surface);
});
transactions.emplace_back(std::move(transaction));
+ if (allowLatchUnsignaled &&
+ enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+ isFirstUnsignaledTransactionApplied = true;
+ }
}
- mTransactionQueue.pop();
+ mTransactionQueue.pop_front();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
- }
- // Now apply all transactions.
- for (const auto& transaction : transactions) {
- applyTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.inputWindowCommands, transaction.desiredPresentTime,
- transaction.isAutoTimestamp, transaction.buffer,
- transaction.postTime, transaction.permissions,
- transaction.hasListenerCallbacks, transaction.listenerCallbacks,
- transaction.originPid, transaction.originUid, transaction.id);
- if (transaction.transactionCommittedSignal) {
- mTransactionCommittedSignals.emplace_back(
- std::move(transaction.transactionCommittedSignal));
- }
+ return applyTransactions(transactions, vsyncId);
}
}
}
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
+ int64_t vsyncId) {
+ bool needsTraversal = false;
+ // Now apply all transactions.
+ for (const auto& transaction : transactions) {
+ needsTraversal |=
+ applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.buffer, transaction.postTime,
+ transaction.permissions, transaction.hasListenerCallbacks,
+ transaction.listenerCallbacks, transaction.originPid,
+ transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
+ }
+ }
+
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+ }
+ return needsTraversal;
+}
+
+bool SurfaceFlinger::allowedLatchUnsignaled() {
+ if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
+ return false;
+ }
+ // Always mode matches the current latch unsignaled behavior.
+ // This behavior is currently used by the partners and we would like
+ // to keep it until we are completely migrated to Auto mode successfully
+ // and we we have our fallback based implementation in place.
+ if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
+ return true;
+ }
+
+ // if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto
+ // we don't latch unsignaled if more than one applyToken, as it can backpressure
+ // the other transactions.
+ if (mPendingTransactionQueues.size() > 1) {
+ return false;
+ }
+ std::optional<sp<IBinder>> applyToken = std::nullopt;
+ bool isPendingTransactionQueuesItem = false;
+ if (!mPendingTransactionQueues.empty()) {
+ applyToken = mPendingTransactionQueues.begin()->first;
+ isPendingTransactionQueuesItem = true;
+ }
+
+ for (const auto& item : mTransactionQueue) {
+ if (!applyToken.has_value()) {
+ applyToken = item.applyToken;
+ } else if (applyToken.has_value() && applyToken != item.applyToken) {
+ return false;
+ }
+ }
+
+ if (isPendingTransactionQueuesItem) {
+ return checkTransactionCanLatchUnsignaled(
+ mPendingTransactionQueues.begin()->second.front());
+ } else if (applyToken.has_value()) {
+ return checkTransactionCanLatchUnsignaled((mTransactionQueue.front()));
+ }
+ return false;
+}
+
+bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) {
+ if (transaction.states.size() == 1) {
+ const auto& state = transaction.states.begin()->state;
+ if ((state.flags & ~layer_state_t::eBufferChanged) == 0 &&
+ state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ state.bufferData.acquireFence &&
+ state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ ATRACE_NAME("transactionCanLatchUnsignaled");
+ return true;
+ }
+ }
+ return false;
+}
+
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3580,7 +3671,8 @@
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- bufferLayersReadyToPresent) const {
+ bufferLayersReadyToPresent,
+ bool allowLatchUnsignaled) const {
ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
@@ -3605,9 +3697,10 @@
for (const ComposerState& state : states) {
const layer_state_t& s = state.state;
- const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
- if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled &&
- s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ const bool acquireFenceChanged =
+ s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged);
+ if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled &&
+ s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
ATRACE_NAME("fence unsignaled");
return false;
}
@@ -3667,7 +3760,7 @@
: CountDownLatch::eSyncTransaction));
}
- mTransactionQueue.emplace(state);
+ mTransactionQueue.emplace_back(state);
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
const auto schedule = [](uint32_t flags) {
@@ -3741,6 +3834,10 @@
state.traverseStatesWithBuffers([&](const layer_state_t& state) {
mBufferCountTracker.increment(state.surface->localBinder());
});
+
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.addQueuedTransaction(state);
+ }
queueTransaction(state);
// Check the pending state to make sure the transaction is synchronous.
@@ -3751,7 +3848,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
@@ -3770,33 +3867,23 @@
// that listeners with SurfaceControls will start registration during setClientStateLocked
// below.
for (const auto& listener : listenerCallbacks) {
- mTransactionCallbackInvoker.startRegistration(listener);
- mTransactionCallbackInvoker.endRegistration(listener);
+ mTransactionCallbackInvoker.addEmptyTransaction(listener);
}
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
uint32_t clientStateFlags = 0;
for (const ComposerState& state : states) {
- clientStateFlags |=
- setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
- postTime, permissions, listenerCallbacksWithSurfaces);
+ clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
+ isAutoTimestamp, postTime, permissions);
if ((flags & eAnimation) && state.state.surface) {
- if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
+ if (const auto layer = fromHandle(state.state.surface).promote()) {
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
mScheduler->recordLayerHistory(layer.get(),
isAutoTimestamp ? 0 : desiredPresentTime,
- LayerHistory::LayerUpdateType::AnimationTX);
+ LayerUpdateType::AnimationTX);
}
}
}
- for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
- mTransactionCallbackInvoker.endRegistration(listenerCallback);
- }
-
- // If the state doesn't require a traversal and there are callbacks, send them now
- if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
- mTransactionCallbackInvoker.sendCallbacks();
- }
transactionFlags |= clientStateFlags;
if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
@@ -3818,6 +3905,7 @@
transactionFlags = eTransactionNeeded;
}
+ bool needsTraversal = false;
if (transactionFlags) {
if (mInterceptor->isEnabled()) {
mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags,
@@ -3828,7 +3916,7 @@
// so we don't have to wake up again next frame to preform an unnecessary traversal.
if (transactionFlags & eTraversalNeeded) {
transactionFlags = transactionFlags & (~eTraversalNeeded);
- mForceTraversal = true;
+ needsTraversal = true;
}
if (transactionFlags) {
setTransactionFlags(transactionFlags);
@@ -3838,6 +3926,8 @@
mAnimTransactionPending = true;
}
}
+
+ return needsTraversal;
}
uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) {
@@ -3906,10 +3996,10 @@
return true;
}
-uint32_t SurfaceFlinger::setClientStateLocked(
- const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& outListenerCallbacks) {
+uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
+ const ComposerState& composerState,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ int64_t postTime, uint32_t permissions) {
const layer_state_t& s = composerState.state;
const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
@@ -3922,16 +4012,12 @@
ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
if (!onCommitCallbacks.callbackIds.empty()) {
- mTransactionCallbackInvoker.startRegistration(onCommitCallbacks);
filteredListeners.push_back(onCommitCallbacks);
- outListenerCallbacks.insert(onCommitCallbacks);
}
ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
if (!onCompleteCallbacks.callbackIds.empty()) {
- mTransactionCallbackInvoker.startRegistration(onCompleteCallbacks);
filteredListeners.push_back(onCompleteCallbacks);
- outListenerCallbacks.insert(onCompleteCallbacks);
}
}
@@ -4104,9 +4190,6 @@
if (what & layer_state_t::eCropChanged) {
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eAcquireFenceChanged) {
- if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eDataspaceChanged) {
if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
}
@@ -4133,13 +4216,14 @@
std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
- auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1);
- if (gameMode != -1) {
+
+ if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) {
// The transaction will be received on the Task layer and needs to be applied to all
// child layers. Child layers that are added at a later point will obtain the game mode
// info through addChild().
- layer->setGameModeForTree(gameMode);
+ layer->setGameModeForTree(static_cast<GameMode>(gameMode));
}
+
if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -4163,7 +4247,8 @@
const auto strategy =
Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
- if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+ if (layer->setFrameRate(
+ Layer::FrameRate(Fps::fromValue(s.frameRate), compatibility, strategy))) {
flags |= eTraversalNeeded;
}
}
@@ -4207,7 +4292,7 @@
mInputInfoChanged = true;
}
} else {
- ALOGE("Attempt to update InputPolicyFlags without permission ACCESS_SURFACE_FLINGER");
+ ALOGE("Attempt to update DropInputMode without permission ACCESS_SURFACE_FLINGER");
}
}
// This has to happen after we reparent children because when we reparent to null we remove
@@ -4233,43 +4318,11 @@
callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
}
}
- bool bufferChanged = what & layer_state_t::eBufferChanged;
- bool cacheIdChanged = what & layer_state_t::eCachedBufferChanged;
- bool bufferSizeExceedsLimit = false;
- std::shared_ptr<renderengine::ExternalTexture> buffer;
- if (bufferChanged && cacheIdChanged && s.buffer != nullptr) {
- bufferSizeExceedsLimit =
- exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- ClientCache::getInstance().add(s.cachedBuffer, s.buffer);
- buffer = ClientCache::getInstance().get(s.cachedBuffer);
- }
- } else if (cacheIdChanged) {
- buffer = ClientCache::getInstance().get(s.cachedBuffer);
- } else if (bufferChanged && s.buffer != nullptr) {
- bufferSizeExceedsLimit =
- exceedsMaxRenderTargetSize(s.buffer->getWidth(), s.buffer->getHeight());
- if (!bufferSizeExceedsLimit) {
- buffer = std::make_shared<
- renderengine::ExternalTexture>(s.buffer, getRenderEngine(),
- renderengine::ExternalTexture::Usage::READABLE);
- }
- }
- ALOGE_IF(bufferSizeExceedsLimit,
- "Attempted to create an ExternalTexture for layer %s that exceeds render target size "
- "limit.",
- layer->getDebugName());
- if (buffer) {
- const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged;
- const uint64_t frameNumber = frameNumberChanged
- ? s.frameNumber
- : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1;
- if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp,
- s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo,
- s.releaseBufferListener, s.releaseBufferEndpoint)) {
- flags |= eTraversalNeeded;
- }
+ if (what & layer_state_t::eBufferChanged &&
+ layer->setBuffer(s.bufferData, postTime, desiredPresentTime, isAutoTimestamp,
+ dequeueBufferTimestamp, frameTimelineInfo)) {
+ flags |= eTraversalNeeded;
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
@@ -4285,25 +4338,22 @@
return hasChanges ? eTraversalNeeded : 0;
}
-status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle, int32_t* outLayerId) {
+status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
+ const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) {
if (!mirrorFromHandle) {
return NAME_NOT_FOUND;
}
sp<Layer> mirrorLayer;
sp<Layer> mirrorFrom;
- std::string uniqueName = getUniqueLayerName("MirrorRoot");
-
{
Mutex::Autolock _l(mStateLock);
mirrorFrom = fromHandle(mirrorFromHandle).promote();
if (!mirrorFrom) {
return NAME_NOT_FOUND;
}
-
- status_t result = createContainerLayer(client, std::move(uniqueName), -1, -1, 0,
- LayerMetadata(), outHandle, &mirrorLayer);
+ status_t result = createContainerLayer(args, outHandle, &mirrorLayer);
if (result != NO_ERROR) {
return result;
}
@@ -4312,22 +4362,17 @@
}
*outLayerId = mirrorLayer->sequence;
- return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
- nullptr /* outTransformHint */);
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
+ args.name, mirrorFrom->sequence);
+ }
+ return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
+ false /* addAsRoot */, nullptr /* outTransformHint */);
}
-status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
const sp<IBinder>& parentHandle, int32_t* outLayerId,
const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
- if (int32_t(w|h) < 0) {
- ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
-
ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
"Expected only one of parentLayer or parentHandle to be non-null. "
"Programmer error?");
@@ -4336,40 +4381,22 @@
sp<Layer> layer;
- std::string uniqueName = getUniqueLayerName(name.string());
-
- switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+ switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
case ISurfaceComposerClient::eFXSurfaceBufferState: {
- result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createBufferStateLayer(args, outHandle, &layer);
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
- mBufferCountTracker.add((*handle)->localBinder(), counterName,
+ mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
pendingBufferCounter);
}
} break;
case ISurfaceComposerClient::eFXSurfaceEffect:
- // check if buffer size is set for color layer.
- if (w > 0 || h > 0) {
- ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
-
- result = createEffectLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createEffectLayer(args, outHandle, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceContainer:
- // check if buffer size is set for container layer.
- if (w > 0 || h > 0) {
- ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
- result = createContainerLayer(client, std::move(uniqueName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createContainerLayer(args, outHandle, &layer);
break;
default:
result = BAD_VALUE;
@@ -4381,46 +4408,38 @@
}
bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
- result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
- outTransformHint);
+ wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
+ if (parentHandle != nullptr && parent == nullptr) {
+ ALOGE("Invalid parent handle %p.", parentHandle.get());
+ addToRoot = false;
+ }
+ if (parentLayer != nullptr) {
+ addToRoot = false;
+ }
+
+ int parentId = -1;
+ // We can safely promote the layer in binder thread because we have a strong reference
+ // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
+ sp<Layer> parentSp = parent.promote();
+ if (parentSp != nullptr) {
+ parentId = parentSp->getSequence();
+ }
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+ args.flags, parentId);
+ }
+
+ result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
if (result != NO_ERROR) {
return result;
}
- mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
*outLayerId = layer->sequence;
return result;
}
-std::string SurfaceFlinger::getUniqueLayerName(const char* name) {
- unsigned dupeCounter = 0;
-
- // Tack on our counter whether there is a hit or not, so everyone gets a tag
- std::string uniqueName = base::StringPrintf("%s#%u", name, dupeCounter);
-
- // Grab the state lock since we're accessing mCurrentState
- Mutex::Autolock lock(mStateLock);
-
- // Loop over layers until we're sure there is no matching name
- bool matchFound = true;
- while (matchFound) {
- matchFound = false;
- mCurrentState.traverse([&](Layer* layer) {
- if (layer->getName() == uniqueName) {
- matchFound = true;
- uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
- }
- });
- }
-
- ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name, uniqueName.c_str());
- return uniqueName;
-}
-
-status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, PixelFormat& format,
+status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
sp<Layer>* outLayer) {
@@ -4436,7 +4455,6 @@
}
sp<BufferQueueLayer> layer;
- LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.textureName = getNewTexture();
{
// Grab the SF state lock during this since it's the only safe way to access
@@ -4446,7 +4464,7 @@
layer = getFactory().createBufferQueueLayer(args);
}
- status_t err = layer->setDefaultBufferProperties(w, h, format);
+ status_t err = layer->setDefaultBufferProperties(0, 0, format);
if (err == NO_ERROR) {
*handle = layer->getHandle();
*gbp = layer->getProducer();
@@ -4457,34 +4475,24 @@
return err;
}
-status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.textureName = getNewTexture();
- sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
- *handle = layer->getHandle();
- *outLayer = layer;
-
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* handle, sp<Layer>* outLayer) {
- *outLayer = getFactory().createEffectLayer(
- {this, client, std::move(name), w, h, flags, std::move(metadata)});
+ *outLayer = getFactory().createBufferStateLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
+ sp<Layer>* outLayer) {
+ *outLayer = getFactory().createEffectLayer(args);
+ *handle = (*outLayer)->getHandle();
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- *outLayer = getFactory().createContainerLayer(
- {this, client, std::move(name), w, h, flags, std::move(metadata)});
+ *outLayer = getFactory().createContainerLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
@@ -4508,6 +4516,9 @@
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
layer.clear();
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onHandleRemoved(handle);
+ }
}
// ---------------------------------------------------------------------------
@@ -4526,7 +4537,7 @@
d.what = DisplayState::eDisplayProjectionChanged |
DisplayState::eLayerStackChanged;
d.token = token;
- d.layerStack = 0;
+ d.layerStack = ui::DEFAULT_LAYER_STACK;
d.orientation = ui::ROTATION_0;
d.orientedDisplaySpaceRect.makeInvalid();
d.layerStackSpaceRect.makeInvalid();
@@ -4535,10 +4546,12 @@
displays.add(d);
nsecs_t now = systemTime();
+
+ int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
// It should be on the main thread, apply it directly.
applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
/* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
- {}, getpid(), getuid(), 0 /* Undefined transactionId */);
+ {}, mPid, getuid(), transactionId);
setPowerModeInternal(display, hal::PowerMode::ON);
const nsecs_t vsyncPeriod =
@@ -4553,27 +4566,7 @@
void SurfaceFlinger::initializeDisplays() {
// Async since we may be called from the main thread.
- static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const {
- sp<DisplayDevice> display;
- for (const auto& pair : mDisplays) {
- const auto& displayDevice = pair.second;
- if (!displayDevice->receivesInput() ||
- !displayDevice->getCompositionDisplay()
- ->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
- continue;
- }
- // Don't return immediately so that we can log duplicates.
- if (display) {
- ALOGE("Multiple display devices claim to accept input for the same layerstack: %d",
- layer->getLayerStack());
- continue;
- }
- display = displayDevice;
- }
- return display;
+ static_cast<void>(mScheduler->schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4624,7 +4617,7 @@
mVisibleRegionsDirty = true;
mHasPoweredOff = true;
- repaintEverything();
+ scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
// Turn off the display
if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
@@ -4673,7 +4666,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() MAIN_THREAD {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4683,7 +4676,9 @@
} else {
setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
}
- }).wait();
+ });
+
+ future.wait();
}
status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
@@ -4733,7 +4728,7 @@
}
if (dumpLayers) {
- LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto();
+ LayersTraceFileProto traceFileProto = mLayerTracing.createTraceFileProto();
LayersTraceProto* layersTrace = traceFileProto.add_entry();
LayersProto layersProto = dumpProtoFromMainThread();
layersTrace->mutable_layers()->Swap(&layersProto);
@@ -4755,8 +4750,9 @@
}
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
- if (asProto && mTracing.isEnabled()) {
- mTracing.writeToFile();
+ if (asProto) {
+ mLayerTracing.writeToFile();
+ mTransactionTracing.writeToFile();
}
return doDump(fd, DumpArgs(), asProto);
@@ -4868,24 +4864,6 @@
bucketTimeSec, percent);
}
-void SurfaceFlinger::recordBufferingStats(const std::string& layerName,
- std::vector<OccupancyTracker::Segment>&& history) {
- Mutex::Autolock lock(getBE().mBufferingStatsMutex);
- auto& stats = getBE().mBufferingStats[layerName];
- for (const auto& segment : history) {
- if (!segment.usedThirdBuffer) {
- stats.twoBufferTime += segment.totalTime;
- }
- if (segment.occupancyAverage < 1.0f) {
- stats.doubleBufferedTime += segment.totalTime;
- } else if (segment.occupancyAverage < 2.0f) {
- stats.tripleBufferedTime += segment.totalTime;
- }
- ++stats.numSegments;
- stats.totalTime += segment.totalTime;
- }
-}
-
void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
result.append("Layer frame timestamps:\n");
// Traverse all layers to dump frame-events for each layer
@@ -4893,38 +4871,6 @@
[&] (Layer* layer) { layer->dumpFrameEvents(result); });
}
-void SurfaceFlinger::dumpBufferingStats(std::string& result) const {
- result.append("Buffering stats:\n");
- result.append(" [Layer name] <Active time> <Two buffer> "
- "<Double buffered> <Triple buffered>\n");
- Mutex::Autolock lock(getBE().mBufferingStatsMutex);
- typedef std::tuple<std::string, float, float, float> BufferTuple;
- std::map<float, BufferTuple, std::greater<float>> sorted;
- for (const auto& statsPair : getBE().mBufferingStats) {
- const char* name = statsPair.first.c_str();
- const SurfaceFlingerBE::BufferingStats& stats = statsPair.second;
- if (stats.numSegments == 0) {
- continue;
- }
- float activeTime = ns2ms(stats.totalTime) / 1000.0f;
- float twoBufferRatio = static_cast<float>(stats.twoBufferTime) /
- stats.totalTime;
- float doubleBufferRatio = static_cast<float>(
- stats.doubleBufferedTime) / stats.totalTime;
- float tripleBufferRatio = static_cast<float>(
- stats.tripleBufferedTime) / stats.totalTime;
- sorted.insert({activeTime, {name, twoBufferRatio,
- doubleBufferRatio, tripleBufferRatio}});
- }
- for (const auto& sortedPair : sorted) {
- float activeTime = sortedPair.first;
- const BufferTuple& values = sortedPair.second;
- StringAppendF(&result, " [%s] %.2f %.3f %.3f %.3f\n", std::get<0>(values).c_str(),
- activeTime, std::get<1>(values), std::get<2>(values), std::get<3>(values));
- }
- result.append("\n");
-}
-
void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
for (const auto& [token, display] : mDisplays) {
const auto displayId = PhysicalDisplayId::tryCast(display->getId());
@@ -5003,7 +4949,6 @@
}
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
- // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
LayersProto layersProto;
@@ -5019,7 +4964,7 @@
DisplayProto* displayProto = layersTraceProto.add_displays();
displayProto->set_id(display->getId().value);
displayProto->set_name(display->getDisplayName());
- displayProto->set_layer_stack(display->getLayerStack());
+ displayProto->set_layer_stack(display->getLayerStack().id);
LayerProtoHelper::writeSizeToProto(display->getWidth(), display->getHeight(),
[&]() { return displayProto->mutable_size(); });
LayerProtoHelper::writeToProto(display->getLayerStackSpaceRect(), [&]() {
@@ -5027,6 +4972,7 @@
});
LayerProtoHelper::writeTransformToProto(display->getTransform(),
displayProto->mutable_transform());
+ displayProto->set_is_virtual(display->isVirtual());
}
}
@@ -5055,21 +5001,21 @@
}
LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
+ auto future = mScheduler->schedule([this] {
+ std::string result;
+ for (Layer* offscreenLayer : mOffscreenLayers) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ }
+ return result;
+ });
+
result.append("Offscreen Layers:\n");
- result.append(schedule([this] {
- std::string result;
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) {
- layer->dumpCallingUidPid(result);
- });
- }
- return result;
- }).get());
+ result.append(future.get());
}
void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
@@ -5116,15 +5062,11 @@
StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load());
StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load());
- dumpBufferingStats(result);
-
/*
* Dump the visible layer list
*/
colorizer.bold(result);
StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
- StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
colorizer.reset(result);
{
@@ -5206,7 +5148,9 @@
/*
* Tracing state
*/
- mTracing.dump(result);
+ mLayerTracing.dump(result);
+ result.append("\n");
+ mTransactionTracing.dump(result);
result.append("\n");
/*
@@ -5240,7 +5184,7 @@
colorizer.bold(result);
result.append("h/w composer state:\n");
colorizer.reset(result);
- bool hwcDisabled = mDebugDisableHWC || mDebugRegion;
+ const bool hwcDisabled = mDebugDisableHWC || mDebugFlashDelay;
StringAppendF(&result, " h/w composer %s\n", hwcDisabled ? "disabled" : "enabled");
getHwComposer().dump(result);
@@ -5250,6 +5194,11 @@
const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.dump(result);
+ /*
+ * Dump flag/property manager state
+ */
+ mFlagManager->dump(result);
+
result.append(mTimeStats->miniDump());
result.append("\n");
}
@@ -5313,11 +5262,9 @@
case SET_GLOBAL_SHADOW_SETTINGS:
case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
- // which acquire the necessary permission dynamically. Don't use the permission cache
- // for this check.
- bool usePermissionCache =
- code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
+ // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary
+ // permission dynamically. Don't use the permission cache for this check.
+ bool usePermissionCache = code != OVERRIDE_HDR_TYPES;
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5437,9 +5384,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1040 are currently used for backdoors. The code
+ // Numbers from 1000 to 1041 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1040) {
+ if (code >= 1000 && code <= 1041) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5450,9 +5397,8 @@
status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
- status_t credentialCheck = CheckTransactCodeCredentials(code);
- if (credentialCheck != OK) {
- return credentialCheck;
+ if (const status_t error = CheckTransactCodeCredentials(code); error != OK) {
+ return error;
}
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
@@ -5469,47 +5415,41 @@
}
int n;
switch (code) {
- case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
- case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
+ case 1000: // Unused.
+ 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();
return NO_ERROR;
- case 1002: // SHOW_UPDATES
- n = data.readInt32();
- mDebugRegion = n ? n : (mDebugRegion ? 0 : 1);
- invalidateHwcGeometry();
- repaintEverything();
+ case 1004: // Force composite ahead of next VSYNC.
+ case 1006:
+ scheduleComposite(FrameHint::kActive);
return NO_ERROR;
- case 1004:{ // repaint everything
- repaintEverything();
+ case 1005: { // Force commit ahead of next VSYNC.
+ Mutex::Autolock lock(mStateLock);
+ setTransactionFlags(eTransactionNeeded | eDisplayTransactionNeeded |
+ eTraversalNeeded);
return NO_ERROR;
}
- case 1005:{ // force transaction
- Mutex::Autolock _l(mStateLock);
- setTransactionFlags(
- eTransactionNeeded|
- eDisplayTransactionNeeded|
- eTraversalNeeded);
+ case 1007: // Unused.
+ return NAME_NOT_FOUND;
+ case 1008: // Toggle forced GPU composition.
+ mDebugDisableHWC = data.readInt32() != 0;
+ scheduleRepaint();
return NO_ERROR;
- }
- case 1006:{ // send empty update
- signalRefresh();
+ case 1009: // Toggle use of transform hint.
+ mDebugDisableTransformHint = data.readInt32() != 0;
+ scheduleRepaint();
return NO_ERROR;
- }
- case 1008: // toggle use of hw composer
- n = data.readInt32();
- mDebugDisableHWC = n != 0;
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- case 1009: // toggle use of transform hint
- n = data.readInt32();
- mDebugDisableTransformHint = n != 0;
- invalidateHwcGeometry();
- repaintEverything();
- return NO_ERROR;
- case 1010: // interrogate.
+ case 1010: // Interrogate.
reply->writeInt32(0);
reply->writeInt32(0);
- reply->writeInt32(mDebugRegion);
+ reply->writeInt32(mDebugFlashDelay);
reply->writeInt32(0);
reply->writeInt32(mDebugDisableHWC);
return NO_ERROR;
@@ -5606,7 +5546,8 @@
}
case 1021: { // Disable HWC virtual displays
const bool enable = data.readInt32() != 0;
- static_cast<void>(schedule([this, enable] { enableHalVirtualDisplays(enable); }));
+ static_cast<void>(
+ mScheduler->schedule([this, enable] { enableHalVirtualDisplays(enable); }));
return NO_ERROR;
}
case 1022: { // Set saturation boost
@@ -5623,8 +5564,7 @@
if (data.readInt32(&colorMode) == NO_ERROR) {
mForceColorMode = static_cast<ColorMode>(colorMode);
}
- invalidateHwcGeometry();
- repaintEverything();
+ scheduleRepaint();
return NO_ERROR;
}
// Deprecate, use 1030 to check whether the device is color managed.
@@ -5636,20 +5576,21 @@
bool tracingEnabledChanged;
if (n) {
ALOGD("LayerTracing enabled");
- tracingEnabledChanged = mTracing.enable();
+ tracingEnabledChanged = mLayerTracing.enable();
if (tracingEnabledChanged) {
- schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+ mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); })
+ .wait();
}
} else {
ALOGD("LayerTracing disabled");
- tracingEnabledChanged = mTracing.disable();
+ tracingEnabledChanged = mLayerTracing.disable();
}
mTracingEnabledChanged = tracingEnabledChanged;
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1026: { // Get layer tracing status
- reply->writeBool(mTracing.isEnabled());
+ reply->writeBool(mLayerTracing.isEnabled());
return NO_ERROR;
}
// Is a DisplayColorSetting supported?
@@ -5690,7 +5631,7 @@
}
ALOGD("Updating trace buffer to %d KB", n);
- mTracing.setBufferSize(n * 1024);
+ mLayerTracing.setBufferSize(n * 1024);
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
@@ -5735,12 +5676,12 @@
case 1033: {
n = data.readUint32();
ALOGD("Updating trace flags to 0x%x", n);
- mTracing.setTraceFlags(n);
+ mLayerTracing.setTraceFlags(n);
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1034: {
- schedule([&] {
+ auto future = mScheduler->schedule([&] {
switch (n = data.readInt32()) {
case 0:
case 1:
@@ -5750,64 +5691,78 @@
reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled()));
}
}
- }).get();
+ });
+
+ future.wait();
return NO_ERROR;
}
case 1035: {
const int modeId = data.readInt32();
- mDebugDisplayModeSetByBackdoor = false;
- const auto displayId = [&]() -> std::optional<PhysicalDisplayId> {
- uint64_t inputDisplayId = 0;
- if (data.readUint64(&inputDisplayId) == NO_ERROR) {
- const auto token = getPhysicalDisplayToken(
- static_cast<PhysicalDisplayId>(inputDisplayId));
- if (!token) {
- ALOGE("No display with id: %" PRIu64, inputDisplayId);
- return std::nullopt;
- }
-
- return std::make_optional<PhysicalDisplayId>(inputDisplayId);
+ const auto display = [&]() -> sp<IBinder> {
+ uint64_t value;
+ if (data.readUint64(&value) != NO_ERROR) {
+ return getDefaultDisplayDevice()->getDisplayToken().promote();
}
- return getDefaultDisplayDevice()->getPhysicalId();
+ if (const auto id = DisplayId::fromValue<PhysicalDisplayId>(value)) {
+ return getPhysicalDisplayToken(*id);
+ }
+
+ ALOGE("Invalid physical display ID");
+ return nullptr;
}();
- if (!displayId) {
- ALOGE("No display found");
- return NO_ERROR;
- }
-
- status_t result = setActiveMode(getPhysicalDisplayToken(*displayId), modeId);
- if (result != NO_ERROR) {
- return result;
- }
-
- mDebugDisplayModeSetByBackdoor = true;
-
- return NO_ERROR;
+ mDebugDisplayModeSetByBackdoor = false;
+ const status_t result = setActiveModeFromBackdoor(display, modeId);
+ mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
+ return result;
}
+ // Turn on/off frame rate flexibility mode. When turned on it overrides the display
+ // manager frame rate policy a new policy which allows switching between all refresh
+ // rates.
case 1036: {
- if (data.readInt32() > 0) {
- status_t result =
- acquireFrameRateFlexibilityToken(&mDebugFrameRateFlexibilityToken);
- if (result != NO_ERROR) {
- return result;
- }
- } else {
- mDebugFrameRateFlexibilityToken = nullptr;
+ if (data.readInt32() > 0) { // turn on
+ return mScheduler
+ ->schedule([this] {
+ const auto display =
+ ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+
+ // This is a little racy, but not in a way that hurts anything. As
+ // we grab the defaultMode from the display manager policy, we could
+ // be setting a new display manager policy, leaving us using a stale
+ // defaultMode. The defaultMode doesn't matter for the override
+ // policy though, since we set allowGroupSwitching to true, so it's
+ // not a problem.
+ scheduler::RefreshRateConfigs::Policy overridePolicy;
+ overridePolicy.defaultMode = display->refreshRateConfigs()
+ .getDisplayManagerPolicy()
+ .defaultMode;
+ overridePolicy.allowGroupSwitching = true;
+ constexpr bool kOverridePolicy = true;
+ return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
+ kOverridePolicy);
+ })
+ .get();
+ } else { // turn off
+ return mScheduler
+ ->schedule([this] {
+ const auto display =
+ ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ constexpr bool kOverridePolicy = true;
+ return setDesiredDisplayModeSpecsInternal(display, {},
+ kOverridePolicy);
+ })
+ .get();
}
- return NO_ERROR;
}
// Inject a hotplug connected event for the primary display. This will deallocate and
// reallocate the display state including framebuffers.
case 1037: {
- std::optional<hal::HWDisplayId> hwcId;
- {
- Mutex::Autolock lock(mStateLock);
- hwcId = getHwComposer().getInternalHwcDisplayId();
- }
- onComposerHalHotplug(*hwcId, hal::Connection::CONNECTED);
+ const hal::HWDisplayId hwcId =
+ (Mutex::Autolock(mStateLock), getHwComposer().getPrimaryHwcDisplayId());
+
+ onComposerHalHotplug(hwcId, hal::Connection::CONNECTED);
return NO_ERROR;
}
// Modify the max number of display frames stored within FrameTimeline
@@ -5842,38 +5797,46 @@
// Second argument is an optional uint64 - if present, then limits enabling/disabling
// caching to a particular physical display
case 1040: {
- status_t error =
- schedule([&] {
- n = data.readInt32();
- std::optional<PhysicalDisplayId> inputId = std::nullopt;
- if (uint64_t inputDisplayId;
- data.readUint64(&inputDisplayId) == NO_ERROR) {
- const auto token = getPhysicalDisplayToken(
- static_cast<PhysicalDisplayId>(inputDisplayId));
- if (!token) {
- ALOGE("No display with id: %" PRIu64, inputDisplayId);
- return NAME_NOT_FOUND;
- }
-
- inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId);
+ auto future = mScheduler->schedule([&] {
+ n = data.readInt32();
+ std::optional<PhysicalDisplayId> inputId = std::nullopt;
+ if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+ inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
+ if (!inputId || getPhysicalDisplayToken(*inputId)) {
+ ALOGE("No display with id: %" PRIu64, inputDisplayId);
+ return NAME_NOT_FOUND;
+ }
+ }
+ {
+ Mutex::Autolock lock(mStateLock);
+ mLayerCachingEnabled = n != 0;
+ for (const auto& [_, display] : mDisplays) {
+ if (!inputId || *inputId == display->getPhysicalId()) {
+ display->enableLayerCaching(mLayerCachingEnabled);
}
- {
- Mutex::Autolock lock(mStateLock);
- mLayerCachingEnabled = n != 0;
- for (const auto& [_, display] : mDisplays) {
- if (!inputId || *inputId == display->getPhysicalId()) {
- display->enableLayerCaching(mLayerCachingEnabled);
- }
- }
- }
- return OK;
- }).get();
+ }
+ }
+ return OK;
+ });
- if (error != OK) {
+ if (const status_t error = future.get(); error != OK) {
return error;
}
- invalidateHwcGeometry();
- repaintEverything();
+ scheduleRepaint();
+ return NO_ERROR;
+ }
+ case 1041: { // Transaction tracing
+ if (data.readInt32()) {
+ // Transaction tracing is always running but allow the user to temporarily
+ // increase the buffer when actively debugging.
+ mTransactionTracing.setBufferSize(
+ TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+ } else {
+ mTransactionTracing.setBufferSize(
+ TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
+ mTransactionTracing.writeToFile();
+ }
+ reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
}
@@ -5881,17 +5844,6 @@
return err;
}
-void SurfaceFlinger::repaintEverything() {
- mRepaintEverything = true;
- signalTransaction();
-}
-
-void SurfaceFlinger::repaintEverythingForHWC() {
- mRepaintEverything = true;
- mPowerAdvisor.notifyDisplayUpdateImminent();
- mEventQueue->invalidate();
-}
-
void SurfaceFlinger::kernelTimerChanged(bool expired) {
static bool updateOverlay =
property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
@@ -5900,7 +5852,7 @@
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getCurrentRefreshRate()
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -5915,7 +5867,7 @@
const bool timerExpired = mKernelIdleTimerEnabled && expired;
if (display->onKernelTimerChanged(desiredModeId, timerExpired)) {
- mEventQueue->invalidate();
+ mScheduler->scheduleFrame();
}
}));
}
@@ -6110,12 +6062,13 @@
traverseLayersInLayerStack(layerStack, args.uid, visitor);
};
- return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, args.allowProtected, args.grayscale,
- captureListener);
+ auto captureResultFuture = captureScreenCommon(std::move(renderAreaFuture), traverseLayers,
+ reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
+ return captureResultFuture.get().status;
}
-status_t SurfaceFlinger::captureDisplay(uint64_t displayIdOrLayerStack,
+status_t SurfaceFlinger::captureDisplay(DisplayId displayId,
const sp<IScreenCaptureListener>& captureListener) {
ui::LayerStack layerStack;
wp<const DisplayDevice> displayWeak;
@@ -6123,21 +6076,14 @@
ui::Dataspace dataspace;
{
Mutex::Autolock lock(mStateLock);
- auto display = getDisplayDeviceLocked(PhysicalDisplayId{displayIdOrLayerStack});
- // Fall back to first display whose layer stack matches.
- if (!display) {
- const auto layerStack = static_cast<ui::LayerStack>(displayIdOrLayerStack);
- display = findDisplay(WithLayerStack(layerStack));
- }
-
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
return NAME_NOT_FOUND;
}
- layerStack = display->getLayerStack();
displayWeak = display;
-
+ layerStack = display->getLayerStack();
size = display->getLayerStackSpaceRect().getSize();
dataspace =
@@ -6154,9 +6100,15 @@
traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
- return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
- ui::PixelFormat::RGBA_8888, false /* allowProtected */,
- false /* grayscale */, captureListener);
+ if (captureListener == nullptr) {
+ ALOGE("capture screen must provide a capture listener callback");
+ return BAD_VALUE;
+ }
+ auto captureResultFuture =
+ captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+ ui::PixelFormat::RGBA_8888, false /* allowProtected */,
+ false /* grayscale */, captureListener);
+ return captureResultFuture.get().status;
}
status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -6226,7 +6178,9 @@
// colors when capture.
dataspace = args.dataspace;
if (dataspace == ui::Dataspace::UNKNOWN) {
- auto display = findDisplay(WithLayerStack(parent->getLayerStack()));
+ auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ });
if (!display) {
// If the layer is not on a display, use the dataspace for the default display.
display = getDefaultDisplayDeviceLocked();
@@ -6274,23 +6228,28 @@
});
};
- return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, args.allowProtected, args.grayscale,
- captureListener);
+ if (captureListener == nullptr) {
+ ALOGE("capture screen must provide a capture listener callback");
+ return BAD_VALUE;
+ }
+
+ auto captureResultFuture = captureScreenCommon(std::move(renderAreaFuture), traverseLayers,
+ reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
+ return captureResultFuture.get().status;
}
-status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
- TraverseLayersFunction traverseLayers,
- ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>& captureListener) {
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScreenCommon(
+ RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+ ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
+ const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) {
ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32
") that exceeds render target size limit.",
bufferSize.getWidth(), bufferSize.getHeight());
- return BAD_VALUE;
+ return ftl::yield<renderengine::RenderEngineResult>({BAD_VALUE, base::unique_fd()}).share();
}
// Loop over all visible layers to see whether there's any protected layer. A protected layer is
@@ -6300,14 +6259,15 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
- hasProtectedLayer = schedule([=]() {
- bool protectedLayerFound = false;
- traverseLayers([&](Layer* layer) {
- protectedLayerFound = protectedLayerFound ||
- (layer->isVisible() && layer->isProtected());
- });
- return protectedLayerFound;
- }).get();
+ auto future = mScheduler->schedule([=]() {
+ bool protectedLayerFound = false;
+ traverseLayers([&](Layer* layer) {
+ protectedLayerFound =
+ protectedLayerFound || (layer->isVisible() && layer->isProtected());
+ });
+ return protectedLayerFound;
+ });
+ hasProtectedLayer = future.get();
}
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6330,50 +6290,67 @@
false /* regionSampling */, grayscale, captureListener);
}
-status_t SurfaceFlinger::captureScreenCommon(
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::captureScreenCommon(
RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- if (captureListener == nullptr) {
- ALOGE("capture screen must provide a capture listener callback");
- return BAD_VALUE;
- }
-
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
- static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
- if (mRefreshPending) {
- ALOGW("Skipping screenshot for now");
- captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
- grayscale, captureListener);
- return;
- }
+ auto scheduleResultFuture = mScheduler->schedule([=,
+ renderAreaFuture =
+ std::move(renderAreaFuture)]() mutable
+ -> std::shared_future<
+ renderengine::RenderEngineResult> {
ScreenCaptureResults captureResults;
std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
if (!renderArea) {
ALOGW("Skipping screen capture because of invalid render area.");
captureResults.result = NO_MEMORY;
captureListener->onScreenCaptureCompleted(captureResults);
- return;
+ return ftl::yield<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})
+ .share();
}
- status_t result = NO_ERROR;
+ std::shared_future<renderengine::RenderEngineResult> renderEngineResultFuture;
+
renderArea->render([&] {
- result = renderScreenImplLocked(*renderArea, traverseLayers, buffer,
- canCaptureBlackoutContent, regionSampling, grayscale,
- captureResults);
+ renderEngineResultFuture =
+ renderScreenImplLocked(*renderArea, traverseLayers, buffer,
+ canCaptureBlackoutContent, regionSampling, grayscale,
+ captureResults);
});
+ // spring up a thread to unblock SF main thread and wait for
+ // RenderEngineResult to be available
+ if (captureListener != nullptr) {
+ std::async([=]() mutable {
+ ATRACE_NAME("captureListener is nonnull!");
+ auto& [status, drawFence] = renderEngineResultFuture.get();
+ captureResults.result = status;
+ captureResults.fence = new Fence(dup(drawFence));
+ captureListener->onScreenCaptureCompleted(captureResults);
+ });
+ }
+ return renderEngineResultFuture;
+ });
- captureResults.result = result;
- captureListener->onScreenCaptureCompleted(captureResults);
- }));
-
- return NO_ERROR;
+ // flatten scheduleResultFuture object to single shared_future object
+ if (captureListener == nullptr) {
+ std::future<renderengine::RenderEngineResult> captureScreenResultFuture =
+ ftl::chain(std::move(scheduleResultFuture))
+ .then([=](std::shared_future<renderengine::RenderEngineResult> futureObject)
+ -> renderengine::RenderEngineResult {
+ auto& [status, drawFence] = futureObject.get();
+ return {status, base::unique_fd(dup(drawFence))};
+ });
+ return captureScreenResultFuture.share();
+ } else {
+ return ftl::yield<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}).share();
+ }
}
-status_t SurfaceFlinger::renderScreenImplLocked(
+std::shared_future<renderengine::RenderEngineResult> SurfaceFlinger::renderScreenImplLocked(
const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
@@ -6392,7 +6369,8 @@
// the impetus on WindowManager to not persist them.
if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) {
ALOGW("FB is protected: PERMISSION_DENIED");
- return PERMISSION_DENIED;
+ return ftl::yield<renderengine::RenderEngineResult>({PERMISSION_DENIED, base::unique_fd()})
+ .share();
}
captureResults.buffer = buffer->getBuffer();
@@ -6432,7 +6410,6 @@
const auto display = renderArea.getDisplayDevice();
std::vector<Layer*> renderedLayers;
- Region clearRegion = Region::INVALID_REGION;
bool disableBlurs = false;
traverseLayers([&](Layer* layer) {
disableBlurs |= layer->getDrawingState().sidebandStream != nullptr;
@@ -6444,7 +6421,6 @@
renderArea.needsFiltering(),
renderArea.isSecure(),
useProtected,
- clearRegion,
layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
true, /* realContentIsVisible */
@@ -6453,6 +6429,8 @@
BlurSetting::Disabled
: compositionengine::LayerFE::ClientCompositionTargetSettings::
BlurSetting::Enabled,
+ DisplayDevice::sDefaultMaxLumiance,
+
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
@@ -6476,35 +6454,35 @@
});
- std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers(
- clientCompositionLayers.size());
+ std::vector<renderengine::LayerSettings> clientRenderEngineLayers;
+ clientRenderEngineLayers.reserve(clientCompositionLayers.size());
std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
- clientCompositionLayerPointers.begin(),
- std::pointer_traits<renderengine::LayerSettings*>::pointer_to);
+ std::back_inserter(clientRenderEngineLayers),
+ [](compositionengine::LayerFE::LayerSettings& settings)
+ -> renderengine::LayerSettings { return settings; });
- clientCompositionDisplay.clearRegion = clearRegion;
// Use an empty fence for the buffer fence, since we just created the buffer so
// there is no need for synchronization with the GPU.
base::unique_fd bufferFence;
- base::unique_fd drawFence;
getRenderEngine().useProtectedContext(useProtected);
const constexpr bool kUseFramebufferCache = false;
- getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
- kUseFramebufferCache, std::move(bufferFence), &drawFence);
+ std::future<renderengine::RenderEngineResult> drawLayersResult =
+ getRenderEngine().drawLayers(clientCompositionDisplay, clientRenderEngineLayers, buffer,
+ kUseFramebufferCache, std::move(bufferFence));
- if (drawFence >= 0) {
- sp<Fence> releaseFence = new Fence(dup(drawFence));
- for (auto* layer : renderedLayers) {
- layer->onLayerDisplayed(releaseFence);
- }
+ std::shared_future<renderengine::RenderEngineResult> drawLayersResultFuture =
+ drawLayersResult.share(); // drawLayersResult will be moved to shared one
+
+ for (auto* layer : renderedLayers) {
+ // make a copy of shared_future object for each layer
+ layer->onLayerDisplayed(drawLayersResultFuture);
}
- captureResults.fence = new Fence(drawFence.release());
// Always switch back to unprotected context.
getRenderEngine().useProtectedContext(false);
- return NO_ERROR;
+ return drawLayersResultFuture;
}
void SurfaceFlinger::windowInfosReported() {
@@ -6531,12 +6509,12 @@
// We loop through the first level of layers without traversing,
// as we need to determine which layers belong to the requested display.
for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (!layer->belongsToDisplay(layerStack)) {
+ if (layer->getLayerStack() != layerStack) {
continue;
}
// relative layers are traversed in Layer::traverseInZOrder
layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (layer->getPrimaryDisplayOnly()) {
+ if (layer->isInternalDisplayOverlay()) {
return;
}
if (!layer->isVisible()) {
@@ -6600,7 +6578,7 @@
if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
ALOGV("switching to Scheduler preferred display mode %d",
preferredDisplayMode->getId().value());
- setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed});
+ setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
} else {
LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
preferredDisplayMode->getId().value());
@@ -6619,7 +6597,7 @@
return BAD_VALUE;
}
- auto future = schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -6632,8 +6610,10 @@
using Policy = scheduler::RefreshRateConfigs::Policy;
const Policy policy{DisplayModeId(defaultMode),
allowGroupSwitching,
- {Fps(primaryRefreshRateMin), Fps(primaryRefreshRateMax)},
- {Fps(appRequestRefreshRateMin), Fps(appRequestRefreshRateMax)}};
+ {Fps::fromValue(primaryRefreshRateMin),
+ Fps::fromValue(primaryRefreshRateMax)},
+ {Fps::fromValue(appRequestRefreshRateMin),
+ Fps::fromValue(appRequestRefreshRateMax)}};
constexpr bool kOverridePolicy = false;
return setDesiredDisplayModeSpecsInternal(display, policy, kOverridePolicy);
@@ -6695,6 +6675,13 @@
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onLayerRemoved(layer->getSequence());
+ }
+}
+
+void SurfaceFlinger::onLayerUpdate() {
+ scheduleCommit(FrameHint::kActive);
}
// WARNING: ONLY CALL THIS FROM LAYER DTOR
@@ -6750,7 +6737,7 @@
return BAD_VALUE;
}
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6761,7 +6748,7 @@
const auto strategy =
Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
if (layer->setFrameRate(
- Layer::FrameRate(Fps{frameRate},
+ Layer::FrameRate(Fps::fromValue(frameRate),
Layer::FrameRate::convertCompatibility(compatibility),
strategy))) {
setTransactionFlags(eTraversalNeeded);
@@ -6776,74 +6763,6 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
- if (!outToken) {
- return BAD_VALUE;
- }
-
- auto future = schedule([this] {
- status_t result = NO_ERROR;
- sp<IBinder> token;
-
- if (mFrameRateFlexibilityTokenCount == 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-
- // This is a little racy, but not in a way that hurts anything. As we grab the
- // defaultMode from the display manager policy, we could be setting a new display
- // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't
- // matter for the override policy though, since we set allowGroupSwitching to
- // true, so it's not a problem.
- scheduler::RefreshRateConfigs::Policy overridePolicy;
- overridePolicy.defaultMode =
- display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode;
- overridePolicy.allowGroupSwitching = true;
- constexpr bool kOverridePolicy = true;
- result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
- }
-
- if (result == NO_ERROR) {
- mFrameRateFlexibilityTokenCount++;
- // Handing out a reference to the SurfaceFlinger object, as we're doing in the line
- // below, is something to consider carefully. The lifetime of the
- // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this
- // SurfaceFlinger object were to be destroyed while the token still exists, the token
- // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok
- // in this case, for two reasons:
- // 1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way
- // the program exits is via a crash. So we won't have a situation where the
- // SurfaceFlinger object is dead but the process is still up.
- // 2. The frame rate flexibility token is acquired/released only by CTS tests, so even
- // if condition 1 were changed, the problem would only show up when running CTS tests,
- // not on end user devices, so we could spot it and fix it without serious impact.
- token = new FrameRateFlexibilityToken(
- [this]() { onFrameRateFlexibilityTokenReleased(); });
- ALOGD("Frame rate flexibility token acquired. count=%d",
- mFrameRateFlexibilityTokenCount);
- }
-
- return std::make_pair(result, token);
- });
-
- status_t result;
- std::tie(result, *outToken) = future.get();
- return result;
-}
-
-void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() {
- static_cast<void>(schedule([this] {
- LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
- "Failed tracking frame rate flexibility tokens");
- mFrameRateFlexibilityTokenCount--;
- ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
- if (mFrameRateFlexibilityTokenCount == 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- constexpr bool kOverridePolicy = true;
- status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy);
- LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
- }
- }));
-}
-
status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) {
Mutex::Autolock lock(mStateLock);
@@ -6895,33 +6814,29 @@
}
status_t SurfaceFlinger::getMaxAcquiredBufferCount(int* buffers) const {
- const auto maxSupportedRefreshRate = [&] {
- const auto display = getDefaultDisplayDevice();
- if (display) {
- return display->refreshRateConfigs().getSupportedRefreshRateRange().max;
+ Fps maxRefreshRate = 60_Hz;
+
+ if (!getHwComposer().isHeadless()) {
+ if (const auto display = getDefaultDisplayDevice()) {
+ maxRefreshRate = display->refreshRateConfigs().getSupportedRefreshRateRange().max;
}
- ALOGW("%s: default display is null", __func__);
- return Fps(60);
- }();
- *buffers = getMaxAcquiredBufferCountForRefreshRate(maxSupportedRefreshRate);
+ }
+
+ *buffers = getMaxAcquiredBufferCountForRefreshRate(maxRefreshRate);
return NO_ERROR;
}
-int SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
- const auto refreshRate = [&] {
- const auto frameRateOverride = mScheduler->getFrameRateOverride(uid);
- if (frameRateOverride.has_value()) {
- return frameRateOverride.value();
- }
+uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const {
+ Fps refreshRate = 60_Hz;
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- if (display) {
- return display->refreshRateConfigs().getCurrentRefreshRate().getFps();
+ if (const auto frameRateOverride = mScheduler->getFrameRateOverride(uid)) {
+ refreshRate = *frameRateOverride;
+ } else if (!getHwComposer().isHeadless()) {
+ if (const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked())) {
+ refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
}
+ }
- ALOGW("%s: default display is null", __func__);
- return Fps(60);
- }();
return getMaxAcquiredBufferCountForRefreshRate(refreshRate);
}
@@ -6931,7 +6846,7 @@
return calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
-void SurfaceFlinger::TransactionState::traverseStatesWithBuffers(
+void TransactionState::traverseStatesWithBuffers(
std::function<void(const layer_state_t&)> visitor) {
for (const auto& state : states) {
if (state.state.hasBufferChanges() && state.state.hasValidBuffer() && state.state.surface) {
@@ -6941,11 +6856,10 @@
}
void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const wp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot) {
+ const wp<Layer> parent, bool addToRoot) {
Mutex::Autolock lock(mCreatedLayersLock);
mCreatedLayers[handle->localBinder()] =
- std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer, addToRoot);
+ std::make_unique<LayerCreatedState>(layer, parent, addToRoot);
}
auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
@@ -6983,19 +6897,16 @@
}
sp<Layer> parent;
- bool allowAddRoot = state->addToRoot;
+ bool addToRoot = state->addToRoot;
if (state->initialParent != nullptr) {
- parent = fromHandle(state->initialParent.promote()).promote();
+ parent = state->initialParent.promote();
if (parent == nullptr) {
ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
- allowAddRoot = false;
+ addToRoot = false;
}
- } else if (state->initialParentLayer != nullptr) {
- parent = state->initialParentLayer.promote();
- allowAddRoot = false;
}
- if (parent == nullptr && allowAddRoot) {
+ if (parent == nullptr && addToRoot) {
layer->setIsAtRoot(true);
mCurrentState.layersSortedByZ.add(layer);
} else if (parent == nullptr) {
@@ -7009,32 +6920,16 @@
layer->updateTransformHint(mActiveDisplayTransformHint);
- if (state->initialProducer != nullptr) {
- mGraphicBufferProducerList.insert(state->initialProducer);
- LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
- "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
- mNumLayers.load());
- if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
- ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
- mNumLayers.load());
- }
- }
-
+ mInterceptor->saveSurfaceCreation(layer);
return layer;
}
-void SurfaceFlinger::scheduleRegionSamplingThread() {
- static_cast<void>(schedule([&] { notifyRegionSamplingThread(); }));
-}
-
-void SurfaceFlinger::notifyRegionSamplingThread() {
+void SurfaceFlinger::sample() {
if (!mLumaSampling || !mRegionSamplingThread) {
return;
}
- mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate());
+ mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
}
void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 74fe7d9..e1b52c5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,7 +23,6 @@
*/
#include <android-base/thread_annotations.h>
-#include <compositionengine/OutputColorSetting.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <gui/BufferQueue.h>
@@ -32,7 +31,6 @@
#include <gui/ISurfaceComposerClient.h>
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
-#include <gui/OccupancyTracker.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
#include <renderengine/LayerSettings.h>
@@ -48,13 +46,15 @@
#include <utils/Trace.h>
#include <utils/threads.h>
+#include <compositionengine/OutputColorSetting.h>
+#include <scheduler/Fps.h>
+
#include "ClientCache.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
-#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "Scheduler/RefreshRateConfigs.h"
@@ -62,9 +62,11 @@
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
-#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
+#include "Tracing/LayerTracing.h"
+#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
+#include "TransactionState.h"
#include <atomic>
#include <cstdint>
@@ -89,6 +91,7 @@
class Client;
class EventThread;
+class FlagManager;
class FpsReporter;
class TunnelModeEnabledReporter;
class HdrLayerInfoReporter;
@@ -103,6 +106,7 @@
class FrameTracer;
class WindowInfosListenerInvoker;
+using gui::IRegionSamplingListener;
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -133,6 +137,8 @@
eTransactionMask = 0x1f,
};
+enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+
using DisplayColorSetting = compositionengine::OutputColorSetting;
struct SurfaceFlingerBE {
@@ -154,29 +160,14 @@
nsecs_t mFrameBuckets[NUM_BUCKETS] = {};
nsecs_t mTotalTime = 0;
std::atomic<nsecs_t> mLastSwapTime = 0;
-
- // Double- vs. triple-buffering stats
- struct BufferingStats {
- size_t numSegments = 0;
- nsecs_t totalTime = 0;
-
- // "Two buffer" means that a third buffer was never used, whereas
- // "double-buffered" means that on average the segment only used two
- // buffers (though it may have used a third for some part of the
- // segment)
- nsecs_t twoBufferTime = 0;
- nsecs_t doubleBufferedTime = 0;
- nsecs_t tripleBufferedTime = 0;
- };
- mutable Mutex mBufferingStatsMutex;
- std::unordered_map<std::string, BufferingStats> mBufferingStats;
};
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
- private ISchedulerCallback {
+ private ICompositor,
+ private scheduler::ISchedulerCallback {
public:
struct SkipInitializationTag {};
@@ -243,7 +234,7 @@
static ui::Rotation internalDisplayOrientation;
// Indicate if device wants color management on its display.
- static bool useColorManagement;
+ static const constexpr bool useColorManagement = true;
static bool useContextPriority;
@@ -259,18 +250,13 @@
static ui::Dataspace wideColorGamutCompositionDataspace;
static ui::PixelFormat wideColorGamutCompositionPixelFormat;
- // Whether to use frame rate API when deciding about the refresh rate of the display. This
- // variable is caches in SF, so that we can check it with each layer creation, and a void the
- // overhead that is caused by reading from sysprop.
- static bool useFrameRateApi;
-
static constexpr SkipInitializationTag SkipInitialization;
// Whether or not SDR layers should be dimmed to the desired SDR white point instead of
// being treated as native display brightness
static bool enableSdrDimming;
- static bool enableLatchUnsignaled;
+ static LatchUnsignaledConfig enableLatchUnsignaledConfig;
// must be called before clients can connect
void init() ANDROID_API;
@@ -281,12 +267,14 @@
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
- // Schedule an asynchronous or synchronous task on the main thread.
- template <typename F, typename T = std::invoke_result_t<F>>
- [[nodiscard]] std::future<T> schedule(F&&);
-
- // force full composition on all displays
- void repaintEverything();
+ // Schedule commit of transactions on the main thread ahead of the next VSYNC.
+ void scheduleCommit(FrameHint);
+ // As above, but also force composite regardless if transactions were committed.
+ void scheduleComposite(FrameHint) override;
+ // As above, but also force dirty geometry to repaint.
+ void scheduleRepaint();
+ // Schedule sampling independently from commit or composite.
+ void scheduleSample();
surfaceflinger::Factory& getFactory() { return mFactory; }
@@ -300,11 +288,6 @@
// utility function to delete a texture on the main thread
void deleteTextureAsync(uint32_t texture);
- // called on the main thread by MessageQueue when an internal message
- // is received
- // TODO: this should be made accessible only to MessageQueue
- void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime);
-
renderengine::RenderEngine& getRenderEngine() const;
bool authenticateSurfaceTextureLocked(
@@ -312,6 +295,7 @@
void onLayerFirstRef(Layer*);
void onLayerDestroyed(Layer*);
+ void onLayerUpdate();
void removeHierarchyFromOffscreenLayers(Layer* layer);
void removeFromOffscreenLayers(Layer* layer);
@@ -343,14 +327,6 @@
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
- virtual uint32_t setClientStateLocked(
- const FrameTimelineInfo& info, const ComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime,
- uint32_t permissions,
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
- REQUIRES(mStateLock);
- virtual void commitTransactionLocked();
-
virtual void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
REQUIRES(mStateLock);
@@ -360,6 +336,10 @@
return static_cast<bool>(findDisplay(p));
}
+ bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
+ return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
+ }
+
private:
friend class BufferLayer;
friend class BufferQueueLayer;
@@ -371,14 +351,13 @@
friend class MonitoredProducer;
friend class RefreshRateOverlay;
friend class RegionSamplingThread;
- friend class SurfaceTracing;
+ friend class LayerTracing;
// For unit tests
friend class TestableSurfaceFlinger;
friend class TransactionApplicationTest;
friend class TunnelModeEnabledReporterTest;
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using VsyncModulator = scheduler::VsyncModulator;
using TransactionSchedule = scheduler::TransactionSchedule;
using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
@@ -471,96 +450,6 @@
hal::Connection connection = hal::Connection::INVALID;
};
- class CountDownLatch {
- public:
- enum {
- eSyncTransaction = 1 << 0,
- eSyncInputWindows = 1 << 1,
- };
- explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
-
- // True if there is no waiting condition after count down.
- bool countDown(uint32_t flag) {
- std::unique_lock<std::mutex> lock(mMutex);
- if (mFlags == 0) {
- return true;
- }
- mFlags &= ~flag;
- if (mFlags == 0) {
- mCountDownComplete.notify_all();
- return true;
- }
- return false;
- }
-
- // Return true if triggered.
- bool wait_until(const std::chrono::seconds& timeout) const {
- std::unique_lock<std::mutex> lock(mMutex);
- const auto untilTime = std::chrono::system_clock::now() + timeout;
- while (mFlags != 0) {
- // Conditional variables can be woken up sporadically, so we check count
- // to verify the wakeup was triggered by |countDown|.
- if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
- return false;
- }
- }
- return true;
- }
-
- private:
- uint32_t mFlags;
- mutable std::condition_variable mCountDownComplete;
- mutable std::mutex mMutex;
- };
-
- struct TransactionState {
- TransactionState(const FrameTimelineInfo& frameTimelineInfo,
- const Vector<ComposerState>& composerStates,
- const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
- const sp<IBinder>& applyToken,
- const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
- int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
- int originUid, uint64_t transactionId)
- : frameTimelineInfo(frameTimelineInfo),
- states(composerStates),
- displays(displayStates),
- flags(transactionFlags),
- applyToken(applyToken),
- inputWindowCommands(inputWindowCommands),
- desiredPresentTime(desiredPresentTime),
- isAutoTimestamp(isAutoTimestamp),
- buffer(uncacheBuffer),
- postTime(postTime),
- permissions(permissions),
- hasListenerCallbacks(hasListenerCallbacks),
- listenerCallbacks(listenerCallbacks),
- originPid(originPid),
- originUid(originUid),
- id(transactionId) {}
-
- void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
-
- FrameTimelineInfo frameTimelineInfo;
- Vector<ComposerState> states;
- Vector<DisplayState> displays;
- uint32_t flags;
- sp<IBinder> applyToken;
- InputWindowCommands inputWindowCommands;
- const int64_t desiredPresentTime;
- const bool isAutoTimestamp;
- client_cache_t buffer;
- const int64_t postTime;
- uint32_t permissions;
- bool hasListenerCallbacks;
- std::vector<ListenerCallbacks> listenerCallbacks;
- int originPid;
- int originUid;
- uint64_t id;
- std::shared_ptr<CountDownLatch> transactionCommittedSignal;
- };
-
template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
static Dumper dumper(F&& dump) {
using namespace std::placeholders;
@@ -631,12 +520,10 @@
sp<IDisplayEventConnection> createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp,
ISurfaceComposer::EventRegistrationFlags eventRegistration = {}) override;
- status_t captureDisplay(const DisplayCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override;
- status_t captureDisplay(uint64_t displayOrLayerStack,
- const sp<IScreenCaptureListener>& captureListener) override;
- status_t captureLayers(const LayerCaptureArgs& args,
- const sp<IScreenCaptureListener>& captureListener) override;
+
+ status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&) override;
+ status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&) override;
+ status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override;
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
@@ -709,7 +596,6 @@
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) override;
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override;
@@ -729,9 +615,6 @@
// Implements IBinder::DeathRecipient.
void binderDied(const wp<IBinder>& who) override;
- // Implements RefBase.
- void onFirstRef() override;
-
// HWC2::ComposerCallback overrides:
void onComposerHalVsync(hal::HWDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos>) override;
@@ -741,16 +624,27 @@
const hal::VsyncPeriodChangeTimeline&) override;
void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
+ // ICompositor overrides:
+
+ // Commits transactions for layers and displays. Returns whether any state has been invalidated,
+ // i.e. whether a frame should be composited for each display.
+ bool commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) override;
+
+ // Composites a frame for each display. CompositionEngine performs GPU and/or HAL composition
+ // via RenderEngine and the Composer HAL, respectively.
+ void composite(nsecs_t frameTime) override;
+
+ // Samples the composited frame via RegionSamplingThread.
+ void sample() override;
+
/*
* ISchedulerCallback
*/
// Toggles hardware VSYNC by calling into HWC.
void setVsyncEnabled(bool) override;
- // Initiates a refresh rate change to be applied on invalidate.
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
- // Forces full composition on all displays without resetting the scheduler idle timer.
- void repaintEverythingForHWC() override;
+ // Initiates a refresh rate change to be applied on commit.
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
// Called when the frame rate override list changed to trigger an event.
@@ -763,26 +657,16 @@
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
- /*
- * Message handling
- */
- // Can only be called from the main thread or with mStateLock held
- void signalTransaction();
- // Can only be called from the main thread or with mStateLock held
- void signalLayerUpdate();
- void signalRefresh();
-
// Called on the main thread in response to initializeDisplays()
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
- status_t setActiveMode(const sp<IBinder>& displayToken, int id);
- // Once HWC has returned the present fence, this sets the active mode and a new refresh
- // rate in SF.
- void setActiveModeInternal() REQUIRES(mStateLock);
+ status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+ // Sets the active mode and a new refresh rate in SF.
+ void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
// Calls to setActiveMode on the main thread if there is a pending mode change
// that needs to be applied.
- void performSetActiveMode() REQUIRES(mStateLock);
+ void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
// Called when active mode is no longer is progress
void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
@@ -796,25 +680,18 @@
const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
EXCLUDES(mStateLock);
- // Handle the INVALIDATE message queue event, latching new buffers and applying
- // incoming transactions
- void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime);
+ void commitTransactions() EXCLUDES(mStateLock);
+ void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+ void doCommitTransactions() REQUIRES(mStateLock);
- // Returns whether the transaction actually modified any state
- bool handleMessageTransaction();
+ // Returns whether a new buffer has been latched.
+ bool latchBuffers();
- // Handle the REFRESH message queue event, sending the current frame down to RenderEngine and
- // the Composer HAL for presentation
- void onMessageRefresh();
-
- // Returns whether a new buffer has been latched (see handlePageFlip())
- bool handleMessageInvalidate();
-
- void handleTransaction(uint32_t transactionFlags);
- void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
+ void updateLayerGeometry();
void updateInputFlinger();
- void notifyWindowInfos();
+ void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
+ std::vector<gui::DisplayInfo>& outDisplayInfos);
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
@@ -822,16 +699,11 @@
void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
- /* handlePageFlip - latch a new buffer if available and compute the dirty
- * region. Returns whether a new buffer has been latched, i.e., whether it
- * is necessary to perform a refresh during this vsync.
- */
- bool handlePageFlip();
/*
* Transactions
*/
- void applyTransactionState(const FrameTimelineInfo& info, const Vector<ComposerState>& state,
+ bool applyTransactionState(const FrameTimelineInfo& info, const Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
@@ -841,27 +713,44 @@
int originPid, int originUid, uint64_t transactionId)
REQUIRES(mStateLock);
// flush pending transaction that was presented after desiredPresentTime.
- void flushTransactionQueues();
+ bool flushTransactionQueues(int64_t vsyncId);
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
- uint32_t getTransactionFlags(uint32_t flags);
- uint32_t peekTransactionFlags();
- // Can only be called from the main thread or with mStateLock held
- uint32_t setTransactionFlags(uint32_t flags);
+
+ uint32_t setClientStateLocked(const FrameTimelineInfo&, const ComposerState&,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ int64_t postTime, uint32_t permissions) REQUIRES(mStateLock);
+
+ uint32_t getTransactionFlags() const;
+
+ // Sets the masked bits, and returns the old flags.
+ uint32_t setTransactionFlags(uint32_t mask);
+
+ // Clears and returns the masked bits.
+ uint32_t clearTransactionFlags(uint32_t mask);
+
// Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
// where there are still pending transactions but we know they won't be ready until a frame
// arrives from a different layer. So we need to ensure we performTransaction from invalidate
// but there is no need to try and wake up immediately to do it. Rather we rely on
// onFrameAvailable or another layer update to wake us up.
void setTraversalNeeded();
- uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule, const sp<IBinder>& = {});
- void commitTransaction() REQUIRES(mStateLock);
+ uint32_t setTransactionFlags(uint32_t mask, TransactionSchedule,
+ const sp<IBinder>& applyToken = {});
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- bufferLayersReadyToPresent) const REQUIRES(mStateLock);
+ bufferLayersReadyToPresent,
+ bool allowLatchUnsignaled) const REQUIRES(mStateLock);
+ static LatchUnsignaledConfig getLatchUnsignaledConfig();
+ bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
+ bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
+ bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
+ REQUIRES(mStateLock);
+ bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
+ REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
@@ -869,35 +758,27 @@
/*
* Layer management
*/
- status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+ status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
const sp<IBinder>& parentHandle, int32_t* outLayerId,
const sp<Layer>& parentLayer = nullptr,
uint32_t* outTransformHint = nullptr);
- status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- PixelFormat& format, sp<IBinder>* outHandle,
- sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
+ status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
+ sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
+ sp<Layer>* outLayer);
- status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
+ sp<Layer>* outLayer);
- status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
- uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
+ status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
sp<Layer>* outLayer);
- status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ status_t createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
+ sp<Layer>* outLayer);
- status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+ status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
sp<IBinder>* outHandle, int32_t* outLayerId);
- std::string getUniqueLayerName(const char* name);
-
// called when all clients have released all their references to
// this layer meaning it is entirely safe to destroy all
// resources associated to this layer.
@@ -906,9 +787,8 @@
// add a layer to SurfaceFlinger
status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- bool addToRoot, uint32_t* outTransformHint);
+ const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+ uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
@@ -916,17 +796,17 @@
// Boot animation, on/off animations and screen capture
void startBootAnim();
- status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
- ui::PixelFormat, bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>&);
- status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
- const std::shared_ptr<renderengine::ExternalTexture>&,
- bool regionSampling, bool grayscale,
- const sp<IScreenCaptureListener>&);
- status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
- const std::shared_ptr<renderengine::ExternalTexture>&,
- bool canCaptureBlackoutContent, bool regionSampling,
- bool grayscale, ScreenCaptureResults&);
+ std::shared_future<renderengine::RenderEngineResult> captureScreenCommon(
+ RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize, ui::PixelFormat,
+ bool allowProtected, bool grayscale, const sp<IScreenCaptureListener>&);
+ std::shared_future<renderengine::RenderEngineResult> captureScreenCommon(
+ RenderAreaFuture, TraverseLayersFunction,
+ const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
+ bool grayscale, const sp<IScreenCaptureListener>&);
+ std::shared_future<renderengine::RenderEngineResult> renderScreenImplLocked(
+ const RenderArea&, TraverseLayersFunction,
+ const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
+ bool regionSampling, bool grayscale, ScreenCaptureResults&);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
@@ -934,11 +814,7 @@
void readPersistentProperties();
- bool exceedsMaxRenderTargetSize(uint32_t width, uint32_t height) const {
- return width > mMaxRenderTargetSize || height > mMaxRenderTargetSize;
- }
-
- int getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
+ uint32_t getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t uid) const;
/*
* Display and layer stack management
@@ -1009,8 +885,6 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
- sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock);
-
bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
return display->getDisplayToken() == mActiveDisplayToken;
}
@@ -1029,8 +903,6 @@
/*
* Compositing
*/
- void invalidateHwcGeometry();
-
void postComposition();
void getCompositorTiming(CompositorTiming* compositorTiming);
void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
@@ -1070,10 +942,6 @@
getHwComposer().setVsyncEnabled(id, enabled);
}
- // Sets the refresh rate by switching active configs, if they are available for
- // the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
-
struct FenceWithFenceTime {
sp<Fence> fence = Fence::NO_FENCE;
std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
@@ -1119,15 +987,19 @@
return {};
}
- // TODO(b/74619554): Remove special cases for primary display.
+ // TODO(b/182939859): SF conflates the primary (a.k.a. default) display with the first display
+ // connected at boot, which is typically internal. (Theoretically, it must be internal because
+ // SF does not support disconnecting it, though in practice HWC may circumvent this limitation.)
+ //
+ // SF inherits getInternalDisplayToken and getInternalDisplayId from ISurfaceComposer, so these
+ // locked counterparts are named consistently. Once SF supports headless mode and can designate
+ // any display as primary, the "internal" misnomer will be phased out.
sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
- const auto displayId = getInternalDisplayIdLocked();
- return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
+ return getPhysicalDisplayTokenLocked(getInternalDisplayIdLocked());
}
- std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
- const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
- return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
+ PhysicalDisplayId getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
+ return getHwComposer().getPrimaryDisplayId();
}
// Toggles use of HAL/GPU virtual displays.
@@ -1158,21 +1030,17 @@
void dumpStaticScreenStats(std::string& result) const;
// Not const because each Layer needs to query Fences and cache timestamps.
void dumpFrameEventsLocked(std::string& result);
-
- void recordBufferingStats(const std::string& layerName,
- std::vector<OccupancyTracker::Segment>&& history);
- void dumpBufferingStats(std::string& result) const;
void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
// Dumps state from HW Composer
void dumpHwc(std::string& result) const;
- LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
+ LayersProto dumpProtoFromMainThread(uint32_t traceFlags = LayerTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
@@ -1185,8 +1053,6 @@
return doDump(fd, args, asProto);
}
- void onFrameRateFlexibilityTokenReleased();
-
static mat4 calculateColorMatrix(float saturation);
void updateColorMatrixLocked();
@@ -1208,8 +1074,7 @@
/*
* Misc
*/
- std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId)
- REQUIRES(mStateLock);
+ std::vector<ui::ColorMode> getDisplayColorModes(const DisplayDevice&) REQUIRES(mStateLock);
static int calculateMaxAcquiredBufferCount(Fps refreshRate,
std::chrono::nanoseconds presentLatency);
@@ -1220,7 +1085,7 @@
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
-
+ pid_t mPid;
std::future<void> mRenderEnginePrimeCacheFuture;
// access must be protected by mStateLock
@@ -1229,29 +1094,26 @@
std::atomic<int32_t> mTransactionFlags = 0;
std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
bool mAnimTransactionPending = false;
+ std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
- bool mForceTraversal = false;
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
mat4 mClientColorMatrix;
- // Can't be unordered_set because wp<> isn't hashable
- std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
// If there are more GraphicBufferProducers tracked by SurfaceFlinger than
// this threshold, then begin logging.
size_t mGraphicBufferProducerListSizeLogThreshold =
static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
- void removeGraphicBufferProducerAsync(const wp<IBinder>&);
-
// protected by mStateLock (but we could use another lock)
bool mLayersRemoved = false;
bool mLayersAdded = false;
- std::atomic<bool> mRepaintEverything = false;
+ std::atomic_bool mMustComposite = false;
+ std::atomic_bool mGeometryDirty = false;
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
@@ -1278,7 +1140,6 @@
bool mSomeDataspaceChanged = false;
bool mForceTransactionDisplayChange = false;
- bool mGeometryInvalid = false;
bool mAnimCompositionPending = false;
// Tracks layers that have pending frames which are candidates for being
@@ -1313,20 +1174,21 @@
std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
} mVirtualDisplayIdGenerators;
- // don't use a lock for these, we don't care
- int mDebugRegion = 0;
- bool mDebugDisableHWC = false;
- bool mDebugDisableTransformHint = false;
+ std::atomic_uint mDebugFlashDelay = 0;
+ std::atomic_bool mDebugDisableHWC = false;
+ std::atomic_bool mDebugDisableTransformHint = false;
+ std::atomic<nsecs_t> mDebugInTransaction = 0;
+ std::atomic_bool mForceFullDamage = false;
+
bool mLayerCachingEnabled = false;
- volatile nsecs_t mDebugInTransaction = 0;
- bool mForceFullDamage = false;
bool mPropagateBackpressureClientComposition = false;
sp<SurfaceInterceptor> mInterceptor;
- SurfaceTracing mTracing{*this};
- std::mutex mTracingLock;
- bool mTracingEnabled = false;
- bool mTracePostComposition = false;
+ LayerTracing mLayerTracing{*this};
+ bool mLayerTracingEnabled = false;
+
+ TransactionTracing mTransactionTracing;
+ bool mTransactionTracingEnabled = false;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
@@ -1343,18 +1205,9 @@
TransactionCallbackInvoker mTransactionCallbackInvoker;
- // these are thread safe
- std::unique_ptr<MessageQueue> mEventQueue;
+ // Thread-safe.
FrameTracker mAnimFrameTracker;
- // protected by mDestroyedLayerLock;
- mutable Mutex mDestroyedLayerLock;
- Vector<Layer const *> mDestroyedLayers;
-
- nsecs_t mRefreshStartTime = 0;
-
- std::atomic<bool> mRefreshPending = false;
-
// 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).
@@ -1366,7 +1219,7 @@
Condition mTransactionQueueCV;
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
mPendingTransactionQueues GUARDED_BY(mQueueLock);
- std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+ std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
/*
* Feature prototyping
*/
@@ -1413,7 +1266,7 @@
/*
* Scheduler
*/
- std::unique_ptr<Scheduler> mScheduler;
+ std::unique_ptr<scheduler::Scheduler> mScheduler;
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
@@ -1442,15 +1295,12 @@
const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
- sp<os::IInputFlinger> mInputFlinger;
// Should only be accessed by the main thread.
+ sp<os::IInputFlinger> mInputFlinger;
InputWindowCommands mInputWindowCommands;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
- // This should only be accessed on the main thread.
- nsecs_t mFrameStartTime = 0;
-
void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock);
// Flag used to set override desired display mode from backdoor
@@ -1462,31 +1312,18 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- int mFrameRateFlexibilityTokenCount = 0;
-
- sp<IBinder> mDebugFrameRateFlexibilityToken;
-
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
mutable Mutex mCreatedLayersLock;
struct LayerCreatedState {
- LayerCreatedState(const wp<Layer>& layer, const wp<IBinder>& parent,
- const wp<Layer> parentLayer, const wp<IBinder>& producer, bool addToRoot)
- : layer(layer),
- initialParent(parent),
- initialParentLayer(parentLayer),
- initialProducer(producer),
- addToRoot(addToRoot) {}
+ LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
+ : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
wp<Layer> layer;
// Indicates the initial parent of the created layer, only used for creating layer in
// SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
- wp<IBinder> initialParent;
- wp<Layer> initialParentLayer;
- // Indicates the initial graphic buffer producer of the created layer, only used for
- // creating layer in SurfaceFlinger.
- wp<IBinder> initialProducer;
+ wp<Layer> initialParent;
// Indicates whether the layer getting created should be added at root if there's no parent
// and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
// be added offscreen.
@@ -1497,16 +1334,12 @@
// thread.
std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const wp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot);
+ const wp<Layer> parent, bool addToRoot);
auto getLayerCreatedState(const sp<IBinder>& handle);
sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
- void scheduleRegionSamplingThread();
- void notifyRegionSamplingThread();
-
bool isRefreshRateOverlayEnabled() const REQUIRES(mStateLock) {
return std::any_of(mDisplays.begin(), mDisplays.end(),
[](std::pair<wp<IBinder>, sp<DisplayDevice>> display) {
@@ -1517,6 +1350,13 @@
wp<IBinder> mActiveDisplayToken GUARDED_BY(mStateLock);
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
+
+ std::unique_ptr<FlagManager> mFlagManager;
+
+ // returns the framerate of the layer with the given sequence ID
+ float getLayerFramerate(nsecs_t now, int32_t id) const {
+ return mScheduler->getLayerFramerate(now, id);
+ }
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 89d1c4d..b81b445 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -38,7 +38,6 @@
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
@@ -51,10 +50,6 @@
return std::make_unique<android::impl::HWComposer>(serviceName);
}
-std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
- return std::make_unique<android::impl::MessageQueue>();
-}
-
std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
Fps currentRefreshRate) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
@@ -64,12 +59,6 @@
}
}
-std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback) {
- return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback);
-}
-
sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
return new android::impl::SurfaceInterceptor();
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index b8bf2ba..501629d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -27,11 +27,8 @@
virtual ~DefaultFactory();
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
- std::unique_ptr<MessageQueue> createMessageQueue() override;
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) override;
- std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override;
sp<SurfaceInterceptor> createSurfaceInterceptor() override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 13c95dd..6153e8e 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,41 +16,38 @@
#pragma once
-#include "Fps.h"
-
-#include <cutils/compiler.h>
-#include <utils/StrongPointer.h>
-
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+#include <scheduler/Fps.h>
+
namespace android {
typedef int32_t PixelFormat;
class BufferQueueLayer;
-class BufferStateLayer;
class BufferLayerConsumer;
-class EffectLayer;
+class BufferStateLayer;
class ContainerLayer;
class DisplayDevice;
+class EffectLayer;
class FrameTracer;
class GraphicBuffer;
class HWComposer;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class Layer;
-class MessageQueue;
-class Scheduler;
class StartPropertySetThread;
class SurfaceFlinger;
class SurfaceInterceptor;
class TimeStats;
struct DisplayDeviceCreationArgs;
-struct ISchedulerCallback;
struct LayerCreationArgs;
namespace compositionengine {
@@ -76,11 +73,8 @@
class Factory {
public:
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
- virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) = 0;
- virtual std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) = 0;
virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index e15eae8..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -195,17 +195,6 @@
return SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0;
}
-bool use_color_management(bool defaultValue) {
- auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management();
- auto tmpHasHDRDisplayVal = has_HDR_display(defaultValue);
- auto tmpHasWideColorDisplayVal = has_wide_color_display(defaultValue);
-
- auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement :
- defaultValue;
-
- return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal;
-}
-
int64_t default_composition_dataspace(Dataspace defaultValue) {
auto temp = SurfaceFlingerProperties::default_composition_dataspace();
if (temp.has_value()) {
@@ -315,14 +304,6 @@
return defaultValue;
}
-bool use_frame_rate_api(bool defaultValue) {
- auto temp = SurfaceFlingerProperties::use_frame_rate_api();
- if (temp.has_value()) {
- return *temp;
- }
- return defaultValue;
-}
-
bool enable_sdr_dimming(bool defaultValue) {
return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 039d316..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -59,8 +59,6 @@
SurfaceFlingerProperties::primary_display_orientation_values primary_display_orientation(
SurfaceFlingerProperties::primary_display_orientation_values defaultValue);
-bool use_color_management(bool defaultValue);
-
int64_t default_composition_dataspace(
android::hardware::graphics::common::V1_2::Dataspace defaultValue);
@@ -90,8 +88,6 @@
bool support_kernel_idle_timer(bool defaultValue);
-bool use_frame_rate_api(bool defaultValue);
-
int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
android::ui::DisplayPrimaries getDisplayNativePrimaries();
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 9be3abe..0782fef 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -323,11 +323,10 @@
}
void SurfaceInterceptor::addLayerStackLocked(Transaction* transaction, int32_t layerId,
- uint32_t layerStack)
-{
+ ui::LayerStack layerStack) {
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
LayerStackChange* layerStackChange(change->mutable_layer_stack());
- layerStackChange->set_layer_stack(layerStack);
+ layerStackChange->set_layer_stack(layerStack.id);
}
void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId,
@@ -568,12 +567,11 @@
}
}
-void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction,
- int32_t sequenceId, uint32_t layerStack)
-{
+void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
+ ui::LayerStack layerStack) {
DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId));
LayerStackChange* layerStackChange(dispChange->mutable_layer_stack());
- layerStackChange->set_layer_stack(layerStack);
+ layerStackChange->set_layer_stack(layerStack.id);
}
void SurfaceInterceptor::addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 7b331b9..970c3e5 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -160,7 +160,7 @@
void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
const Region& transRegion);
void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
- void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
+ void addLayerStackLocked(Transaction* transaction, int32_t layerId, ui::LayerStack);
void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
@@ -183,8 +183,7 @@
DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
void addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId,
const sp<const IGraphicBufferProducer>& surface);
- void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId,
- uint32_t layerStack);
+ void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, ui::LayerStack);
void addDisplayFlagsLocked(Transaction* transaction, int32_t sequenceId, uint32_t flags);
void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w,
uint32_t h);
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
deleted file mode 100644
index 5963737..0000000
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2017 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 "SurfaceTracing"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "SurfaceTracing.h"
-#include <SurfaceFlinger.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/SystemClock.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
-
-bool SurfaceTracing::enable() {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- return false;
- }
-
- if (flagIsSet(TRACE_SYNC)) {
- runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
- } else {
- runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
- mFlinger.mTracingLock);
- }
- mEnabled = true;
- return true;
-}
-
-bool SurfaceTracing::disable() {
- std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return false;
- }
- mEnabled = false;
- runner->stop();
- return true;
-}
-
-bool SurfaceTracing::isEnabled() const {
- std::scoped_lock lock(mTraceLock);
- return mEnabled;
-}
-
-status_t SurfaceTracing::writeToFile() {
- std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return STATUS_OK;
- }
- return runner->writeToFile();
-}
-
-void SurfaceTracing::notify(const char* where) {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- runner->notify(where);
- }
-}
-
-void SurfaceTracing::notifyLocked(const char* where) {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- runner->notifyLocked(where);
- }
-}
-
-void SurfaceTracing::dump(std::string& result) const {
- std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
- if (mEnabled) {
- runner->dump(result);
- }
-}
-
-void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
- // use the swap trick to make sure memory is released
- std::queue<LayersTraceProto>().swap(mStorage);
- mSizeInBytes = newSize;
- mUsedInBytes = 0U;
-}
-
-void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
- size_t protoSize = static_cast<size_t>(proto.ByteSize());
- while (mUsedInBytes + protoSize > mSizeInBytes) {
- if (mStorage.empty()) {
- return;
- }
- mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
- mStorage.pop();
- }
- mUsedInBytes += protoSize;
- mStorage.emplace();
- mStorage.back().Swap(&proto);
-}
-
-void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
- fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
-
- while (!mStorage.empty()) {
- auto entry = fileProto->add_entry();
- entry->Swap(&mStorage.front());
- mStorage.pop();
- }
-}
-
-SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
- : mFlinger(flinger), mConfig(config) {
- mBuffer.setSize(mConfig.bufferSize);
-}
-
-void SurfaceTracing::Runner::notify(const char* where) {
- LayersTraceProto entry = traceLayers(where);
- mBuffer.emplace(std::move(entry));
-}
-
-status_t SurfaceTracing::Runner::stop() {
- return writeToFile();
-}
-
-LayersTraceFileProto SurfaceTracing::createLayersTraceFileProto() {
- LayersTraceFileProto fileProto;
- fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
- LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
- return fileProto;
-}
-
-status_t SurfaceTracing::Runner::writeToFile() {
- ATRACE_CALL();
-
- LayersTraceFileProto fileProto = createLayersTraceFileProto();
- std::string output;
-
- mBuffer.flush(&fileProto);
- mBuffer.reset(mConfig.bufferSize);
-
- if (!fileProto.SerializeToString(&output)) {
- ALOGE("Could not save the proto file! Permission denied");
- return PERMISSION_DENIED;
- }
-
- // -rw-r--r--
- const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
- if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
- true)) {
- ALOGE("Could not save the proto file! There are missing fields");
- return PERMISSION_DENIED;
- }
-
- return NO_ERROR;
-}
-
-LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
- ATRACE_CALL();
-
- LayersTraceProto entry;
- entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
- entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
-
- if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
- mFlinger.dumpOffscreenLayersProto(layers);
- }
- entry.mutable_layers()->Swap(&layers);
-
- if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
- std::string hwcDump;
- mFlinger.dumpHwc(hwcDump);
- entry.set_hwc_blob(hwcDump);
- }
- if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
- entry.set_excludes_composition_state(true);
- }
- entry.set_missed_entries(mMissedTraceEntries);
- mFlinger.dumpDisplayProto(entry);
- return entry;
-}
-
-void SurfaceTracing::Runner::dump(std::string& result) const {
- base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
- mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
- float(mBuffer.size()) / float(1_MB));
-}
-
-SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
- std::mutex& sfLock)
- : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
- mEnabled = true;
- mThread = std::thread(&AsyncRunner::loop, this);
-}
-
-void SurfaceTracing::AsyncRunner::loop() {
- while (mEnabled) {
- LayersTraceProto entry;
- bool entryAdded = traceWhenNotified(&entry);
- if (entryAdded) {
- mBuffer.emplace(std::move(entry));
- }
- if (mWriteToFile) {
- Runner::writeToFile();
- mWriteToFile = false;
- }
- }
-}
-
-bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
- std::unique_lock<std::mutex> lock(mSfLock);
- mCanStartTrace.wait(lock);
- if (!mAddEntry) {
- return false;
- }
- *outProto = traceLayers(mWhere);
- mAddEntry = false;
- mMissedTraceEntries = 0;
- return true;
-}
-
-void SurfaceTracing::AsyncRunner::notify(const char* where) {
- std::scoped_lock lock(mSfLock);
- notifyLocked(where);
-}
-
-void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
- mWhere = where;
- if (mAddEntry) {
- mMissedTraceEntries++;
- }
- mAddEntry = true;
- mCanStartTrace.notify_one();
-}
-
-status_t SurfaceTracing::AsyncRunner::writeToFile() {
- mWriteToFile = true;
- mCanStartTrace.notify_one();
- return STATUS_OK;
-}
-
-status_t SurfaceTracing::AsyncRunner::stop() {
- mEnabled = false;
- mCanStartTrace.notify_one();
- mThread.join();
- return Runner::writeToFile();
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
deleted file mode 100644
index cea1a33..0000000
--- a/services/surfaceflinger/SurfaceTracing.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2017 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 <android-base/thread_annotations.h>
-#include <layerproto/LayerProtoHeader.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-class SurfaceFlinger;
-constexpr auto operator""_MB(unsigned long long const num) {
- return num * 1024 * 1024;
-}
-/*
- * SurfaceTracing records layer states during surface flinging. Manages tracing state and
- * configuration.
- */
-class SurfaceTracing {
-public:
- SurfaceTracing(SurfaceFlinger& flinger);
- bool enable();
- bool disable();
- status_t writeToFile();
- bool isEnabled() const;
- /*
- * Adds a trace entry, must be called from the drawing thread or while holding the
- * SurfaceFlinger tracing lock.
- */
- void notify(const char* where);
- /*
- * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
- */
- void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
-
- void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
- void dump(std::string& result) const;
-
- enum : uint32_t {
- TRACE_CRITICAL = 1 << 0,
- TRACE_INPUT = 1 << 1,
- TRACE_COMPOSITION = 1 << 2,
- TRACE_EXTRA = 1 << 3,
- TRACE_HWC = 1 << 4,
- // Add non-geometry composition changes to the trace.
- TRACE_BUFFERS = 1 << 5,
- // Add entries from the drawing thread post composition.
- TRACE_SYNC = 1 << 6,
- TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
- };
- void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
- bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
- static LayersTraceFileProto createLayersTraceFileProto();
-
-private:
- class Runner;
- static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
- static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
- SurfaceFlinger& mFlinger;
- mutable std::mutex mTraceLock;
- bool mEnabled GUARDED_BY(mTraceLock) = false;
- std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock);
-
- struct Config {
- uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC;
- size_t bufferSize = DEFAULT_BUFFER_SIZE;
- } mConfig;
-
- /*
- * ring buffer.
- */
- class LayersTraceBuffer {
- public:
- size_t size() const { return mSizeInBytes; }
- size_t used() const { return mUsedInBytes; }
- size_t frameCount() const { return mStorage.size(); }
-
- void setSize(size_t newSize) { mSizeInBytes = newSize; }
- void reset(size_t newSize);
- void emplace(LayersTraceProto&& proto);
- void flush(LayersTraceFileProto* fileProto);
-
- private:
- size_t mUsedInBytes = 0U;
- size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
- std::queue<LayersTraceProto> mStorage;
- };
-
- /*
- * Implements a synchronous way of adding trace entries. This must be called
- * from the drawing thread.
- */
- class Runner {
- public:
- Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
- virtual ~Runner() = default;
- virtual status_t stop();
- virtual status_t writeToFile();
- virtual void notify(const char* where);
- /* Cannot be called with a synchronous runner. */
- virtual void notifyLocked(const char* /* where */) {}
- void dump(std::string& result) const;
-
- protected:
- bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
- SurfaceFlinger& mFlinger;
- SurfaceTracing::Config mConfig;
- SurfaceTracing::LayersTraceBuffer mBuffer;
- uint32_t mMissedTraceEntries = 0;
- LayersTraceProto traceLayers(const char* where);
- };
-
- /*
- * Implements asynchronous way to add trace entries called from a separate thread while holding
- * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
- * scheduled in time.
- */
- class AsyncRunner : public Runner {
- public:
- AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
- virtual ~AsyncRunner() = default;
- status_t stop() override;
- status_t writeToFile() override;
- void notify(const char* where) override;
- void notifyLocked(const char* where);
-
- private:
- std::mutex& mSfLock;
- std::condition_variable mCanStartTrace;
- std::thread mThread;
- const char* mWhere = "";
- bool mWriteToFile = false;
- bool mEnabled = false;
- bool mAddEntry = false;
- void loop();
- bool traceWhenNotified(LayersTraceProto* outProto);
- };
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index bcc3e4e..4686eed 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -12,6 +12,9 @@
srcs: [
"TimeStats.cpp",
],
+ header_libs: [
+ "libscheduler_headers",
+ ],
shared_libs: [
"android.hardware.graphics.composer@2.4",
"libbase",
@@ -24,6 +27,9 @@
"libutils",
],
export_include_dirs: ["."],
+ export_header_lib_headers: [
+ "libscheduler_headers",
+ ],
export_shared_lib_headers: [
"libtimestats_proto",
],
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 7c1f21f..b1a2bda 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <unordered_map>
#undef LOG_TAG
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +27,7 @@
#include <algorithm>
#include <chrono>
+#include <unordered_map>
#include "TimeStats.h"
#include "timestatsproto/TimeStatsHelper.h"
@@ -58,15 +58,15 @@
return histogramProto;
}
-SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) {
+SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) {
switch (gameMode) {
- case TimeStatsHelper::GameModeUnsupported:
+ case GameMode::Unsupported:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED;
- case TimeStatsHelper::GameModeStandard:
+ case GameMode::Standard:
return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD;
- case TimeStatsHelper::GameModePerformance:
+ case GameMode::Performance:
return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
- case TimeStatsHelper::GameModeBattery:
+ case GameMode::Battery:
return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
default:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
@@ -454,7 +454,7 @@
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
SetFrameRateVote frameRateVote,
- int32_t gameMode) {
+ GameMode gameMode) {
ATRACE_CALL();
ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
@@ -554,7 +554,7 @@
}
bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName,
- int32_t gameMode) {
+ GameMode gameMode) {
uint32_t layerRecords = 0;
for (const auto& record : mTimeStats.stats) {
if (record.second.stats.count({uid, layerName, gameMode}) > 0) {
@@ -564,11 +564,11 @@
layerRecords += record.second.stats.size();
}
- return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+ return layerRecords < MAX_NUM_LAYER_STATS;
}
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime, int32_t gameMode) {
+ uid_t uid, nsecs_t postTime, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -718,7 +718,7 @@
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) {
+ SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -744,7 +744,7 @@
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) {
+ SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -823,7 +823,7 @@
// the first jank record is not dropped.
static const std::string kDefaultLayerName = "none";
- static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported;
+ constexpr GameMode kDefaultGameMode = GameMode::Unsupported;
const int32_t refreshRateBucket =
clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 9e70684..77c7973 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,21 +17,22 @@
#pragma once
#include <cstdint>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <variant>
-#include <../Fps.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
#include <utils/String16.h>
#include <utils/Vector.h>
-#include <deque>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <variant>
+#include <scheduler/Fps.h>
using namespace android::surfaceflinger;
@@ -79,7 +80,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
+ uid_t uid, nsecs_t postTime, GameMode) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -101,11 +102,11 @@
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+ SetFrameRateVote frameRateVote, GameMode) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+ SetFrameRateVote frameRateVote, GameMode) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -123,19 +124,21 @@
std::optional<Fps> renderRate;
uid_t uid = 0;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
int32_t reasons = 0;
nsecs_t displayDeadlineDelta = 0;
nsecs_t displayPresentJitter = 0;
nsecs_t appDeadlineDelta = 0;
+ static bool isOptApproxEqual(std::optional<Fps> lhs, std::optional<Fps> rhs) {
+ return (!lhs && !rhs) || (lhs && rhs && isApproxEqual(*lhs, *rhs));
+ }
+
bool operator==(const JankyFramesInfo& o) const {
- return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
- ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
- (renderRate != std::nullopt && o.renderRate != std::nullopt &&
- Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
- uid == o.uid && layerName == o.layerName && gameMode == o.gameMode &&
- reasons == o.reasons && displayDeadlineDelta == o.displayDeadlineDelta &&
+ return isApproxEqual(refreshRate, o.refreshRate) &&
+ isOptApproxEqual(renderRate, o.renderRate) && uid == o.uid &&
+ layerName == o.layerName && gameMode == o.gameMode && reasons == o.reasons &&
+ displayDeadlineDelta == o.displayDeadlineDelta &&
displayPresentJitter == o.displayPresentJitter &&
appDeadlineDelta == o.appDeadlineDelta;
}
@@ -192,7 +195,7 @@
struct LayerRecord {
uid_t uid;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
@@ -245,7 +248,7 @@
const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
- nsecs_t postTime, int32_t gameMode) override;
+ nsecs_t postTime, GameMode) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
void incrementBadDesiredPresent(int32_t layerId) override;
@@ -254,12 +257,11 @@
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) override;
+ Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote,
+ GameMode) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
- int32_t gameMode) override;
+ std::optional<Fps> renderRate, SetFrameRateVote, GameMode) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -280,11 +282,11 @@
bool populateLayerAtom(std::string* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode);
+ std::optional<Fps> renderRate, SetFrameRateVote,
+ GameMode);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
- bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, GameMode);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index ffb2f09..69afa2a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -16,9 +16,10 @@
#include "timestatsproto/TimeStatsHelper.h"
#include <android-base/stringprintf.h>
-#include <inttypes.h>
+#include <ftl/enum.h>
#include <array>
+#include <cinttypes>
#define HISTOGRAM_SIZE 85
@@ -91,51 +92,15 @@
return result;
}
-std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
- switch (compatibility) {
- case FrameRateCompatibility::Undefined:
- return "Undefined";
- case FrameRateCompatibility::Default:
- return "Default";
- case FrameRateCompatibility::ExactOrMultiple:
- return "ExactOrMultiple";
- }
-}
-
-std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
- switch (seamlessness) {
- case Seamlessness::Undefined:
- return "Undefined";
- case Seamlessness::ShouldBeSeamless:
- return "ShouldBeSeamless";
- case Seamlessness::NotRequired:
- return "NotRequired";
- }
-}
-
std::string TimeStatsHelper::SetFrameRateVote::toString() const {
std::string result;
StringAppendF(&result, "frameRate = %.2f\n", frameRate);
StringAppendF(&result, "frameRateCompatibility = %s\n",
- toString(frameRateCompatibility).c_str());
- StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+ ftl::enum_string(frameRateCompatibility).c_str());
+ StringAppendF(&result, "seamlessness = %s\n", ftl::enum_string(seamlessness).c_str());
return result;
}
-std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const {
- switch (gameMode) {
- case TimeStatsHelper::GameModeUnsupported:
- return "GameModeUnsupported";
- case TimeStatsHelper::GameModeStandard:
- return "GameModeStandard";
- case TimeStatsHelper::GameModePerformance:
- return "GameModePerformance";
- case TimeStatsHelper::GameModeBattery:
- return "GameModeBattery";
- default:
- return "GameModeUnspecified";
- }
-}
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -143,7 +108,7 @@
StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
- StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str());
+ StringAppendF(&result, "gameMode = %s\n", ftl::enum_string(gameMode).c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 2afff8d..438561c 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <gui/LayerMetadata.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <utils/Timers.h>
@@ -63,6 +64,8 @@
Undefined = 0,
Default = 1,
ExactOrMultiple = 2,
+
+ ftl_last = ExactOrMultiple
} frameRateCompatibility = FrameRateCompatibility::Undefined;
// Needs to be in sync with atoms.proto
@@ -70,25 +73,13 @@
Undefined = 0,
ShouldBeSeamless = 1,
NotRequired = 2,
+
+ ftl_last = NotRequired
} seamlessness = Seamlessness::Undefined;
- static std::string toString(FrameRateCompatibility);
- static std::string toString(Seamlessness);
std::string toString() const;
};
- /**
- * GameMode of the layer. GameModes are set by SysUI through WMShell.
- * Actual game mode definitions are managed by GameManager.java
- * The values defined here should always be in sync with the ones in GameManager.
- */
- enum GameMode {
- GameModeUnsupported = 0,
- GameModeStandard = 1,
- GameModePerformance = 2,
- GameModeBattery = 3,
- };
-
class TimeStatsLayer {
public:
uid_t uid;
@@ -96,7 +87,7 @@
std::string packageName;
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
@@ -106,7 +97,6 @@
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
- std::string toString(int32_t gameMode) const;
SFTimeStatsLayerProto toProto() const;
};
@@ -137,13 +127,14 @@
struct LayerStatsKey {
uid_t uid = 0;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
struct Hasher {
size_t operator()(const LayerStatsKey& key) const {
size_t uidHash = std::hash<uid_t>{}(key.uid);
size_t layerNameHash = std::hash<std::string>{}(key.layerName);
- size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
+ using T = std::underlying_type_t<GameMode>;
+ size_t gameModeHash = std::hash<T>{}(static_cast<T>(key.gameMode));
return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
}
};
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
new file mode 100644
index 0000000..d136e0b
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 "LayerTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <SurfaceFlinger.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "LayerTracing.h"
+#include "RingBuffer.h"
+
+namespace android {
+
+LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
+ mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
+}
+
+LayerTracing::~LayerTracing() = default;
+
+bool LayerTracing::enable() {
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ return false;
+ }
+ mBuffer->setSize(mBufferSizeInBytes);
+ mEnabled = true;
+ return true;
+}
+
+bool LayerTracing::disable() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+ LayersTraceFileProto fileProto = createTraceFileProto();
+ mBuffer->writeToFile(fileProto, FILE_NAME);
+ mBuffer->reset();
+ return true;
+}
+
+bool LayerTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
+ return mEnabled;
+}
+
+status_t LayerTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ LayersTraceFileProto fileProto = createTraceFileProto();
+ return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void LayerTracing::setTraceFlags(uint32_t flags) {
+ std::scoped_lock lock(mTraceLock);
+ mFlags = flags;
+}
+
+void LayerTracing::setBufferSize(size_t bufferSizeInBytes) {
+ std::scoped_lock lock(mTraceLock);
+ mBufferSizeInBytes = bufferSizeInBytes;
+}
+
+bool LayerTracing::flagIsSet(uint32_t flags) const {
+ return (mFlags & flags) == flags;
+}
+
+LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+ LayersTraceFileProto fileProto;
+ fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ return fileProto;
+}
+
+void LayerTracing::dump(std::string& result) const {
+ std::scoped_lock lock(mTraceLock);
+ base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ mBuffer->dump(result);
+}
+
+void LayerTracing::notify(const char* where) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ LayersTraceProto entry;
+ entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+ entry.set_where(where);
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
+
+ if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
+ mFlinger.dumpOffscreenLayersProto(layers);
+ }
+ entry.mutable_layers()->Swap(&layers);
+
+ if (flagIsSet(LayerTracing::TRACE_HWC)) {
+ std::string hwcDump;
+ mFlinger.dumpHwc(hwcDump);
+ entry.set_hwc_blob(hwcDump);
+ }
+ if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
+ entry.set_excludes_composition_state(true);
+ }
+ mFlinger.dumpDisplayProto(entry);
+ mBuffer->emplace(std::move(entry));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
new file mode 100644
index 0000000..8ca3587
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <layerproto/LayerProtoHeader.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+
+/*
+ * LayerTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
+ */
+class LayerTracing {
+public:
+ LayerTracing(SurfaceFlinger& flinger);
+ ~LayerTracing();
+ bool enable();
+ bool disable();
+ bool isEnabled() const;
+ status_t writeToFile();
+ LayersTraceFileProto createTraceFileProto() const;
+ void notify(const char* where);
+
+ enum : uint32_t {
+ TRACE_INPUT = 1 << 1,
+ TRACE_COMPOSITION = 1 << 2,
+ TRACE_EXTRA = 1 << 3,
+ TRACE_HWC = 1 << 4,
+ TRACE_BUFFERS = 1 << 5,
+ TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
+ };
+ void setTraceFlags(uint32_t flags);
+ bool flagIsSet(uint32_t flags) const;
+ void setBufferSize(size_t bufferSizeInBytes);
+ void dump(std::string&) const;
+
+private:
+ static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
+
+ SurfaceFlinger& mFlinger;
+ uint32_t mFlags = TRACE_INPUT;
+ mutable std::mutex mTraceLock;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
+ GUARDED_BY(mTraceLock);
+ size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
new file mode 100644
index 0000000..3b2626d
--- /dev/null
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <android-base/file.h>
+#include <android-base/stringprintf.h>
+
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <queue>
+
+namespace android {
+
+class SurfaceFlinger;
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer {
+public:
+ size_t size() const { return mSizeInBytes; }
+ size_t used() const { return mUsedInBytes; }
+ size_t frameCount() const { return mStorage.size(); }
+ void setSize(size_t newSize) { mSizeInBytes = newSize; }
+ const std::string& front() const { return mStorage.front(); }
+ const std::string& back() const { return mStorage.back(); }
+
+ void reset() {
+ // use the swap trick to make sure memory is released
+ std::deque<std::string>().swap(mStorage);
+ mUsedInBytes = 0U;
+ }
+
+ void writeToProto(FileProto& fileProto) {
+ fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+ fileProto.entry().size());
+ for (const std::string& entry : mStorage) {
+ EntryProto* entryProto = fileProto.add_entry();
+ entryProto->ParseFromString(entry);
+ }
+ }
+
+ status_t writeToFile(FileProto& fileProto, std::string filename) {
+ ATRACE_CALL();
+ writeToProto(fileProto);
+ std::string output;
+ if (!fileProto.SerializeToString(&output)) {
+ ALOGE("Could not serialize proto.");
+ return UNKNOWN_ERROR;
+ }
+
+ // -rw-r--r--
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
+ ALOGE("Could not save the proto file.");
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+ }
+
+ std::vector<std::string> emplace(std::string&& serializedProto) {
+ std::vector<std::string> replacedEntries;
+ size_t protoSize = static_cast<size_t>(serializedProto.size());
+ while (mUsedInBytes + protoSize > mSizeInBytes) {
+ if (mStorage.empty()) {
+ return {};
+ }
+ mUsedInBytes -= static_cast<size_t>(mStorage.front().size());
+ replacedEntries.emplace_back(mStorage.front());
+ mStorage.pop_front();
+ }
+ mUsedInBytes += protoSize;
+ mStorage.emplace_back(serializedProto);
+ return replacedEntries;
+ }
+
+ std::vector<std::string> emplace(EntryProto&& proto) {
+ std::string serializedProto;
+ proto.SerializeToString(&serializedProto);
+ return emplace(std::move(serializedProto));
+ }
+
+ void dump(std::string& result) const {
+ std::chrono::milliseconds duration(0);
+ if (frameCount() > 0) {
+ EntryProto entry;
+ entry.ParseFromString(mStorage.front());
+ duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::nanoseconds(systemTime() - entry.elapsed_realtime_nanos()));
+ }
+ const int64_t durationCount = duration.count();
+ base::StringAppendF(&result,
+ " number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n",
+ frameCount(), float(used()) / (1024.f * 1024.f),
+ float(size()) / (1024.f * 1024.f), durationCount);
+ }
+
+private:
+ size_t mUsedInBytes = 0U;
+ size_t mSizeInBytes = 0U;
+ std::deque<std::string> mStorage;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
new file mode 100644
index 0000000..378deb0
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <gui/SurfaceComposerClient.h>
+#include <ui/Rect.h>
+
+#include "LayerProtoHelper.h"
+#include "TransactionProtoParser.h"
+
+namespace android::surfaceflinger {
+
+proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t,
+ LayerHandleToIdFn getLayerId,
+ DisplayHandleToIdFn getDisplayId) {
+ proto::TransactionState proto;
+ proto.set_pid(t.originPid);
+ proto.set_uid(t.originUid);
+ proto.set_vsync_id(t.frameTimelineInfo.vsyncId);
+ proto.set_input_event_id(t.frameTimelineInfo.inputEventId);
+ proto.set_post_time(t.postTime);
+
+ for (auto& layerState : t.states) {
+ proto.mutable_layer_changes()->Add(std::move(toProto(layerState.state, getLayerId)));
+ }
+
+ for (auto& displayState : t.displays) {
+ proto.mutable_display_changes()->Add(std::move(toProto(displayState, getDisplayId)));
+ }
+ return proto;
+}
+
+proto::TransactionState TransactionProtoParser::toProto(
+ const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+ proto::TransactionState proto;
+ for (auto& [layerId, state] : states) {
+ proto::LayerState layerProto = toProto(state, nullptr);
+ if (layerProto.has_buffer_data()) {
+ proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
+ bufferProto->set_buffer_id(state.bufferId);
+ bufferProto->set_width(state.bufferWidth);
+ bufferProto->set_height(state.bufferHeight);
+ }
+ layerProto.set_has_sideband_stream(state.hasSidebandStream);
+ layerProto.set_layer_id(state.layerId);
+ layerProto.set_parent_id(state.parentId);
+ layerProto.set_relative_parent_id(state.relativeParentId);
+ if (layerProto.has_window_info_handle()) {
+ layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
+ }
+ proto.mutable_layer_changes()->Add(std::move(layerProto));
+ }
+ return proto;
+}
+
+proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer,
+ LayerHandleToIdFn getLayerId) {
+ proto::LayerState proto;
+ if (getLayerId != nullptr) {
+ proto.set_layer_id(getLayerId(layer.surface));
+ } else {
+ proto.set_layer_id(layer.layerId);
+ }
+
+ proto.set_what(layer.what);
+
+ if (layer.what & layer_state_t::ePositionChanged) {
+ proto.set_x(layer.x);
+ proto.set_y(layer.y);
+ }
+ if (layer.what & layer_state_t::eLayerChanged) {
+ proto.set_z(layer.z);
+ }
+ if (layer.what & layer_state_t::eSizeChanged) {
+ proto.set_w(layer.w);
+ proto.set_h(layer.h);
+ }
+ if (layer.what & layer_state_t::eLayerStackChanged) {
+ proto.set_layer_stack(layer.layerStack.id);
+ }
+ if (layer.what & layer_state_t::eFlagsChanged) {
+ proto.set_flags(layer.flags);
+ proto.set_mask(layer.mask);
+ }
+ if (layer.what & layer_state_t::eMatrixChanged) {
+ proto::LayerState_Matrix22* matrixProto = proto.mutable_matrix();
+ matrixProto->set_dsdx(layer.matrix.dsdx);
+ matrixProto->set_dsdy(layer.matrix.dsdy);
+ matrixProto->set_dtdx(layer.matrix.dtdx);
+ matrixProto->set_dtdy(layer.matrix.dtdy);
+ }
+ if (layer.what & layer_state_t::eCornerRadiusChanged) {
+ proto.set_corner_radius(layer.cornerRadius);
+ }
+ if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ proto.set_background_blur_radius(layer.backgroundBlurRadius);
+ }
+
+ if (layer.what & layer_state_t::eAlphaChanged) {
+ proto.set_alpha(layer.alpha);
+ }
+
+ if (layer.what & layer_state_t::eColorChanged) {
+ proto::LayerState_Color3* colorProto = proto.mutable_color();
+ colorProto->set_r(layer.color.r);
+ colorProto->set_g(layer.color.g);
+ colorProto->set_b(layer.color.b);
+ }
+ if (layer.what & layer_state_t::eTransparentRegionChanged) {
+ LayerProtoHelper::writeToProto(layer.transparentRegion, proto.mutable_transparent_region());
+ }
+ if (layer.what & layer_state_t::eTransformChanged) {
+ proto.set_transform(layer.transform);
+ }
+ if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
+ proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
+ }
+ if (layer.what & layer_state_t::eCropChanged) {
+ LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+ }
+ if (layer.what & layer_state_t::eBufferChanged) {
+ proto::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
+ if (layer.bufferData.buffer) {
+ bufferProto->set_buffer_id(layer.bufferData.buffer->getId());
+ bufferProto->set_width(layer.bufferData.buffer->getWidth());
+ bufferProto->set_height(layer.bufferData.buffer->getHeight());
+ }
+ bufferProto->set_frame_number(layer.bufferData.frameNumber);
+ bufferProto->set_flags(layer.bufferData.flags.get());
+ bufferProto->set_cached_buffer_id(layer.bufferData.cachedBuffer.id);
+ }
+ if (layer.what & layer_state_t::eSidebandStreamChanged) {
+ proto.set_has_sideband_stream(layer.sidebandStream != nullptr);
+ }
+
+ if (layer.what & layer_state_t::eApiChanged) {
+ proto.set_api(layer.api);
+ }
+
+ if (layer.what & layer_state_t::eColorTransformChanged) {
+ LayerProtoHelper::writeToProto(layer.colorTransform, proto.mutable_color_transform());
+ }
+ if (layer.what & layer_state_t::eBlurRegionsChanged) {
+ for (auto& region : layer.blurRegions) {
+ LayerProtoHelper::writeToProto(region, proto.add_blur_regions());
+ }
+ }
+
+ if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) {
+ int32_t layerId = layer.parentSurfaceControlForChild
+ ? getLayerId(layer.parentSurfaceControlForChild->getHandle())
+ : -1;
+ proto.set_parent_id(layerId);
+ }
+ if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) {
+ int32_t layerId = layer.relativeLayerSurfaceControl
+ ? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
+ : -1;
+ proto.set_relative_parent_id(layerId);
+ }
+
+ if (layer.what & layer_state_t::eInputInfoChanged) {
+ if (layer.windowInfoHandle) {
+ const gui::WindowInfo* inputInfo = layer.windowInfoHandle->getInfo();
+ proto::LayerState_WindowInfo* windowInfoProto = proto.mutable_window_info_handle();
+ windowInfoProto->set_layout_params_flags(inputInfo->flags.get());
+ windowInfoProto->set_layout_params_type(static_cast<int32_t>(inputInfo->type));
+ LayerProtoHelper::writeToProto(inputInfo->touchableRegion,
+ windowInfoProto->mutable_touchable_region());
+ windowInfoProto->set_surface_inset(inputInfo->surfaceInset);
+ windowInfoProto->set_focusable(inputInfo->focusable);
+ windowInfoProto->set_has_wallpaper(inputInfo->hasWallpaper);
+ windowInfoProto->set_global_scale_factor(inputInfo->globalScaleFactor);
+ proto::LayerState_Transform* transformProto = windowInfoProto->mutable_transform();
+ transformProto->set_dsdx(inputInfo->transform.dsdx());
+ transformProto->set_dtdx(inputInfo->transform.dtdx());
+ transformProto->set_dtdy(inputInfo->transform.dtdy());
+ transformProto->set_dsdy(inputInfo->transform.dsdy());
+ transformProto->set_tx(inputInfo->transform.tx());
+ transformProto->set_ty(inputInfo->transform.ty());
+ windowInfoProto->set_replace_touchable_region_with_crop(
+ inputInfo->replaceTouchableRegionWithCrop);
+ if (getLayerId != nullptr) {
+ windowInfoProto->set_crop_layer_id(
+ getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+ } else {
+ windowInfoProto->set_crop_layer_id(-1);
+ }
+ }
+ }
+ if (layer.what & layer_state_t::eBackgroundColorChanged) {
+ proto.set_bg_color_alpha(layer.bgColorAlpha);
+ proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace));
+ proto::LayerState_Color3* colorProto = proto.mutable_color();
+ colorProto->set_r(layer.color.r);
+ colorProto->set_g(layer.color.g);
+ colorProto->set_b(layer.color.b);
+ }
+ if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
+ proto.set_color_space_agnostic(layer.colorSpaceAgnostic);
+ }
+ if (layer.what & layer_state_t::eShadowRadiusChanged) {
+ proto.set_shadow_radius(layer.shadowRadius);
+ }
+ if (layer.what & layer_state_t::eFrameRateSelectionPriority) {
+ proto.set_frame_rate_selection_priority(layer.frameRateSelectionPriority);
+ }
+ if (layer.what & layer_state_t::eFrameRateChanged) {
+ proto.set_frame_rate(layer.frameRate);
+ proto.set_frame_rate_compatibility(layer.frameRateCompatibility);
+ proto.set_change_frame_rate_strategy(layer.changeFrameRateStrategy);
+ }
+ if (layer.what & layer_state_t::eFixedTransformHintChanged) {
+ proto.set_fixed_transform_hint(layer.fixedTransformHint);
+ }
+ if (layer.what & layer_state_t::eAutoRefreshChanged) {
+ proto.set_auto_refresh(layer.autoRefresh);
+ }
+ if (layer.what & layer_state_t::eTrustedOverlayChanged) {
+ proto.set_is_trusted_overlay(layer.isTrustedOverlay);
+ }
+ if (layer.what & layer_state_t::eBufferCropChanged) {
+ LayerProtoHelper::writeToProto(layer.bufferCrop, proto.mutable_buffer_crop());
+ }
+ if (layer.what & layer_state_t::eDestinationFrameChanged) {
+ LayerProtoHelper::writeToProto(layer.destinationFrame, proto.mutable_destination_frame());
+ }
+ if (layer.what & layer_state_t::eDropInputModeChanged) {
+ proto.set_drop_input_mode(
+ static_cast<proto::LayerState_DropInputMode>(layer.dropInputMode));
+ }
+ return proto;
+}
+
+proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display,
+ DisplayHandleToIdFn getDisplayId) {
+ proto::DisplayState proto;
+ proto.set_what(display.what);
+ if (getDisplayId != nullptr) {
+ proto.set_id(getDisplayId(display.token));
+ }
+
+ if (display.what & DisplayState::eLayerStackChanged) {
+ proto.set_layer_stack(display.layerStack.id);
+ }
+ if (display.what & DisplayState::eDisplayProjectionChanged) {
+ proto.set_orientation(static_cast<uint32_t>(display.orientation));
+ LayerProtoHelper::writeToProto(display.orientedDisplaySpaceRect,
+ proto.mutable_oriented_display_space_rect());
+ LayerProtoHelper::writeToProto(display.layerStackSpaceRect,
+ proto.mutable_layer_stack_space_rect());
+ }
+ if (display.what & DisplayState::eDisplaySizeChanged) {
+ proto.set_width(display.width);
+ proto.set_height(display.height);
+ }
+ if (display.what & DisplayState::eFlagsChanged) {
+ proto.set_flags(display.flags);
+ }
+ return proto;
+}
+
+proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+ proto::LayerCreationArgs proto;
+ proto.set_layer_id(args.layerId);
+ proto.set_name(args.name);
+ proto.set_flags(args.flags);
+ proto.set_parent_id(args.parentId);
+ proto.set_mirror_from_id(args.mirrorFromId);
+ return proto;
+}
+
+TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto,
+ LayerIdToHandleFn getLayerHandle,
+ DisplayIdToHandleFn getDisplayHandle) {
+ TransactionState t;
+ t.originPid = proto.pid();
+ t.originUid = proto.uid();
+ t.frameTimelineInfo.vsyncId = proto.vsync_id();
+ t.frameTimelineInfo.inputEventId = proto.input_event_id();
+ t.postTime = proto.post_time();
+ int32_t layerCount = proto.layer_changes_size();
+ t.states.reserve(static_cast<size_t>(layerCount));
+ for (int i = 0; i < layerCount; i++) {
+ ComposerState s;
+ fromProto(proto.layer_changes(i), getLayerHandle, s.state);
+ t.states.add(s);
+ }
+
+ int32_t displayCount = proto.display_changes_size();
+ t.displays.reserve(static_cast<size_t>(displayCount));
+ for (int i = 0; i < displayCount; i++) {
+ t.displays.add(fromProto(proto.display_changes(i), getDisplayHandle));
+ }
+ return t;
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
+ TracingLayerCreationArgs& outArgs) {
+ outArgs.layerId = proto.layer_id();
+ outArgs.name = proto.name();
+ outArgs.flags = proto.flags();
+ outArgs.parentId = proto.parent_id();
+ outArgs.mirrorFromId = proto.mirror_from_id();
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+ LayerIdToHandleFn getLayerHandle,
+ TracingLayerState& outState) {
+ fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState));
+ if (proto.what() & layer_state_t::eReparent) {
+ outState.parentId = proto.parent_id();
+ outState.args.parentId = outState.parentId;
+ }
+ if (proto.what() & layer_state_t::eRelativeLayerChanged) {
+ outState.relativeParentId = proto.relative_parent_id();
+ }
+ if (proto.what() & layer_state_t::eInputInfoChanged) {
+ outState.inputCropId = proto.window_info_handle().crop_layer_id();
+ }
+ if (proto.what() & layer_state_t::eBufferChanged) {
+ const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+ outState.bufferId = bufferProto.buffer_id();
+ outState.bufferWidth = bufferProto.width();
+ outState.bufferHeight = bufferProto.height();
+ }
+ if (proto.what() & layer_state_t::eSidebandStreamChanged) {
+ outState.hasSidebandStream = proto.has_sideband_stream();
+ }
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+ LayerIdToHandleFn getLayerHandle, layer_state_t& layer) {
+ layer.layerId = proto.layer_id();
+ layer.what |= proto.what();
+
+ if (getLayerHandle != nullptr) {
+ layer.surface = getLayerHandle(layer.layerId);
+ }
+
+ if (proto.what() & layer_state_t::ePositionChanged) {
+ layer.x = proto.x();
+ layer.y = proto.y();
+ }
+ if (proto.what() & layer_state_t::eLayerChanged) {
+ layer.z = proto.z();
+ }
+ if (proto.what() & layer_state_t::eSizeChanged) {
+ layer.w = proto.w();
+ layer.h = proto.h();
+ }
+ if (proto.what() & layer_state_t::eLayerStackChanged) {
+ layer.layerStack.id = proto.layer_stack();
+ }
+ if (proto.what() & layer_state_t::eFlagsChanged) {
+ layer.flags = proto.flags();
+ layer.mask = proto.mask();
+ }
+ if (proto.what() & layer_state_t::eMatrixChanged) {
+ const proto::LayerState_Matrix22& matrixProto = proto.matrix();
+ layer.matrix.dsdx = matrixProto.dsdx();
+ layer.matrix.dsdy = matrixProto.dsdy();
+ layer.matrix.dtdx = matrixProto.dtdx();
+ layer.matrix.dtdy = matrixProto.dtdy();
+ }
+ if (proto.what() & layer_state_t::eCornerRadiusChanged) {
+ layer.cornerRadius = proto.corner_radius();
+ }
+ if (proto.what() & layer_state_t::eBackgroundBlurRadiusChanged) {
+ layer.backgroundBlurRadius = proto.background_blur_radius();
+ }
+
+ if (proto.what() & layer_state_t::eAlphaChanged) {
+ layer.alpha = proto.alpha();
+ }
+
+ if (proto.what() & layer_state_t::eColorChanged) {
+ const proto::LayerState_Color3& colorProto = proto.color();
+ layer.color.r = colorProto.r();
+ layer.color.g = colorProto.g();
+ layer.color.b = colorProto.b();
+ }
+ if (proto.what() & layer_state_t::eTransparentRegionChanged) {
+ LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
+ }
+ if (proto.what() & layer_state_t::eTransformChanged) {
+ layer.transform = proto.transform();
+ }
+ if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
+ layer.transformToDisplayInverse = proto.transform_to_display_inverse();
+ }
+ if (proto.what() & layer_state_t::eCropChanged) {
+ LayerProtoHelper::readFromProto(proto.crop(), layer.crop);
+ }
+ if (proto.what() & layer_state_t::eBufferChanged) {
+ const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+ layer.bufferData.frameNumber = bufferProto.frame_number();
+ layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
+ layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id();
+ }
+
+ if (proto.what() & layer_state_t::eApiChanged) {
+ layer.api = proto.api();
+ }
+
+ if (proto.what() & layer_state_t::eColorTransformChanged) {
+ LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform);
+ }
+ if (proto.what() & layer_state_t::eBlurRegionsChanged) {
+ layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size()));
+ for (int i = 0; i < proto.blur_regions_size(); i++) {
+ android::BlurRegion region;
+ LayerProtoHelper::readFromProto(proto.blur_regions(i), region);
+ layer.blurRegions.push_back(region);
+ }
+ }
+
+ if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
+ int32_t layerId = proto.parent_id();
+ layer.parentSurfaceControlForChild =
+ new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+ nullptr, layerId);
+ }
+ if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) {
+ int32_t layerId = proto.relative_parent_id();
+ layer.relativeLayerSurfaceControl =
+ new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
+ nullptr, layerId);
+ }
+
+ if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
+ gui::WindowInfo inputInfo;
+ const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle();
+
+ inputInfo.flags = static_cast<gui::WindowInfo::Flag>(windowInfoProto.layout_params_flags());
+ inputInfo.type = static_cast<gui::WindowInfo::Type>(windowInfoProto.layout_params_type());
+ LayerProtoHelper::readFromProto(windowInfoProto.touchable_region(),
+ inputInfo.touchableRegion);
+ inputInfo.surfaceInset = windowInfoProto.surface_inset();
+ inputInfo.focusable = windowInfoProto.focusable();
+ inputInfo.hasWallpaper = windowInfoProto.has_wallpaper();
+ inputInfo.globalScaleFactor = windowInfoProto.global_scale_factor();
+ const proto::LayerState_Transform& transformProto = windowInfoProto.transform();
+ inputInfo.transform.set(transformProto.dsdx(), transformProto.dtdx(), transformProto.dtdy(),
+ transformProto.dsdy());
+ inputInfo.transform.set(transformProto.tx(), transformProto.ty());
+ inputInfo.replaceTouchableRegionWithCrop =
+ windowInfoProto.replace_touchable_region_with_crop();
+ int32_t layerId = windowInfoProto.crop_layer_id();
+ if (getLayerHandle != nullptr) {
+ inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
+ }
+ layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
+ }
+ if (proto.what() & layer_state_t::eBackgroundColorChanged) {
+ layer.bgColorAlpha = proto.bg_color_alpha();
+ layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
+ const proto::LayerState_Color3& colorProto = proto.color();
+ layer.color.r = colorProto.r();
+ layer.color.g = colorProto.g();
+ layer.color.b = colorProto.b();
+ }
+ if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
+ layer.colorSpaceAgnostic = proto.color_space_agnostic();
+ }
+ if (proto.what() & layer_state_t::eShadowRadiusChanged) {
+ layer.shadowRadius = proto.shadow_radius();
+ }
+ if (proto.what() & layer_state_t::eFrameRateSelectionPriority) {
+ layer.frameRateSelectionPriority = proto.frame_rate_selection_priority();
+ }
+ if (proto.what() & layer_state_t::eFrameRateChanged) {
+ layer.frameRate = proto.frame_rate();
+ layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility());
+ layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy());
+ }
+ if (proto.what() & layer_state_t::eFixedTransformHintChanged) {
+ layer.fixedTransformHint =
+ static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint());
+ }
+ if (proto.what() & layer_state_t::eAutoRefreshChanged) {
+ layer.autoRefresh = proto.auto_refresh();
+ }
+ if (proto.what() & layer_state_t::eTrustedOverlayChanged) {
+ layer.isTrustedOverlay = proto.is_trusted_overlay();
+ }
+ if (proto.what() & layer_state_t::eBufferCropChanged) {
+ LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop);
+ }
+ if (proto.what() & layer_state_t::eDestinationFrameChanged) {
+ LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame);
+ }
+ if (proto.what() & layer_state_t::eDropInputModeChanged) {
+ layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode());
+ }
+}
+
+DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto,
+ DisplayIdToHandleFn getDisplayHandle) {
+ DisplayState display;
+ display.what = proto.what();
+ if (getDisplayHandle != nullptr) {
+ display.token = getDisplayHandle(proto.id());
+ }
+
+ if (display.what & DisplayState::eLayerStackChanged) {
+ display.layerStack.id = proto.layer_stack();
+ }
+ if (display.what & DisplayState::eDisplayProjectionChanged) {
+ display.orientation = static_cast<ui::Rotation>(proto.orientation());
+ LayerProtoHelper::readFromProto(proto.oriented_display_space_rect(),
+ display.orientedDisplaySpaceRect);
+ LayerProtoHelper::readFromProto(proto.layer_stack_space_rect(),
+ display.layerStackSpaceRect);
+ }
+ if (display.what & DisplayState::eDisplaySizeChanged) {
+ display.width = proto.width();
+ display.height = proto.height();
+ }
+ if (display.what & DisplayState::eFlagsChanged) {
+ display.flags = proto.flags();
+ }
+ return display;
+}
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
new file mode 100644
index 0000000..b78d3d9
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 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 <layerproto/TransactionProto.h>
+#include <utils/RefBase.h>
+
+#include "TransactionState.h"
+
+namespace android::surfaceflinger {
+
+struct TracingLayerCreationArgs {
+ int32_t layerId;
+ std::string name;
+ uint32_t flags = 0;
+ int32_t parentId = -1;
+ int32_t mirrorFromId = -1;
+};
+
+struct TracingLayerState : layer_state_t {
+ uint64_t bufferId;
+ uint32_t bufferHeight;
+ uint32_t bufferWidth;
+ bool hasSidebandStream;
+ int32_t parentId;
+ int32_t relativeParentId;
+ int32_t inputCropId;
+ TracingLayerCreationArgs args;
+};
+
+class TransactionProtoParser {
+public:
+ typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn;
+ typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn;
+ typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn;
+ typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn;
+
+ static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
+ DisplayHandleToIdFn getDisplayIdFn);
+ static proto::TransactionState toProto(
+ const std::map<int32_t /* layerId */, TracingLayerState>&);
+
+ static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+
+ static TransactionState fromProto(const proto::TransactionState&,
+ LayerIdToHandleFn getLayerHandleFn,
+ DisplayIdToHandleFn getDisplayHandleFn);
+ static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
+ TracingLayerState& outState);
+ static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+
+private:
+ static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId);
+ static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId);
+ static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle,
+ layer_state_t& out);
+ static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle);
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
new file mode 100644
index 0000000..b5966d5
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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 "TransactionTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "RingBuffer.h"
+#include "TransactionTracing.h"
+
+namespace android {
+
+TransactionTracing::TransactionTracing() {
+ mBuffer = std::make_unique<
+ RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
+}
+
+TransactionTracing::~TransactionTracing() = default;
+
+bool TransactionTracing::enable() {
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ return false;
+ }
+ mBuffer->setSize(mBufferSizeInBytes);
+ mStartingTimestamp = systemTime();
+ mEnabled = true;
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ mDone = false;
+ mThread = std::thread(&TransactionTracing::loop, this);
+ }
+ return true;
+}
+
+bool TransactionTracing::disable() {
+ std::thread thread;
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ mDone = true;
+ mTransactionsAvailableCv.notify_all();
+ thread = std::move(mThread);
+ }
+ if (thread.joinable()) {
+ thread.join();
+ }
+
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+
+ writeToFileLocked();
+ mBuffer->reset();
+ mQueuedTransactions.clear();
+ mStartingStates.clear();
+ mLayerHandles.clear();
+ return true;
+}
+
+bool TransactionTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
+ return mEnabled;
+}
+
+status_t TransactionTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ return writeToFileLocked();
+}
+
+status_t TransactionTracing::writeToFileLocked() {
+ proto::TransactionTraceFile fileProto = createTraceFileProto();
+ addStartingStateToProtoLocked(fileProto);
+ return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
+ std::scoped_lock lock(mTraceLock);
+ mBufferSizeInBytes = bufferSizeInBytes;
+ mBuffer->setSize(mBufferSizeInBytes);
+}
+
+proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
+ proto::TransactionTraceFile proto;
+ proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
+ return proto;
+}
+
+void TransactionTracing::dump(std::string& result) const {
+ std::scoped_lock lock(mTraceLock);
+ base::StringAppendF(&result, "Transaction tracing state: %s\n",
+ mEnabled ? "enabled" : "disabled");
+ base::StringAppendF(&result,
+ " queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
+ mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
+ mStartingStates.size());
+ mBuffer->dump(result);
+}
+
+void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
+ std::scoped_lock lock(mTraceLock);
+ ATRACE_CALL();
+ if (!mEnabled) {
+ return;
+ }
+ mQueuedTransactions[transaction.id] =
+ TransactionProtoParser::toProto(transaction,
+ std::bind(&TransactionTracing::getLayerIdLocked, this,
+ std::placeholders::_1),
+ nullptr);
+}
+
+void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
+ int64_t vsyncId) {
+ CommittedTransactions committedTransactions;
+ committedTransactions.vsyncId = vsyncId;
+ committedTransactions.timestamp = systemTime();
+ committedTransactions.transactionIds.reserve(transactions.size());
+ for (const auto& transaction : transactions) {
+ committedTransactions.transactionIds.emplace_back(transaction.id);
+ }
+
+ mPendingTransactions.emplace_back(committedTransactions);
+ tryPushToTracingThread();
+}
+
+void TransactionTracing::loop() {
+ while (true) {
+ std::vector<CommittedTransactions> committedTransactions;
+ std::vector<int32_t> removedLayers;
+ {
+ std::unique_lock<std::mutex> lock(mMainThreadLock);
+ base::ScopedLockAssertion assumeLocked(mMainThreadLock);
+ mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
+ return mDone || !mCommittedTransactions.empty();
+ });
+ if (mDone) {
+ mCommittedTransactions.clear();
+ mRemovedLayers.clear();
+ break;
+ }
+
+ removedLayers = std::move(mRemovedLayers);
+ mRemovedLayers.clear();
+ committedTransactions = std::move(mCommittedTransactions);
+ mCommittedTransactions.clear();
+ } // unlock mMainThreadLock
+
+ addEntry(committedTransactions, removedLayers);
+ }
+}
+
+void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+ const std::vector<int32_t>& removedLayers) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mTraceLock);
+ std::vector<std::string> removedEntries;
+ proto::TransactionTraceEntry entryProto;
+ for (const CommittedTransactions& entry : committedTransactions) {
+ entryProto.set_elapsed_realtime_nanos(entry.timestamp);
+ entryProto.set_vsync_id(entry.vsyncId);
+ entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
+ for (auto& newLayer : mCreatedLayers) {
+ entryProto.mutable_added_layers()->Add(std::move(newLayer));
+ }
+ entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
+ for (auto& removedLayer : removedLayers) {
+ entryProto.mutable_removed_layers()->Add(removedLayer);
+ }
+ mCreatedLayers.clear();
+ entryProto.mutable_transactions()->Reserve(
+ static_cast<int32_t>(entry.transactionIds.size()));
+ for (const uint64_t& id : entry.transactionIds) {
+ auto it = mQueuedTransactions.find(id);
+ if (it != mQueuedTransactions.end()) {
+ entryProto.mutable_transactions()->Add(std::move(it->second));
+ mQueuedTransactions.erase(it);
+ } else {
+ ALOGW("Could not find transaction id %" PRIu64, id);
+ }
+ }
+
+ std::string serializedProto;
+ entryProto.SerializeToString(&serializedProto);
+ entryProto.Clear();
+ std::vector<std::string> entries = mBuffer->emplace(std::move(serializedProto));
+ removedEntries.reserve(removedEntries.size() + entries.size());
+ removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+ std::make_move_iterator(entries.end()));
+ }
+
+ proto::TransactionTraceEntry removedEntryProto;
+ for (const std::string& removedEntry : removedEntries) {
+ removedEntryProto.ParseFromString(removedEntry);
+ updateStartingStateLocked(removedEntryProto);
+ removedEntryProto.Clear();
+ }
+ mTransactionsAddedToBufferCv.notify_one();
+}
+
+void TransactionTracing::flush(int64_t vsyncId) {
+ while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+ tryPushToTracingThread();
+ }
+ std::unique_lock<std::mutex> lock(mTraceLock);
+ base::ScopedLockAssertion assumeLocked(mTraceLock);
+ mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
+ proto::TransactionTraceEntry entry;
+ if (mBuffer->used() > 0) {
+ entry.ParseFromString(mBuffer->back());
+ }
+ return mBuffer->used() > 0 && entry.vsync_id() >= vsyncId;
+ });
+}
+
+void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+ uint32_t flags, int parentId) {
+ std::scoped_lock lock(mTraceLock);
+ TracingLayerCreationArgs args{layerId, name, flags, parentId, -1 /* mirrorFromId */};
+ if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+ ALOGW("Duplicate handles found. %p", layerHandle);
+ }
+ mLayerHandles[layerHandle] = layerId;
+ proto::LayerCreationArgs protoArgs = TransactionProtoParser::toProto(args);
+ proto::LayerCreationArgs protoArgsCopy = protoArgs;
+ mCreatedLayers.push_back(protoArgs);
+}
+
+void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
+ const std::string& name, int mirrorFromId) {
+ std::scoped_lock lock(mTraceLock);
+ TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
+ if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+ ALOGW("Duplicate handles found. %p", layerHandle);
+ }
+ mLayerHandles[layerHandle] = layerId;
+ mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+ mPendingRemovedLayers.emplace_back(layerId);
+ tryPushToTracingThread();
+}
+
+void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
+ std::scoped_lock lock(mTraceLock);
+ mLayerHandles.erase(layerHandle);
+}
+
+void TransactionTracing::tryPushToTracingThread() {
+ // Try to acquire the lock from main thread.
+ if (mMainThreadLock.try_lock()) {
+ // We got the lock! Collect any pending transactions and continue.
+ mCommittedTransactions.insert(mCommittedTransactions.end(),
+ std::make_move_iterator(mPendingTransactions.begin()),
+ std::make_move_iterator(mPendingTransactions.end()));
+ mPendingTransactions.clear();
+ mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
+ mPendingRemovedLayers.end());
+ mPendingRemovedLayers.clear();
+ mTransactionsAvailableCv.notify_one();
+ mMainThreadLock.unlock();
+ } else {
+ ALOGV("Couldn't get lock");
+ }
+}
+
+int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
+ if (layerHandle == nullptr) {
+ return -1;
+ }
+ auto it = mLayerHandles.find(layerHandle->localBinder());
+ if (it == mLayerHandles.end()) {
+ ALOGW("Could not find layer handle %p", layerHandle->localBinder());
+ return -1;
+ }
+ return it->second;
+}
+
+void TransactionTracing::updateStartingStateLocked(
+ const proto::TransactionTraceEntry& removedEntry) {
+ // Keep track of layer starting state so we can reconstruct the layer state as we purge
+ // transactions from the buffer.
+ for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
+ TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
+ startingState.layerId = addedLayer.layer_id();
+ TransactionProtoParser::fromProto(addedLayer, startingState.args);
+ }
+
+ // Merge layer states to starting transaction state.
+ for (const proto::TransactionState& transaction : removedEntry.transactions()) {
+ for (const proto::LayerState& layerState : transaction.layer_changes()) {
+ auto it = mStartingStates.find(layerState.layer_id());
+ if (it == mStartingStates.end()) {
+ ALOGW("Could not find layer id %d", layerState.layer_id());
+ continue;
+ }
+ TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+ }
+ }
+
+ // Clean up stale starting states since the layer has been removed and the buffer does not
+ // contain any references to the layer.
+ for (const int32_t removedLayerId : removedEntry.removed_layers()) {
+ mStartingStates.erase(removedLayerId);
+ }
+}
+
+void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
+ proto::TransactionTraceEntry* entryProto = proto.add_entry();
+ entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+ entryProto->set_vsync_id(0);
+ if (mStartingStates.size() == 0) {
+ return;
+ }
+
+ entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+ for (auto& [layerId, state] : mStartingStates) {
+ entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(state.args));
+ }
+
+ proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+ transactionProto.set_vsync_id(0);
+ transactionProto.set_post_time(mStartingTimestamp);
+ entryProto->mutable_transactions()->Add(std::move(transactionProto));
+}
+
+proto::TransactionTraceFile TransactionTracing::writeToProto() {
+ std::scoped_lock<std::mutex> lock(mTraceLock);
+ proto::TransactionTraceFile proto = createTraceFileProto();
+ addStartingStateToProtoLocked(proto);
+ mBuffer->writeToProto(proto);
+ return proto;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
new file mode 100644
index 0000000..26a3758
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -0,0 +1,128 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <layerproto/TransactionProto.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+class TransactionTracingTest;
+/*
+ * Records all committed transactions into a ring bufffer.
+ *
+ * Transactions come in via the binder thread. They are serialized to proto
+ * and stored in a map using the transaction id as key. Main thread will
+ * pass the list of transaction ids that are committed every vsync and notify
+ * the tracing thread. The tracing thread will then wake up and add the
+ * committed transactions to the ring buffer.
+ *
+ * When generating SF dump state, we will flush the buffer to a file which
+ * will then be included in the bugreport.
+ *
+ */
+class TransactionTracing {
+public:
+ TransactionTracing();
+ ~TransactionTracing();
+
+ bool enable();
+ bool disable();
+ bool isEnabled() const;
+
+ void addQueuedTransaction(const TransactionState&);
+ void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+ status_t writeToFile();
+ void setBufferSize(size_t bufferSizeInBytes);
+ void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
+ int parentId);
+ void onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+ int mirrorFromId);
+ void onLayerRemoved(int layerId);
+ void onHandleRemoved(BBinder* layerHandle);
+ void dump(std::string&) const;
+ static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
+ static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+
+private:
+ friend class TransactionTracingTest;
+
+ static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
+
+ mutable std::mutex mTraceLock;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ std::unique_ptr<RingBuffer<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
+ GUARDED_BY(mTraceLock);
+ nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
+ std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+ std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+ GUARDED_BY(mTraceLock);
+ std::map<int32_t /* layerId */, TracingLayerState> mStartingStates GUARDED_BY(mTraceLock);
+
+ // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
+ // otherwise will push data to temporary container.
+ std::mutex mMainThreadLock;
+ std::thread mThread GUARDED_BY(mMainThreadLock);
+ bool mDone GUARDED_BY(mMainThreadLock) = false;
+ std::condition_variable mTransactionsAvailableCv;
+ std::condition_variable mTransactionsAddedToBufferCv;
+ struct CommittedTransactions {
+ std::vector<uint64_t> transactionIds;
+ int64_t vsyncId;
+ int64_t timestamp;
+ };
+ std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
+ std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+
+ std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
+ std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+
+ proto::TransactionTraceFile createTraceFileProto() const;
+ void loop();
+ void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+ const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+ int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
+ void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
+ void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
+ void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
+ status_t writeToFileLocked() REQUIRES(mTraceLock);
+
+ // TEST
+ // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+ void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
+ // Return buffer contents as trace file proto
+ proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 6af69f0..b705d9c 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -24,6 +24,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "TransactionCallbackInvoker.h"
+#include "BackgroundExecutor.h"
#include <cinttypes>
@@ -49,121 +50,25 @@
return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
}
-TransactionCallbackInvoker::~TransactionCallbackInvoker() {
- {
- std::lock_guard lock(mMutex);
- for (const auto& [listener, transactionStats] : mCompletedTransactions) {
- listener->unlinkToDeath(mDeathRecipient);
- }
- }
-}
-
-status_t TransactionCallbackInvoker::startRegistration(const ListenerCallbacks& listenerCallbacks) {
- std::lock_guard lock(mMutex);
-
- auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
auto& [listener, callbackIds] = listenerCallbacks;
-
- if (inserted) {
- if (mCompletedTransactions.count(listener) == 0) {
- status_t err = listener->linkToDeath(mDeathRecipient);
- if (err != NO_ERROR) {
- ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
- return err;
- }
- }
- auto& transactionStatsDeque = mCompletedTransactions[listener];
- transactionStatsDeque.emplace_back(callbackIds);
- }
-
- return NO_ERROR;
+ auto& transactionStatsDeque = mCompletedTransactions[listener];
+ transactionStatsDeque.emplace_back(callbackIds);
}
-status_t TransactionCallbackInvoker::endRegistration(const ListenerCallbacks& listenerCallbacks) {
- std::lock_guard lock(mMutex);
-
- auto itr = mRegisteringTransactions.find(listenerCallbacks);
- if (itr == mRegisteringTransactions.end()) {
- ALOGE("cannot end a registration that does not exist");
- return BAD_VALUE;
- }
-
- mRegisteringTransactions.erase(itr);
-
- return NO_ERROR;
-}
-
-bool TransactionCallbackInvoker::isRegisteringTransaction(
- const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
- ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
-
- auto itr = mRegisteringTransactions.find(listenerCallbacks);
- return itr != mRegisteringTransactions.end();
-}
-
-status_t TransactionCallbackInvoker::registerPendingCallbackHandle(
- const sp<CallbackHandle>& handle) {
- std::lock_guard lock(mMutex);
-
- // If we can't find the transaction stats something has gone wrong. The client should call
- // startRegistration before trying to register a pending callback handle.
- TransactionStats* transactionStats;
- status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
- if (err != NO_ERROR) {
- ALOGE("cannot find transaction stats");
- return err;
- }
-
- mPendingTransactions[handle->listener][handle->callbackIds]++;
- return NO_ERROR;
-}
-
-status_t TransactionCallbackInvoker::finalizeCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData) {
- auto listener = mPendingTransactions.find(handle->listener);
- if (listener != mPendingTransactions.end()) {
- auto& pendingCallbacks = listener->second;
- auto pendingCallback = pendingCallbacks.find(handle->callbackIds);
-
- if (pendingCallback != pendingCallbacks.end()) {
- auto& pendingCount = pendingCallback->second;
-
- // Decrease the pending count for this listener
- if (--pendingCount == 0) {
- pendingCallbacks.erase(pendingCallback);
- }
- } else {
- ALOGW("there are more latched callbacks than there were registered callbacks");
- }
- if (listener->second.size() == 0) {
- mPendingTransactions.erase(listener);
- }
- } else {
- ALOGW("cannot find listener in mPendingTransactions");
- }
-
- status_t err = addCallbackHandle(handle, jankData);
- if (err != NO_ERROR) {
- ALOGE("could not add callback handle");
- return err;
- }
- return NO_ERROR;
-}
-
-status_t TransactionCallbackInvoker::finalizeOnCommitCallbackHandles(
+status_t TransactionCallbackInvoker::addOnCommitCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles,
std::deque<sp<CallbackHandle>>& outRemainingHandles) {
if (handles.empty()) {
return NO_ERROR;
}
- std::lock_guard lock(mMutex);
const std::vector<JankData>& jankData = std::vector<JankData>();
for (const auto& handle : handles) {
if (!containsOnCommitCallbacks(handle->callbackIds)) {
outRemainingHandles.push_back(handle);
continue;
}
- status_t err = finalizeCallbackHandle(handle, jankData);
+ status_t err = addCallbackHandle(handle, jankData);
if (err != NO_ERROR) {
return err;
}
@@ -172,14 +77,13 @@
return NO_ERROR;
}
-status_t TransactionCallbackInvoker::finalizePendingCallbackHandles(
+status_t TransactionCallbackInvoker::addCallbackHandles(
const std::deque<sp<CallbackHandle>>& handles, const std::vector<JankData>& jankData) {
if (handles.empty()) {
return NO_ERROR;
}
- std::lock_guard lock(mMutex);
for (const auto& handle : handles) {
- status_t err = finalizeCallbackHandle(handle, jankData);
+ status_t err = addCallbackHandle(handle, jankData);
if (err != NO_ERROR) {
return err;
}
@@ -190,12 +94,10 @@
status_t TransactionCallbackInvoker::registerUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
- std::lock_guard lock(mMutex);
-
return addCallbackHandle(handle, std::vector<JankData>());
}
-status_t TransactionCallbackInvoker::findTransactionStats(
+status_t TransactionCallbackInvoker::findOrCreateTransactionStats(
const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
TransactionStats** outTransactionStats) {
auto& transactionStatsDeque = mCompletedTransactions[listener];
@@ -208,9 +110,8 @@
return NO_ERROR;
}
}
-
- ALOGE("could not find transaction stats");
- return BAD_VALUE;
+ *outTransactionStats = &transactionStatsDeque.emplace_back(callbackIds);
+ return NO_ERROR;
}
status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle,
@@ -218,7 +119,8 @@
// If we can't find the transaction stats something has gone wrong. The client should call
// startRegistration before trying to add a callback handle.
TransactionStats* transactionStats;
- status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
+ status_t err =
+ findOrCreateTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
if (err != NO_ERROR) {
return err;
}
@@ -229,6 +131,38 @@
// destroyed the client side is dead and there won't be anyone to send the callback to.
sp<IBinder> surfaceControl = handle->surfaceControl.promote();
if (surfaceControl) {
+ sp<Fence> prevFence = nullptr;
+
+ for (const auto& futureStruct : handle->previousReleaseFences) {
+ sp<Fence> currentFence = sp<Fence>::make(dup(futureStruct.get().drawFence));
+ if (prevFence == nullptr && currentFence->getStatus() != Fence::Status::Invalid) {
+ prevFence = currentFence;
+ handle->previousReleaseFence = prevFence;
+ } else if (prevFence != nullptr) {
+ // If both fences are signaled or both are unsignaled, we need to merge
+ // them to get an accurate timestamp.
+ if (prevFence->getStatus() != Fence::Status::Invalid &&
+ prevFence->getStatus() == currentFence->getStatus()) {
+ char fenceName[32] = {};
+ snprintf(fenceName, 32, "%.28s", handle->name.c_str());
+ sp<Fence> mergedFence = Fence::merge(fenceName, prevFence, currentFence);
+ if (mergedFence->isValid()) {
+ handle->previousReleaseFence = mergedFence;
+ prevFence = handle->previousReleaseFence;
+ }
+ } else if (currentFence->getStatus() == Fence::Status::Unsignaled) {
+ // If one fence has signaled and the other hasn't, the unsignaled
+ // fence will approximately correspond with the correct timestamp.
+ // There's a small race if both fences signal at about the same time
+ // and their statuses are retrieved with unfortunate timing. However,
+ // by this point, they will have both signaled and only the timestamp
+ // will be slightly off; any dependencies after this point will
+ // already have been met.
+ handle->previousReleaseFence = currentFence;
+ }
+ }
+ }
+ handle->previousReleaseFences = {};
FrameEventHistoryStats eventStats(handle->frameNumber,
handle->gpuCompositionDoneFence->getSnapshot().fence,
handle->compositorTiming, handle->refreshStartTime,
@@ -244,13 +178,10 @@
}
void TransactionCallbackInvoker::addPresentFence(const sp<Fence>& presentFence) {
- std::lock_guard<std::mutex> lock(mMutex);
mPresentFence = presentFence;
}
-void TransactionCallbackInvoker::sendCallbacks() {
- std::lock_guard lock(mMutex);
-
+void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
// For each listener
auto completedTransactionsItr = mCompletedTransactions.begin();
while (completedTransactionsItr != mCompletedTransactions.end()) {
@@ -262,28 +193,14 @@
auto transactionStatsItr = transactionStatsDeque.begin();
while (transactionStatsItr != transactionStatsDeque.end()) {
auto& transactionStats = *transactionStatsItr;
-
- // If this transaction is still registering, it is not safe to send a callback
- // because there could be surface controls that haven't been added to
- // transaction stats or mPendingTransactions.
- if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
- break;
- }
-
- // If we are still waiting on the callback handles for this transaction, stop
- // here because all transaction callbacks for the same listener must come in order
- auto pendingTransactions = mPendingTransactions.find(listener);
- if (pendingTransactions != mPendingTransactions.end() &&
- pendingTransactions->second.count(transactionStats.callbackIds) != 0) {
- break;
+ if (onCommitOnly && !containsOnCommitCallbacks(transactionStats.callbackIds)) {
+ transactionStatsItr++;
+ continue;
}
// If the transaction has been latched
if (transactionStats.latchTime >= 0 &&
!containsOnCommitCallbacks(transactionStats.callbackIds)) {
- if (!mPresentFence) {
- break;
- }
transactionStats.presentFence = mPresentFence;
}
@@ -301,22 +218,13 @@
// keep it as an IBinder due to consistency reasons: if we
// interface_cast at the IPC boundary when reading a Parcel,
// we get pointers that compare unequal in the SF process.
- interface_cast<ITransactionCompletedListener>(listenerStats.listener)
- ->onTransactionCompleted(listenerStats);
- if (transactionStatsDeque.empty()) {
- listener->unlinkToDeath(mDeathRecipient);
- completedTransactionsItr =
- mCompletedTransactions.erase(completedTransactionsItr);
- } else {
- completedTransactionsItr++;
- }
- } else {
- completedTransactionsItr =
- mCompletedTransactions.erase(completedTransactionsItr);
+ BackgroundExecutor::getInstance().execute([stats = std::move(listenerStats)]() {
+ interface_cast<ITransactionCompletedListener>(stats.listener)
+ ->onTransactionCompleted(stats);
+ });
}
- } else {
- completedTransactionsItr++;
}
+ completedTransactionsItr++;
}
if (mPresentFence) {
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 6f4d812..5ef5475 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -18,7 +18,9 @@
#include <condition_variable>
#include <deque>
+#include <future>
#include <mutex>
+#include <queue>
#include <thread>
#include <unordered_map>
#include <unordered_set>
@@ -27,6 +29,7 @@
#include <binder/IBinder.h>
#include <gui/ITransactionCompletedListener.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Fence.h>
namespace android {
@@ -41,7 +44,9 @@
wp<IBinder> surfaceControl;
bool releasePreviousBuffer = false;
+ std::string name;
sp<Fence> previousReleaseFence;
+ std::vector<std::shared_future<renderengine::RenderEngineResult>> previousReleaseFences;
nsecs_t acquireTime = -1;
nsecs_t latchTime = -1;
uint32_t transformHint = 0;
@@ -56,79 +61,36 @@
class TransactionCallbackInvoker {
public:
- ~TransactionCallbackInvoker();
-
- // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
- // to be included in the callback. This functions should be call before attempting to register
- // any callback handles.
- status_t startRegistration(const ListenerCallbacks& listenerCallbacks);
- // Ends the registration. After this is called, no more CallbackHandles will be registered.
- // It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
- status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
-
- // Informs the TransactionCallbackInvoker that there is a Transaction with a CallbackHandle
- // that needs to be latched and presented this frame. This function should be called once the
- // layer has received the CallbackHandle so the TransactionCallbackInvoker knows not to send
- // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and
- // presented.
- status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
- // Notifies the TransactionCallbackInvoker that a pending CallbackHandle has been presented.
- status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
- const std::vector<JankData>& jankData);
- status_t finalizeOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
+ const std::vector<JankData>& jankData);
+ status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
std::deque<sp<CallbackHandle>>& outRemainingHandles);
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+ void addEmptyTransaction(const ListenerCallbacks& listenerCallbacks);
void addPresentFence(const sp<Fence>& presentFence);
- void sendCallbacks();
-
-private:
-
- bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
- const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
-
- status_t findTransactionStats(const sp<IBinder>& listener,
- const std::vector<CallbackId>& callbackIds,
- TransactionStats** outTransactionStats) REQUIRES(mMutex);
+ void sendCallbacks(bool onCommitOnly);
+ void clearCompletedTransactions() {
+ mCompletedTransactions.clear();
+ }
status_t addCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData) REQUIRES(mMutex);
+ const std::vector<JankData>& jankData);
- status_t finalizeCallbackHandle(const sp<CallbackHandle>& handle,
- const std::vector<JankData>& jankData) REQUIRES(mMutex);
- class CallbackDeathRecipient : public IBinder::DeathRecipient {
- public:
- // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work.
- // Death recipients needs a binderDied function.
- //
- // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary.
- // sendObituary is only called if linkToDeath was called with a DeathRecipient.)
- void binderDied(const wp<IBinder>& /*who*/) override {}
- };
- sp<CallbackDeathRecipient> mDeathRecipient =
- new CallbackDeathRecipient();
-
- std::mutex mMutex;
- std::condition_variable_any mConditionVariable;
-
- std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions
- GUARDED_BY(mMutex);
-
- std::unordered_map<
- sp<IBinder>,
- std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
- IListenerHash>
- mPendingTransactions GUARDED_BY(mMutex);
+private:
+ status_t findOrCreateTransactionStats(const sp<IBinder>& listener,
+ const std::vector<CallbackId>& callbackIds,
+ TransactionStats** outTransactionStats);
std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
- mCompletedTransactions GUARDED_BY(mMutex);
+ mCompletedTransactions;
- sp<Fence> mPresentFence GUARDED_BY(mMutex);
+ sp<Fence> mPresentFence;
};
} // namespace android
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
new file mode 100644
index 0000000..fe3f3fc
--- /dev/null
+++ b/services/surfaceflinger/TransactionState.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 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 <gui/LayerState.h>
+
+namespace android {
+class CountDownLatch;
+
+struct TransactionState {
+ TransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+ bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
+ int originPid, int originUid, uint64_t transactionId)
+ : frameTimelineInfo(frameTimelineInfo),
+ states(composerStates),
+ displays(displayStates),
+ flags(transactionFlags),
+ applyToken(applyToken),
+ inputWindowCommands(inputWindowCommands),
+ desiredPresentTime(desiredPresentTime),
+ isAutoTimestamp(isAutoTimestamp),
+ buffer(uncacheBuffer),
+ postTime(postTime),
+ permissions(permissions),
+ hasListenerCallbacks(hasListenerCallbacks),
+ listenerCallbacks(listenerCallbacks),
+ originPid(originPid),
+ originUid(originUid),
+ id(transactionId) {}
+
+ TransactionState() {}
+
+ void traverseStatesWithBuffers(std::function<void(const layer_state_t&)> visitor);
+
+ FrameTimelineInfo frameTimelineInfo;
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags;
+ sp<IBinder> applyToken;
+ InputWindowCommands inputWindowCommands;
+ int64_t desiredPresentTime;
+ bool isAutoTimestamp;
+ client_cache_t buffer;
+ int64_t postTime;
+ uint32_t permissions;
+ bool hasListenerCallbacks;
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int originPid;
+ int originUid;
+ uint64_t id;
+ std::shared_ptr<CountDownLatch> transactionCommittedSignal;
+};
+
+class CountDownLatch {
+public:
+ enum {
+ eSyncTransaction = 1 << 0,
+ eSyncInputWindows = 1 << 1,
+ };
+ explicit CountDownLatch(uint32_t flags) : mFlags(flags) {}
+
+ // True if there is no waiting condition after count down.
+ bool countDown(uint32_t flag) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mFlags == 0) {
+ return true;
+ }
+ mFlags &= ~flag;
+ if (mFlags == 0) {
+ mCountDownComplete.notify_all();
+ return true;
+ }
+ return false;
+ }
+
+ // Return true if triggered.
+ bool wait_until(const std::chrono::seconds& timeout) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ const auto untilTime = std::chrono::system_clock::now() + timeout;
+ while (mFlags != 0) {
+ // Conditional variables can be woken up sporadically, so we check count
+ // to verify the wakeup was triggered by |countDown|.
+ if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+private:
+ uint32_t mFlags;
+ mutable std::condition_variable mCountDownComplete;
+ mutable std::mutex mMutex;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index 935502a..802d22d 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.h
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -22,6 +22,8 @@
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class Layer;
@@ -54,11 +56,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
index dc2aa58..b93d127 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp
@@ -21,6 +21,7 @@
namespace android {
+using gui::DisplayInfo;
using gui::IWindowInfosListener;
using gui::WindowInfo;
@@ -67,6 +68,7 @@
}
void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>& displayInfos,
bool shouldSync) {
std::unordered_set<sp<IWindowInfosListener>, ISurfaceComposer::SpHash<IWindowInfosListener>>
windowInfosListeners;
@@ -81,7 +83,7 @@
mCallbacksPending = windowInfosListeners.size();
for (const auto& listener : windowInfosListeners) {
- listener->onWindowInfosChanged(windowInfos,
+ listener->onWindowInfosChanged(windowInfos, displayInfos,
shouldSync ? mWindowInfosReportedListener : nullptr);
}
}
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index 5e5796f..4e08393 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,6 +23,8 @@
#include <utils/Mutex.h>
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class SurfaceFlinger;
@@ -33,7 +35,8 @@
void addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener);
- void windowInfosChanged(const std::vector<gui::WindowInfo>& windowInfos, bool shouldSync);
+ void windowInfosChanged(const std::vector<gui::WindowInfo>&,
+ const std::vector<gui::DisplayInfo>&, bool shouldSync);
protected:
void binderDied(const wp<IBinder>& who) override;
@@ -41,12 +44,6 @@
private:
void windowInfosReported();
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
-
const sp<SurfaceFlinger> mSf;
std::mutex mListenersMutex;
std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/WpHash.h
similarity index 71%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/WpHash.h
index d2996d1..86777b7 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/WpHash.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include <ui/Size.h>
+#pragma once
-namespace android::ui {
+namespace android {
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
+struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const { return std::hash<IBinder*>()(p.unsafe_get()); }
+};
-} // namespace android::ui
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/common.proto b/services/surfaceflinger/layerproto/common.proto
index 1c73a9f..a6d8d61 100644
--- a/services/surfaceflinger/layerproto/common.proto
+++ b/services/surfaceflinger/layerproto/common.proto
@@ -18,6 +18,11 @@
option optimize_for = LITE_RUNTIME;
package android.surfaceflinger;
+message RegionProto {
+ reserved 1; // Previously: uint64 id
+ repeated RectProto rect = 2;
+}
+
message RectProto {
int32 left = 1;
int32 top = 2;
@@ -36,4 +41,51 @@
float dsdy = 3;
float dtdy = 4;
int32 type = 5;
-}
\ No newline at end of file
+}
+
+message ColorProto {
+ float r = 1;
+ float g = 2;
+ float b = 3;
+ float a = 4;
+}
+
+message InputWindowInfoProto {
+ uint32 layout_params_flags = 1;
+ int32 layout_params_type = 2;
+ RectProto frame = 3;
+ RegionProto touchable_region = 4;
+
+ int32 surface_inset = 5;
+ bool visible = 6;
+ bool can_receive_keys = 7 [deprecated = true];
+ bool focusable = 8;
+ bool has_wallpaper = 9;
+
+ float global_scale_factor = 10;
+ float window_x_scale = 11 [deprecated = true];
+ float window_y_scale = 12 [deprecated = true];
+
+ int32 crop_layer_id = 13;
+ bool replace_touchable_region_with_crop = 14;
+ RectProto touchable_region_crop = 15;
+ TransformProto transform = 16;
+}
+
+message BlurRegion {
+ uint32 blur_radius = 1;
+ uint32 corner_radius_tl = 2;
+ uint32 corner_radius_tr = 3;
+ uint32 corner_radius_bl = 4;
+ float corner_radius_br = 5;
+ float alpha = 6;
+ int32 left = 7;
+ int32 top = 8;
+ int32 right = 9;
+ int32 bottom = 10;
+}
+
+message ColorTransformProto {
+ // This will be a 4x4 matrix of float values
+ repeated float val = 1;
+}
diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto
index ee8830e..c8cd926 100644
--- a/services/surfaceflinger/layerproto/display.proto
+++ b/services/surfaceflinger/layerproto/display.proto
@@ -33,4 +33,6 @@
RectProto layer_stack_space_rect = 5;
TransformProto transform = 6;
+
+ bool is_virtual = 7;
}
diff --git a/libs/ui/Size.cpp b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
similarity index 68%
copy from libs/ui/Size.cpp
copy to services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
index d2996d1..3e9ca52 100644
--- a/libs/ui/Size.cpp
+++ b/services/surfaceflinger/layerproto/include/layerproto/TransactionProto.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 The Android Open Source Project
+ * Copyright (C) 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.
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-#include <ui/Size.h>
-
-namespace android::ui {
-
-const Size Size::INVALID{-1, -1};
-const Size Size::EMPTY{0, 0};
-
-} // namespace android::ui
+// disable the warnings emitted from the protobuf headers. This file should be included instead of
+// directly including the generated header file
+#pragma GCC system_header
+#include <transactions.pb.h>
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 057eabb..4529905 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -143,11 +143,6 @@
float y = 2;
}
-message RegionProto {
- reserved 1; // Previously: uint64 id
- repeated RectProto rect = 2;
-}
-
message FloatRectProto {
float left = 1;
float top = 2;
@@ -162,13 +157,6 @@
int32 format = 4;
}
-message ColorProto {
- float r = 1;
- float g = 2;
- float b = 3;
- float a = 4;
-}
-
message BarrierLayerProto {
// layer id the barrier is waiting on.
int32 id = 1;
@@ -176,42 +164,3 @@
uint64 frame_number = 2;
}
-message InputWindowInfoProto {
- uint32 layout_params_flags = 1;
- uint32 layout_params_type = 2;
- RectProto frame = 3;
- RegionProto touchable_region = 4;
-
- uint32 surface_inset = 5;
- bool visible = 6;
- bool can_receive_keys = 7 [deprecated=true];
- bool focusable = 8;
- bool has_wallpaper = 9;
-
- float global_scale_factor = 10;
- float window_x_scale = 11 [deprecated=true];
- float window_y_scale = 12 [deprecated=true];
-
- uint32 crop_layer_id = 13;
- bool replace_touchable_region_with_crop = 14;
- RectProto touchable_region_crop = 15;
- TransformProto transform = 16;
-}
-
-message ColorTransformProto {
- // This will be a 4x4 matrix of float values
- repeated float val = 1;
-}
-
-message BlurRegion {
- uint32 blur_radius = 1;
- uint32 corner_radius_tl = 2;
- uint32 corner_radius_tr = 3;
- uint32 corner_radius_bl = 4;
- float corner_radius_br = 5;
- float alpha = 6;
- int32 left = 7;
- int32 top = 8;
- int32 right = 9;
- int32 bottom = 10;
-}
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
new file mode 100644
index 0000000..e31b502
--- /dev/null
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 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.
+ */
+
+syntax = "proto3";
+option optimize_for = LITE_RUNTIME;
+
+import "frameworks/native/services/surfaceflinger/layerproto/common.proto";
+
+package android.surfaceflinger.proto;
+
+/* Represents a file full of surface flinger transactions.
+ Encoded, it should start with 0x54 0x4E 0x58 0x54 0x52 0x41 0x43 0x45 (.TNXTRACE), such
+ that they can be easily identified. */
+message TransactionTraceFile {
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x54584E54; /* TNXT (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */
+ }
+
+ fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated TransactionTraceEntry entry = 2;
+}
+
+message TransactionTraceEntry {
+ int64 elapsed_realtime_nanos = 1;
+ int64 vsync_id = 2;
+ repeated TransactionState transactions = 3;
+ repeated LayerCreationArgs added_layers = 4;
+ repeated int32 removed_layers = 5;
+ repeated DisplayState added_displays = 6;
+ repeated int32 removed_displays = 7;
+}
+
+message LayerCreationArgs {
+ int32 layer_id = 1;
+ string name = 2;
+ uint32 flags = 3;
+ int32 parent_id = 4;
+ int32 mirror_from_id = 5;
+}
+
+message TransactionState {
+ int32 pid = 1;
+ int32 uid = 2;
+ int64 vsync_id = 3;
+ int32 input_event_id = 4;
+ int64 post_time = 5;
+ repeated LayerState layer_changes = 6;
+ repeated DisplayState display_changes = 7;
+}
+
+// Keep insync with layer_state_t
+message LayerState {
+ int32 layer_id = 1;
+ // Changes are split into ChangesLsb and ChangesMsb. First 32 bits are in ChangesLsb
+ // and the next 32 bits are in ChangesMsb. This is needed because enums have to be
+ // 32 bits and there's no nice way to put 64bit constants into .proto files.
+ enum ChangesLsb {
+ eChangesLsbNone = 0;
+ ePositionChanged = 0x00000001;
+ eLayerChanged = 0x00000002;
+ eSizeChanged = 0x00000004;
+ eAlphaChanged = 0x00000008;
+ eMatrixChanged = 0x00000010;
+ eTransparentRegionChanged = 0x00000020;
+ eFlagsChanged = 0x00000040;
+ eLayerStackChanged = 0x00000080;
+ eReleaseBufferListenerChanged = 0x00000400;
+ eShadowRadiusChanged = 0x00000800;
+ eLayerCreated = 0x00001000;
+ eBufferCropChanged = 0x00002000;
+ eRelativeLayerChanged = 0x00004000;
+ eReparent = 0x00008000;
+ eColorChanged = 0x00010000;
+ eDestroySurface = 0x00020000;
+ eTransformChanged = 0x00040000;
+ eTransformToDisplayInverseChanged = 0x00080000;
+ eCropChanged = 0x00100000;
+ eBufferChanged = 0x00200000;
+ eAcquireFenceChanged = 0x00400000;
+ eDataspaceChanged = 0x00800000;
+ eHdrMetadataChanged = 0x01000000;
+ eSurfaceDamageRegionChanged = 0x02000000;
+ eApiChanged = 0x04000000;
+ eSidebandStreamChanged = 0x08000000;
+ eColorTransformChanged = 0x10000000;
+ eHasListenerCallbacksChanged = 0x20000000;
+ eInputInfoChanged = 0x40000000;
+ eCornerRadiusChanged = -2147483648; // 0x80000000; (proto stores enums as signed int)
+ };
+ enum ChangesMsb {
+ eChangesMsbNone = 0;
+ eDestinationFrameChanged = 0x1;
+ eCachedBufferChanged = 0x2;
+ eBackgroundColorChanged = 0x4;
+ eMetadataChanged = 0x8;
+ eColorSpaceAgnosticChanged = 0x10;
+ eFrameRateSelectionPriority = 0x20;
+ eFrameRateChanged = 0x40;
+ eBackgroundBlurRadiusChanged = 0x80;
+ eProducerDisconnect = 0x100;
+ eFixedTransformHintChanged = 0x200;
+ eFrameNumberChanged = 0x400;
+ eBlurRegionsChanged = 0x800;
+ eAutoRefreshChanged = 0x1000;
+ eStretchChanged = 0x2000;
+ eTrustedOverlayChanged = 0x4000;
+ eDropInputModeChanged = 0x8000;
+ };
+ uint64 what = 2;
+ float x = 3;
+ float y = 4;
+ int32 z = 5;
+ uint32 w = 6;
+ uint32 h = 7;
+ uint32 layer_stack = 8;
+
+ enum Flags {
+ eFlagsNone = 0;
+ eLayerHidden = 0x01;
+ eLayerOpaque = 0x02;
+ eLayerSkipScreenshot = 0x40;
+ eLayerSecure = 0x80;
+ eEnableBackpressure = 0x100;
+ };
+ uint32 flags = 9;
+ uint32 mask = 10;
+
+ message Matrix22 {
+ float dsdx = 1;
+ float dtdx = 2;
+ float dtdy = 3;
+ float dsdy = 4;
+ };
+ Matrix22 matrix = 11;
+ float corner_radius = 12;
+ uint32 background_blur_radius = 13;
+ int32 parent_id = 14;
+ int32 relative_parent_id = 15;
+
+ float alpha = 16;
+ message Color3 {
+ float r = 1;
+ float g = 2;
+ float b = 3;
+ }
+ Color3 color = 17;
+ RegionProto transparent_region = 18;
+ uint32 transform = 19;
+ bool transform_to_display_inverse = 20;
+ RectProto crop = 21;
+
+ message BufferData {
+ uint64 buffer_id = 1;
+ uint32 width = 2;
+ uint32 height = 3;
+ uint64 frame_number = 4;
+
+ enum BufferDataChange {
+ BufferDataChangeNone = 0;
+ fenceChanged = 0x01;
+ frameNumberChanged = 0x02;
+ cachedBufferChanged = 0x04;
+ }
+ uint32 flags = 5;
+ uint64 cached_buffer_id = 6;
+ }
+ BufferData buffer_data = 22;
+ int32 api = 23;
+ bool has_sideband_stream = 24;
+ ColorTransformProto color_transform = 25;
+ repeated BlurRegion blur_regions = 26;
+
+ message Transform {
+ float dsdx = 1;
+ float dtdx = 2;
+ float dtdy = 3;
+ float dsdy = 4;
+ float tx = 5;
+ float ty = 6;
+ }
+ message WindowInfo {
+ uint32 layout_params_flags = 1;
+ int32 layout_params_type = 2;
+ RegionProto touchable_region = 3;
+ int32 surface_inset = 4;
+ bool focusable = 5;
+ bool has_wallpaper = 6;
+ float global_scale_factor = 7;
+ int32 crop_layer_id = 8;
+ bool replace_touchable_region_with_crop = 9;
+ RectProto touchable_region_crop = 10;
+ Transform transform = 11;
+ }
+ WindowInfo window_info_handle = 27;
+ float bg_color_alpha = 28;
+ int32 bg_color_dataspace = 29;
+ bool color_space_agnostic = 30;
+ float shadow_radius = 31;
+ int32 frame_rate_selection_priority = 32;
+ float frame_rate = 33;
+ int32 frame_rate_compatibility = 34;
+ int32 change_frame_rate_strategy = 35;
+ uint32 fixed_transform_hint = 36;
+ uint64 frame_number = 37;
+ bool auto_refresh = 38;
+ bool is_trusted_overlay = 39;
+ RectProto buffer_crop = 40;
+ RectProto destination_frame = 41;
+
+ enum DropInputMode {
+ NONE = 0;
+ ALL = 1;
+ OBSCURED = 2;
+ };
+ DropInputMode drop_input_mode = 42;
+}
+
+message DisplayState {
+ enum Changes {
+ eChangesNone = 0;
+ eSurfaceChanged = 0x01;
+ eLayerStackChanged = 0x02;
+ eDisplayProjectionChanged = 0x04;
+ eDisplaySizeChanged = 0x08;
+ eFlagsChanged = 0x10;
+ };
+ int32 id = 1;
+ uint32 what = 2;
+ uint32 flags = 3;
+ uint32 layer_stack = 4;
+ uint32 orientation = 5;
+ RectProto layer_stack_space_rect = 6;
+ RectProto oriented_display_space_rect = 7;
+ uint32 width = 8;
+ uint32 height = 9;
+}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
prop_name: "ro.surface_flinger.supports_background_blur"
}
-# Indicates whether Scheduler should use frame rate API when adjusting the
-# display refresh rate.
-prop {
- api_name: "use_frame_rate_api"
- type: Boolean
- scope: Public
- access: Readonly
- prop_name: "ro.surface_flinger.use_frame_rate_api"
-}
-
# Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
# SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
# duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 32ad873..7b86229 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -60,7 +60,7 @@
"android.hardware.graphics.composer@2.1",
],
shared_libs: [
- "android.hardware.graphics.common-V2-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.common@1.2",
"libandroid",
"libbase",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index fa3f0e7..d33bc10 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -93,7 +93,7 @@
ASSERT_TRUE(mBGSurfaceControl->isValid());
Transaction t;
- t.setDisplayLayerStack(mDisplay, 0);
+ t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
ASSERT_EQ(NO_ERROR,
t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply());
}
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 93656f3..9fa0452 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -33,7 +33,7 @@
mParentLayer = createColorLayer("Parent layer", Color::RED);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
});
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
index 84fea6c..ce94dab 100644
--- a/services/surfaceflinger/tests/IPC_test.cpp
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -159,10 +159,9 @@
{0, 0, static_cast<int32_t>(width),
static_cast<int32_t>(height)},
Color::RED);
- transaction->setLayerStack(mSurfaceControl, 0)
+ transaction->setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .setBuffer(mSurfaceControl, gb)
- .setAcquireFence(mSurfaceControl, fence)
+ .setBuffer(mSurfaceControl, gb, fence)
.show(mSurfaceControl)
.addTransactionCompletedCallback(mCallbackHelper.function,
mCallbackHelper.getContext());
@@ -232,7 +231,7 @@
mDisplayHeight = mode.resolution.getHeight();
Transaction setupTransaction;
- setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+ setupTransaction.setDisplayLayerStack(mPrimaryDisplay, ui::DEFAULT_LAYER_STACK);
setupTransaction.apply();
}
@@ -310,10 +309,9 @@
Color::RED);
Transaction transaction;
- transaction.setLayerStack(sc, 0)
+ transaction.setLayerStack(sc, ui::DEFAULT_LAYER_STACK)
.setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
- .setBuffer(sc, gb)
- .setAcquireFence(sc, fence)
+ .setBuffer(sc, gb, fence)
.show(sc)
.addTransactionCompletedCallback(helper1.function, helper1.getContext());
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 7ff041e..5c16fee 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -26,6 +26,7 @@
namespace android {
using android::hardware::graphics::common::V1_1::BufferUsage;
+using SCHash = SurfaceComposerClient::SCHash;
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -66,8 +67,7 @@
return err;
}
- transaction.setBuffer(layer, buffer);
- transaction.setAcquireFence(layer, fence);
+ transaction.setBuffer(layer, buffer, fence);
}
if (setBackgroundColor) {
@@ -103,6 +103,24 @@
}
}
+ static void waitForCommitCallback(
+ CallbackHelper& helper,
+ const std::unordered_set<sp<SurfaceControl>, SCHash>& committedSc) {
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+
+ const auto& surfaceControlStats = callbackData.surfaceControlStats;
+
+ ASSERT_EQ(surfaceControlStats.size(), committedSc.size()) << "wrong number of surfaces";
+
+ for (const auto& stats : surfaceControlStats) {
+ ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+ const auto& expectedSc = committedSc.find(stats.surfaceControl);
+ ASSERT_NE(expectedSc, committedSc.end()) << "unexpected surface control";
+ }
+ }
+
DisplayEventReceiver mDisplayEventReceiver;
int mEpollFd;
@@ -1030,4 +1048,85 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+// b202394221
+TEST_F(LayerCallbackTest, EmptyBufferStateChanges) {
+ sp<SurfaceControl> bufferLayer, emptyBufferLayer;
+ ASSERT_NO_FATAL_FAILURE(bufferLayer = createBufferStateLayer());
+ ASSERT_NO_FATAL_FAILURE(emptyBufferLayer = createBufferStateLayer());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ for (size_t i = 0; i < 10; i++) {
+ int err = fillTransaction(transaction, &callback, bufferLayer);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ ui::Size bufferSize = getBufferSize();
+
+ TransactionUtils::setFrame(transaction, bufferLayer,
+ Rect(0, 0, bufferSize.width, bufferSize.height),
+ Rect(0, 0, 32, 32));
+ transaction.setPosition(emptyBufferLayer, 1 + i, 2 + i);
+ transaction.apply();
+
+ ExpectedResult expected;
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, bufferLayer,
+ ExpectedResult::Buffer::ACQUIRED,
+ (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+ : ExpectedResult::PreviousBuffer::RELEASED);
+ expected.addSurface(ExpectedResult::Transaction::PRESENTED, emptyBufferLayer,
+ ExpectedResult::Buffer::NOT_ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+ }
+ ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+// b202394221
+TEST_F(LayerCallbackTest, DISABLED_NonBufferLayerStateChanges) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createColorLayer("ColorLayer", Color::RED));
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ transaction.setPosition(layer, 1, 2);
+ transaction.apply();
+
+ ExpectedResult expected;
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+ sp<SurfaceControl> offscreenLayer =
+ createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true);
+ err |= fillTransaction(transaction, &callback, offscreenLayer, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.reparent(offscreenLayer, nullptr)
+ .addTransactionCommittedCallback(callback.function, callback.getContext());
+ transaction.apply();
+
+ std::unordered_set<sp<SurfaceControl>, SCHash> committedSc;
+ committedSc.insert(layer);
+ committedSc.insert(offscreenLayer);
+ EXPECT_NO_FATAL_FAILURE(waitForCommitCallback(callback, committedSc));
+}
} // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index c8eeac6..0e2bc3d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -1353,7 +1353,7 @@
return;
}
- Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+ Transaction().setBuffer(layer, buffer, fence).apply();
status_t status = fence->wait(1000);
ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
@@ -1375,7 +1375,7 @@
sp<Fence> fence = Fence::NO_FENCE;
- Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+ Transaction().setBuffer(layer, buffer, fence).apply();
auto shot = getScreenCapture();
shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 0bc8fe7..6bd7920 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -266,7 +266,7 @@
sp<IBinder> mDisplay;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
- uint32_t mDisplayLayerStack;
+ ui::LayerStack mDisplayLayerStack = ui::DEFAULT_LAYER_STACK;
Rect mDisplayRect = Rect::INVALID_RECT;
// leave room for ~256 layers
@@ -294,8 +294,6 @@
// vsyncs.
mBufferPostDelay = static_cast<int32_t>(1e6 / mode.refreshRate) * 3;
- mDisplayLayerStack = 0;
-
mBlackBgSurface =
createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 25f3bb9..9cb617a 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -763,7 +763,8 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
- Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+ const auto layerStack = ui::LayerStack::fromValue(mDisplayLayerStack.id + 1);
+ Transaction().setLayerStack(layer, layerStack).apply();
{
SCOPED_TRACE("non-existing layer stack");
getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index ee4d367..e1a7ecc 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -63,7 +63,7 @@
TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index b7a9271..a921aa8 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -36,7 +36,7 @@
mParentLayer = createColorLayer("Parent layer", Color::RED);
mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
t.setCrop(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
t.setPosition(mChildLayer, 50, 50);
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 08de01c..1ed6c65 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -52,7 +52,7 @@
mColorLayer = 0;
}
- void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
+ void createDisplay(const ui::Size& layerStackSize, ui::LayerStack layerStack) {
mVirtualDisplay =
SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
asTransaction([&](Transaction& t) {
@@ -63,7 +63,7 @@
});
}
- void createColorLayer(uint32_t layerStack) {
+ void createColorLayer(ui::LayerStack layerStack) {
mColorLayer =
createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
@@ -90,8 +90,9 @@
};
TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
- createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */);
- createColorLayer(1 /* layerStack */);
+ constexpr ui::LayerStack kLayerStack{1u};
+ createDisplay(mMainDisplayState.layerStackSpaceRect, kLayerStack);
+ createColorLayer(kLayerStack);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
@@ -113,8 +114,8 @@
// Assumption here is that the new mirrored display has the same layer stack rect as the
// primary display that it is mirroring.
- createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */);
- createColorLayer(0 /* layerStack */);
+ createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK);
+ createColorLayer(ui::DEFAULT_LAYER_STACK);
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
index fde6e6e..50a4092 100644
--- a/services/surfaceflinger/tests/RelativeZ_test.cpp
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -43,7 +43,7 @@
mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
});
diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
index 309ab2f..f6b0def 100644
--- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
+++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp
@@ -31,7 +31,7 @@
public:
static void function(void* callbackContext, ReleaseCallbackId callbackId,
const sp<Fence>& releaseFence,
- uint32_t /*currentMaxAcquiredBufferCount*/) {
+ std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {
if (!callbackContext) {
FAIL() << "failed to get callback context";
}
@@ -85,9 +85,7 @@
sp<Fence> fence, CallbackHelper& callback, const ReleaseCallbackId& id,
ReleaseBufferCallbackHelper& releaseCallback) {
Transaction t;
- t.setFrameNumber(layer, id.framenumber);
- t.setBuffer(layer, buffer, id, releaseCallback.getCallback());
- t.setAcquireFence(layer, fence);
+ t.setBuffer(layer, buffer, fence, id.framenumber, id, releaseCallback.getCallback());
t.addTransactionCompletedCallback(callback.function, callback.getContext());
t.apply();
}
@@ -302,8 +300,8 @@
nsecs_t time = systemTime() + std::chrono::nanoseconds(100ms).count();
Transaction t;
- t.setBuffer(layer, firstBuffer, firstBufferCallbackId, releaseCallback->getCallback());
- t.setAcquireFence(layer, Fence::NO_FENCE);
+ t.setBuffer(layer, firstBuffer, std::nullopt, std::nullopt, firstBufferCallbackId,
+ releaseCallback->getCallback());
t.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext());
t.setDesiredPresentTime(time);
@@ -318,8 +316,8 @@
// Dropping frames in transaction queue emits a callback
sp<GraphicBuffer> secondBuffer = getBuffer();
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
- t.setBuffer(layer, secondBuffer, secondBufferCallbackId, releaseCallback->getCallback());
- t.setAcquireFence(layer, Fence::NO_FENCE);
+ t.setBuffer(layer, secondBuffer, std::nullopt, std::nullopt, secondBufferCallbackId,
+ releaseCallback->getCallback());
t.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext());
t.setDesiredPresentTime(time);
@@ -361,10 +359,8 @@
ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
Transaction transaction1;
- transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber);
- transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId,
- releaseCallback->getCallback());
- transaction1.setAcquireFence(layer, Fence::NO_FENCE);
+ transaction1.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext());
// Set a different TransactionCompletedListener to mimic a second process
@@ -389,4 +385,84 @@
ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
}
+TEST_F(ReleaseBufferCallbackTest, DISABLED_SetBuffer_OverwriteBuffers) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ // Create transaction with a buffer.
+ Transaction transaction;
+ transaction.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ // Call setBuffer on the same transaction with a different buffer.
+ transaction.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Transactions_OverwriteBuffers) {
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ // Create transaction with a buffer.
+ Transaction transaction1;
+ transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ // Create a second transaction with a new buffer for the same layer.
+ Transaction transaction2;
+ transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ // merge transaction1 into transaction2 so ensure we get a proper buffer release callback.
+ transaction1.merge(std::move(transaction2));
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
+TEST_F(ReleaseBufferCallbackTest, DISABLED_MergeBuffers_Different_Processes) {
+ sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener();
+ sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener();
+
+ TransactionCompletedListener::setInstance(firstCompletedListener);
+
+ sp<SurfaceControl> layer = createBufferStateLayer();
+ ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper();
+
+ sp<GraphicBuffer> firstBuffer = getBuffer();
+ ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber());
+
+ Transaction transaction1;
+ transaction1.setBuffer(layer, firstBuffer, std::nullopt, firstBufferCallbackId.framenumber,
+ firstBufferCallbackId, releaseCallback->getCallback());
+
+ // Sent a second buffer to allow the first buffer to get released.
+ sp<GraphicBuffer> secondBuffer = getBuffer();
+ ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber());
+
+ Transaction transaction2;
+ transaction2.setBuffer(layer, secondBuffer, std::nullopt, secondBufferCallbackId.framenumber,
+ secondBufferCallbackId, releaseCallback->getCallback());
+
+ // Set a different TransactionCompletedListener to mimic a second process
+ TransactionCompletedListener::setInstance(secondCompletedListener);
+ Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply();
+
+ // Make sure we can still get the release callback even though the merge happened in a different
+ // process.
+ ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 2d5b502..f9b3185 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -55,7 +55,7 @@
TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
@@ -556,7 +556,7 @@
Transaction()
.show(redLayer)
.show(secureLayer)
- .setLayerStack(redLayer, 0)
+ .setLayerStack(redLayer, ui::DEFAULT_LAYER_STACK)
.setLayer(redLayer, INT32_MAX)
.apply();
@@ -648,7 +648,7 @@
Transaction()
.show(layer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, 0)
+ .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setCrop(layer, bounds)
@@ -695,7 +695,7 @@
.show(layer)
.show(childLayer)
.hide(mFGSurfaceControl)
- .setLayerStack(layer, 0)
+ .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
.setLayer(layer, INT32_MAX)
.setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
.setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index a424059..28e8b8c 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
+#include <android-base/stringprintf.h>
#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <gtest/gtest.h>
@@ -56,10 +57,7 @@
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
-constexpr auto UNIQUE_TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface#0";
-constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface#0";
constexpr auto LAYER_NAME = "Layer Create and Delete Test";
-constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.winscope";
@@ -105,11 +103,15 @@
system("service call SurfaceFlinger 1020 i32 0 > /dev/null");
}
+std::string getUniqueName(const std::string& name, const Increment& increment) {
+ return base::StringPrintf("%s#%d", name.c_str(), increment.surface_creation().id());
+}
+
int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) {
int32_t layerId = 0;
for (const auto& increment : capturedTrace.increment()) {
if (increment.increment_case() == increment.kSurfaceCreation) {
- if (increment.surface_creation().name() == surfaceName) {
+ if (increment.surface_creation().name() == getUniqueName(surfaceName, increment)) {
layerId = increment.surface_creation().id();
}
}
@@ -283,7 +285,7 @@
ASSERT_TRUE(mFGSurfaceControl->isValid());
Transaction t;
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ASSERT_EQ(NO_ERROR,
t.setLayer(mBGSurfaceControl, INT_MAX - 3)
.show(mBGSurfaceControl)
@@ -293,8 +295,8 @@
}
void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
- mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_BG_SURFACE_NAME);
- mFGLayerId = getSurfaceId(trace, UNIQUE_TEST_FG_SURFACE_NAME);
+ mBGLayerId = getSurfaceId(trace, TEST_BG_SURFACE_NAME);
+ mFGLayerId = getSurfaceId(trace, TEST_FG_SURFACE_NAME);
}
void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
@@ -380,7 +382,7 @@
}
void SurfaceInterceptorTest::layerStackUpdate(Transaction& t) {
- t.setLayerStack(mBGSurfaceControl, STACK_UPDATE);
+ t.setLayerStack(mBGSurfaceControl, ui::LayerStack::fromValue(STACK_UPDATE));
}
void SurfaceInterceptorTest::hiddenFlagUpdate(Transaction& t) {
@@ -752,9 +754,7 @@
}
bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
- bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME &&
- increment.surface_creation().w() == SIZE_UPDATE &&
- increment.surface_creation().h() == SIZE_UPDATE);
+ bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
if (isMatch && !foundSurface) {
foundSurface = true;
} else if (isMatch && foundSurface) {
@@ -808,7 +808,7 @@
break;
case Increment::IncrementCase::kSurfaceDeletion:
// Find the id of created surface.
- targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME);
+ targetId = getSurfaceId(trace, LAYER_NAME);
foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement);
break;
case Increment::IncrementCase::kDisplayCreation:
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 89f6086..60cffb1 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -65,7 +65,7 @@
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(vDisplay, producer);
- t.setDisplayLayerStack(vDisplay, 0);
+ t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
t.setDisplayProjection(vDisplay, displayState.orientation,
Rect(displayState.layerStackSpaceRect), Rect(resolution));
t.apply();
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 15b79be..bb52245 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -22,6 +22,7 @@
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
+using gui::DisplayInfo;
using gui::WindowInfo;
class WindowInfosListenerTest : public ::testing::Test {
@@ -40,7 +41,8 @@
struct SyncWindowInfosListener : public gui::WindowInfosListener {
public:
- void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+ void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
+ const std::vector<DisplayInfo>&) override {
windowInfosPromise.set_value(windowInfos);
}
@@ -84,7 +86,7 @@
ISurfaceComposerClient::eFXSurfaceBufferState);
Transaction()
- .setLayerStack(surfaceControl, 0)
+ .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
.show(surfaceControl)
.setLayer(surfaceControl, INT32_MAX - 1)
.setInputWindowInfo(surfaceControl, windowInfo)
@@ -112,7 +114,7 @@
ISurfaceComposerClient::eFXSurfaceBufferState);
const Rect crop(0, 0, 100, 100);
Transaction()
- .setLayerStack(surfaceControl, 0)
+ .setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
.show(surfaceControl)
.setLayer(surfaceControl, INT32_MAX - 1)
.setCrop(surfaceControl, crop)
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2551a19..168b576 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -12,10 +12,10 @@
defaults: ["surfaceflinger_defaults"],
test_suites: ["device-tests"],
srcs: [
- "FakeComposerClient.cpp",
- "FakeComposerService.cpp",
- "FakeComposerUtils.cpp",
- "SFFakeHwc_test.cpp"
+ "FakeComposerClient.cpp",
+ "FakeComposerService.cpp",
+ "FakeComposerUtils.cpp",
+ "SFFakeHwc_test.cpp",
],
require_root: true,
shared_libs: [
@@ -23,12 +23,14 @@
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.hardware.graphics.composer3-V1-ndk",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
"android.hardware.power@1.3",
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"libfmq",
"libgui",
@@ -43,15 +45,18 @@
],
static_libs: [
"android.hardware.graphics.composer@2.1-resources",
+ "libaidlcommonsupport",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
"librenderengine",
"libtrace_proto",
+ "libaidlcommonsupport",
],
header_libs: [
"android.hardware.graphics.composer@2.4-command-buffer",
"android.hardware.graphics.composer@2.4-hal",
+ "android.hardware.graphics.composer3-command-buffer",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 162711d..b3b4ec1 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -279,7 +279,7 @@
}
bool waitForHotplugEvent(Display displayId, bool connected) {
- return waitForHotplugEvent(PhysicalDisplayId(displayId), connected);
+ return waitForHotplugEvent(physicalIdFromHwcDisplayId(displayId), connected);
}
bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
@@ -305,7 +305,7 @@
}
bool waitForModeChangedEvent(Display display, int32_t modeId) {
- PhysicalDisplayId displayId(display);
+ PhysicalDisplayId displayId = physicalIdFromHwcDisplayId(display);
int waitCount = 20;
while (waitCount--) {
while (!mReceivedDisplayEvents.empty()) {
@@ -363,7 +363,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -426,7 +426,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -479,7 +479,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -534,7 +534,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -586,7 +586,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -651,7 +651,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -703,7 +703,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -750,7 +750,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -797,7 +797,7 @@
{
TransactionScope ts(*mFakeComposerClient);
- ts.setDisplayLayerStack(display, 0);
+ ts.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
@@ -1195,7 +1195,7 @@
fillSurfaceRGBA8(mFGSurfaceControl, RED);
Transaction t;
- t.setDisplayLayerStack(display, 0);
+ t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
t.show(mBGSurfaceControl);
@@ -1342,7 +1342,7 @@
ALOGD("TransactionTest::SetLayerStack");
{
TransactionScope ts(*sFakeComposer);
- ts.setLayerStack(mFGSurfaceControl, 1);
+ ts.setLayerStack(mFGSurfaceControl, ui::LayerStack{1});
}
// Foreground layer should have disappeared.
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9e704c3..5568418 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -23,7 +23,10 @@
cc_test {
name: "libsurfaceflinger_unittest",
- defaults: ["surfaceflinger_defaults"],
+ defaults: [
+ "skia_renderengine_deps",
+ "surfaceflinger_defaults",
+ ],
test_suites: ["device-tests"],
sanitize: {
// Using the address sanitizer not only helps uncover issues in the code
@@ -54,6 +57,7 @@
"DisplayDevice_InitiateModeChange.cpp",
"DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
+ "FlagManagerTest.cpp",
"FpsReporterTest.cpp",
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
@@ -67,10 +71,11 @@
"MessageQueueTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
+ "SurfaceFlinger_DisplayModeSwitching.cpp",
+ "SurfaceFlinger_DisplayTransactionCommitTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
- "SurfaceFlinger_HandleTransactionLockedTest.cpp",
- "SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
+ "SurfaceFlinger_NotifyPowerBoostTest.cpp",
"SurfaceFlinger_OnInitializeDisplaysTest.cpp",
"SurfaceFlinger_SetDisplayStateTest.cpp",
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
@@ -87,7 +92,9 @@
"TimerTest.cpp",
"TransactionApplicationTest.cpp",
"TransactionFrameTracerTest.cpp",
+ "TransactionProtoParserTest.cpp",
"TransactionSurfaceFrameTest.cpp",
+ "TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
@@ -102,7 +109,6 @@
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
"mock/MockFrameTracer.cpp",
- "mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
"mock/MockTimeStats.cpp",
@@ -111,15 +117,20 @@
"mock/system/window/MockNativeWindow.cpp",
],
static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.common.fmq-V1-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
+ "android.hardware.graphics.composer3-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power@1.2",
"android.hardware.power@1.3",
- "android.hardware.power-V1-cpp",
+ "android.hardware.power-V2-cpp",
+ "libaidlcommonsupport",
"libcompositionengine_mocks",
"libcompositionengine",
"libframetimeline",
@@ -127,12 +138,14 @@
"libgui_mocks",
"liblayers_proto",
"libperfetto_client_experimental",
- "librenderengine_mocks",
"librenderengine",
+ "librenderengine_mocks",
+ "libscheduler",
"libserviceutils",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
+ "libtonemap",
"libtrace_proto",
"perfetto_trace_protos",
],
@@ -145,6 +158,7 @@
"android.hardware.graphics.common@1.2",
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"libEGL",
"libfmq",
@@ -161,12 +175,14 @@
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
"android.hardware.graphics.composer@2.2-command-buffer",
"android.hardware.graphics.composer@2.3-command-buffer",
"android.hardware.graphics.composer@2.4-command-buffer",
+ "android.hardware.graphics.composer3-command-buffer",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 6a7ec9b..6f85498 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -14,11 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
#undef LOG_TAG
#define LOG_TAG "CachingTest"
@@ -42,7 +37,7 @@
sp<IBinder> binder = new BBinder();
// test getting invalid client_cache_id
client_cache_t id;
- uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+ int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
}
@@ -51,7 +46,7 @@
client_cache_t id;
id.token = binder;
id.id = 0;
- uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+ int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
client_cache_t idB;
@@ -72,31 +67,28 @@
std::vector<client_cache_t> ids;
uint32_t cacheId = 0;
// fill up cache
- for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
client_cache_t id;
id.token = binder;
id.id = cacheId;
ids.push_back(id);
- uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+ int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
- for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(ids[i]);
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
}
- for (uint32_t i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
+ for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
client_cache_t id;
id.token = binder;
id.id = cacheId;
- uint32_t slot = mHwcSlotGenerator->getHwcCacheSlot(id);
+ int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
cacheId++;
}
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 52a36a2..554e454 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,7 +45,6 @@
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockTimeStats.h"
#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
@@ -77,12 +76,12 @@
constexpr hal::HWLayerId HWC_LAYER = 5000;
constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
-constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42);
+constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u);
constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
constexpr int DEFAULT_TEXTURE_ID = 6000;
-constexpr int DEFAULT_LAYER_STACK = 7000;
+constexpr ui::LayerStack LAYER_STACK{7000u};
constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500;
@@ -95,7 +94,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
@@ -140,14 +138,11 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- constexpr ISchedulerCallback* kCallback = nullptr;
+ constexpr scheduler::ISchedulerCallback* kCallback = nullptr;
constexpr bool kHasMultipleConfigs = true;
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread), kCallback,
kHasMultipleConfigs);
-
- // Layer history should be created if there are multiple configs.
- ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
@@ -157,7 +152,7 @@
// pain)
// mFlinger.mutableVisibleRegionsDirty() = true;
- mFlinger.mutableGeometryInvalid() = true;
+ mFlinger.mutableGeometryDirty() = true;
}
template <typename Case>
@@ -186,7 +181,6 @@
Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::TimeStats* mTimeStats = new mock::TimeStats();
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
Hwc2::mock::PowerAdvisor mPowerAdvisor;
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -202,8 +196,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
- mFlinger.onMessageReceived(MessageQueue::REFRESH);
+ mFlinger.commitAndComposite();
LayerCase::cleanup(this);
}
@@ -215,8 +208,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
- mFlinger.onMessageReceived(MessageQueue::REFRESH);
+ mFlinger.commitAndComposite();
LayerCase::cleanup(this);
}
@@ -247,14 +239,28 @@
"screenshot"),
*mRenderEngine, true);
- status_t result =
- mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
- forSystem, regionSampling);
- EXPECT_EQ(NO_ERROR, result);
+ auto result = mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer,
+ forSystem, regionSampling);
+ EXPECT_TRUE(result.valid());
+
+ auto& [status, drawFence] = result.get();
+
+ EXPECT_EQ(NO_ERROR, status);
+ if (drawFence.ok()) {
+ sync_wait(drawFence.get(), -1);
+ }
LayerCase::cleanup(this);
}
+template <class T>
+std::future<T> futureOf(T obj) {
+ std::promise<T> resultPromise;
+ std::future<T> resultFuture = resultPromise.get_future();
+ resultPromise.set_value(std::move(obj));
+ return resultFuture;
+}
+
/* ------------------------------------------------------------------------
* Variants for each display configuration which can be tested
*/
@@ -287,10 +293,8 @@
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
.setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(ui::DisplayConnectionType::Internal)
.setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
.setIsSecure(Derived::IS_SECURE)
- .setLayerStackId(DEFAULT_LAYER_STACK)
.setPowerAdvisor(&test->mPowerAdvisor)
.setName(std::string("Injected display for ") +
test_info->test_case_name() + "." + test_info->name())
@@ -309,7 +313,7 @@
.setPowerMode(Derived::INIT_POWER_MODE)
.inject();
Mock::VerifyAndClear(test->mNativeWindow);
- test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
+ test->mDisplay->setLayerStack(LAYER_STACK);
}
template <typename Case>
@@ -339,16 +343,18 @@
template <typename Case>
static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>&,
- const std::shared_ptr<renderengine::ExternalTexture>&,
- const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+ .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ const bool, base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.clip);
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
});
}
@@ -364,7 +370,7 @@
EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
}
@@ -378,7 +384,7 @@
static void setupRECompositionCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Gpu))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -388,17 +394,19 @@
.WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
Return(0)));
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>&,
- const std::shared_ptr<renderengine::ExternalTexture>&,
- const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+ .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ const bool, base::unique_fd&&)
+ -> std::future<renderengine::RenderEngineResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.clip);
EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
- return NO_ERROR;
+ return futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
});
}
@@ -521,15 +529,16 @@
static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
// TODO: Eliminate the complexity of actually creating a buffer
+ layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT);
status_t err =
layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::FORMAT);
ASSERT_EQ(NO_ERROR, err);
Mock::VerifyAndClear(test->mRenderEngine);
- EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
enqueueBuffer(test, layer);
- Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
bool ignoredRecomputeVisibleRegions;
layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
@@ -622,10 +631,10 @@
static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillOnce([](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> status_t {
+ .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -633,23 +642,26 @@
displaySettings.clip);
// screen capture adds an additional color layer as an alpha
// prefill, so gtet the back layer.
+ std::future<renderengine::RenderEngineResult> resultFuture =
+ futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
if (layerSettings.empty()) {
ADD_FAILURE() << "layerSettings was not expected to be empty in "
"setupREBufferCompositionCommonCallExpectations "
"verification lambda";
- return NO_ERROR;
+ return resultFuture;
}
- 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(false, layer->source.buffer.isY410BT2020);
- EXPECT_EQ(true, layer->source.buffer.usePremultipliedAlpha);
- EXPECT_EQ(false, layer->source.buffer.isOpaque);
- EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
- EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
- return NO_ERROR;
+ 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(false, layer.source.buffer.isY410BT2020);
+ EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
+ EXPECT_EQ(false, layer.source.buffer.isOpaque);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return resultFuture;
});
}
@@ -671,10 +683,10 @@
static void setupREColorCompositionCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillOnce([](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> status_t {
+ .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -682,21 +694,24 @@
displaySettings.clip);
// screen capture adds an additional color layer as an alpha
// prefill, so get the back layer.
+ std::future<renderengine::RenderEngineResult> resultFuture =
+ futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
if (layerSettings.empty()) {
ADD_FAILURE()
<< "layerSettings was not expected to be empty in "
"setupREColorCompositionCallExpectations verification lambda";
- return NO_ERROR;
+ return resultFuture;
}
- const renderengine::LayerSettings* layer = layerSettings.back();
- EXPECT_THAT(layer->source.buffer.buffer, IsNull());
+ const renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2]),
- layer->source.solidColor);
- EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
- EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
- return NO_ERROR;
+ layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+ return resultFuture;
});
}
@@ -748,10 +763,10 @@
static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillOnce([](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>& layerSettings,
- const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
- base::unique_fd&&, base::unique_fd*) -> status_t {
+ .WillOnce([&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>& layerSettings,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
displaySettings.physicalDisplay);
@@ -759,19 +774,22 @@
displaySettings.clip);
// screen capture adds an additional color layer as an alpha
// prefill, so get the back layer.
+ std::future<renderengine::RenderEngineResult> resultFuture =
+ futureOf<renderengine::RenderEngineResult>(
+ {NO_ERROR, base::unique_fd()});
if (layerSettings.empty()) {
ADD_FAILURE() << "layerSettings was not expected to be empty in "
"setupInsecureREBufferCompositionCommonCallExpectations "
"verification lambda";
- return NO_ERROR;
+ return resultFuture;
}
- const renderengine::LayerSettings* layer = layerSettings.back();
- EXPECT_THAT(layer->source.buffer.buffer, IsNull());
- EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer->source.solidColor);
- EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
- EXPECT_EQ(1.0f, layer->alpha);
- return NO_ERROR;
+ const renderengine::LayerSettings layer = layerSettings.back();
+ EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+ EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
+ EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
+ EXPECT_EQ(1.0f, layer.alpha);
+ return resultFuture;
});
}
@@ -815,16 +833,16 @@
struct BaseLayerVariant {
template <typename L, typename F>
static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
- EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(0);
+ EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0);
sp<L> layer = factory();
// Layer should be registered with scheduler.
- EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+ EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize());
Mock::VerifyAndClear(test->mComposer);
Mock::VerifyAndClear(test->mRenderEngine);
- Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
initLayerDrawingStateAndComputeBounds(test, layer);
@@ -834,7 +852,7 @@
template <typename L>
static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
- layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
+ layerDrawingState.layerStack = LAYER_STACK;
layerDrawingState.width = 100;
layerDrawingState.height = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
@@ -864,8 +882,8 @@
test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
// Layer should be unregistered with scheduler.
- test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
- EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
+ test->mFlinger.commit();
+ EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize());
}
};
@@ -878,7 +896,6 @@
FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
return new EffectLayer(
LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata()));
});
@@ -917,7 +934,6 @@
FlingerLayerType layer =
Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
args.textureName = test->mFlinger.mutableTexturePool().back();
return new BufferQueueLayer(args);
@@ -929,7 +945,6 @@
}
static void cleanupInjectedLayers(CompositionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(1);
Base::cleanupInjectedLayers(test);
}
@@ -967,7 +982,6 @@
static FlingerLayerType createLayer(CompositionTest* test) {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
FlingerLayerType layer = new ContainerLayer(args);
Base::template initLayerDrawingStateAndComputeBounds(test, layer);
@@ -1015,9 +1029,10 @@
}
};
-template <IComposerClient::Composition CompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition CompositionType>
struct KeepCompositionTypeVariant {
- static constexpr hal::Composition TYPE = CompositionType;
+ static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+ CompositionType;
static void setupHwcSetCallExpectations(CompositionTest* test) {
if (!test->mDisplayOff) {
@@ -1032,10 +1047,11 @@
}
};
-template <IComposerClient::Composition InitialCompositionType,
- IComposerClient::Composition FinalCompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition InitialCompositionType,
+ aidl::android::hardware::graphics::composer3::Composition FinalCompositionType>
struct ChangeCompositionTypeVariant {
- static constexpr hal::Composition TYPE = FinalCompositionType;
+ static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+ FinalCompositionType;
static void setupHwcSetCallExpectations(CompositionTest* test) {
if (!test->mDisplayOff) {
@@ -1049,8 +1065,9 @@
EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{
static_cast<Hwc2::Layer>(HWC_LAYER)}),
- SetArgPointee<2>(std::vector<IComposerClient::Composition>{
- FinalCompositionType}),
+ SetArgPointee<2>(
+ std::vector<aidl::android::hardware::graphics::composer3::
+ Composition>{FinalCompositionType}),
Return(Error::NONE)));
}
};
@@ -1244,25 +1261,28 @@
*/
TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedNormalBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenNormalBufferLayer) {
@@ -1276,25 +1296,28 @@
*/
TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedEffectLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenEffectLayer) {
@@ -1308,25 +1331,28 @@
*/
TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedSidebandBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSidebandBufferLayer) {
@@ -1340,25 +1366,28 @@
*/
TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedSecureBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) {
@@ -1372,17 +1401,19 @@
*/
TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) {
@@ -1397,22 +1428,24 @@
TEST_F(CompositionTest,
HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest,
HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
@@ -1428,25 +1461,28 @@
*/
TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedCursorLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenCursorLayer) {
@@ -1463,7 +1499,8 @@
mDisplayOff = true;
displayRefreshCompositionDirtyGeometry<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
HwcCompositionResultVariant>>();
}
@@ -1471,7 +1508,8 @@
mDisplayOff = true;
displayRefreshCompositionDirtyFrame<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
HwcCompositionResultVariant>>();
}
@@ -1479,8 +1517,9 @@
mDisplayOff = true;
displayRefreshCompositionDirtyFrame<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
RECompositionResultVariant>>();
}
@@ -1495,17 +1534,19 @@
*/
TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionViaDebugOptionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
}
TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionViaDebugOptionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index d4cfbbb..5a0033e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -29,7 +29,7 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
- using Event = scheduler::RefreshRateConfigEvent;
+ using Event = scheduler::DisplayModeEvent;
void SetUp() override {
injectFakeBufferQueueFactory();
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
index 9fe30f8..3d24ecb 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -84,10 +84,10 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -96,13 +96,14 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
// For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -111,9 +112,9 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
@@ -122,13 +123,14 @@
EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
mHardwareDisplaySize.height),
compositionState.transform);
- EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.getOrientation());
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.getContent());
// For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
// size width and height swapped
EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ compositionState.orientedDisplaySpace.getContent());
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.layerStackSpace.getContent());
EXPECT_EQ(false, compositionState.needsFiltering);
}
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index cc24323..b1f704a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -38,7 +38,6 @@
// Default to no wide color display support configured
mFlinger.mutableHasWideColorDisplay() = false;
- mFlinger.mutableUseColorManagement() = false;
mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
mFlinger.setCreateBufferQueueFunction([](auto, auto, auto) {
@@ -51,7 +50,6 @@
});
injectMockScheduler();
- mFlinger.mutableEventQueue().reset(mMessageQueue);
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.mutableInterceptor() = mSurfaceInterceptor;
@@ -122,7 +120,7 @@
sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
- constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777);
+ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
@@ -139,20 +137,18 @@
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
- constexpr auto kConnectionType = ui::DisplayConnectionType::Internal;
- constexpr bool kIsPrimary = true;
-
auto compositionDisplay =
compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(),
compositionengine::DisplayCreationArgsBuilder()
.setId(DEFAULT_DISPLAY_ID)
- .setConnectionType(kConnectionType)
.setPixels({DEFAULT_DISPLAY_WIDTH,
DEFAULT_DISPLAY_HEIGHT})
.setPowerAdvisor(&mPowerAdvisor)
.build());
- auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, kConnectionType,
+ constexpr bool kIsPrimary = true;
+ auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay,
+ ui::DisplayConnectionType::Internal,
DEFAULT_DISPLAY_HWC_DISPLAY_ID, kIsPrimary);
injector.setNativeWindow(mNativeWindow);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index de058a4..0a3437a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,7 +47,6 @@
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockNativeWindowSurface.h"
#include "mock/MockSchedulerCallback.h"
#include "mock/MockSurfaceInterceptor.h"
@@ -118,12 +117,11 @@
// to keep a reference to them for use in setting up call expectations.
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
Hwc2::mock::Composer* mComposer = nullptr;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
mock::VsyncController* mVsyncController = new mock::VsyncController;
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
- mock::SchedulerCallback mSchedulerCallback;
+ scheduler::mock::SchedulerCallback mSchedulerCallback;
mock::EventThread* mEventThread = new mock::EventThread;
mock::EventThread* mSFEventThread = new mock::EventThread;
@@ -233,7 +231,7 @@
// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage>
+ Secure secure, Primary primary, int grallocUsage, int displayFlags>
struct DisplayVariant {
using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
@@ -261,17 +259,14 @@
// Whether the display is primary
static constexpr Primary PRIMARY = primary;
+ static constexpr int DISPLAY_FLAGS = displayFlags;
+
static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
ceDisplayArgs.setId(DISPLAY_ID::get())
.setPixels({WIDTH, HEIGHT})
.setPowerAdvisor(&test->mPowerAdvisor);
- const auto connectionType = CONNECTION_TYPE::value;
- if (connectionType) {
- ceDisplayArgs.setConnectionType(*connectionType);
- }
-
auto compositionDisplay =
compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
ceDisplayArgs.build());
@@ -279,7 +274,7 @@
auto injector =
TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
compositionDisplay,
- connectionType,
+ CONNECTION_TYPE::value,
HWC_DISPLAY_ID_OPT::value,
static_cast<bool>(PRIMARY));
@@ -388,7 +383,6 @@
auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
.setId(DisplayVariant::DISPLAY_ID::get())
- .setConnectionType(PhysicalDisplay::CONNECTION_TYPE)
.setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
.setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
.setPowerAdvisor(&test->mPowerAdvisor)
@@ -475,16 +469,19 @@
constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1;
+
template <typename PhysicalDisplay, int width, int height, Critical critical>
struct PhysicalDisplayVariant
: DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- critical, Async::FALSE, Secure::TRUE,
- PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- PhysicalDisplay> {};
+ GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<
+ PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>,
+ PhysicalDisplay> {};
template <bool hasIdentificationData>
struct PrimaryDisplay {
@@ -526,13 +523,16 @@
// A virtual display not supported by the HWC.
constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+constexpr int VIRTUAL_DISPLAY_FLAGS = 0x0;
+
template <int width, int height, Secure secure>
struct NonHwcVirtualDisplayVariant
: DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
- using Base =
- DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS> {
+ using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>;
static void injectHwcDisplay(DisplayTransactionTest*) {}
@@ -575,13 +575,16 @@
template <int width, int height, Secure secure>
struct HwcVirtualDisplayVariant
: DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
- Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY,
+ VIRTUAL_DISPLAY_FLAGS>,
+ HwcDisplayVariant<
+ HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> {
using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER,
+ VIRTUAL_DISPLAY_FLAGS>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
@@ -671,7 +674,6 @@
static constexpr bool WIDE_COLOR_SUPPORTED = false;
static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
test->mFlinger.mutableHasWideColorDisplay() = true;
}
@@ -690,7 +692,6 @@
static void injectConfigChange(DisplayTransactionTest* test) {
test->mFlinger.mutableHasWideColorDisplay() = false;
- test->mFlinger.mutableUseColorManagement() = false;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 4ff7592..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -28,6 +28,7 @@
#include "AsyncCallRecorder.h"
#include "DisplayHardware/DisplayMode.h"
+#include "FrameTimeline.h"
#include "Scheduler/EventThread.h"
using namespace std::chrono_literals;
@@ -41,10 +42,13 @@
namespace {
-constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111);
-constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222);
-constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL);
+constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(111u);
+constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = PhysicalDisplayId::fromPort(222u);
+constexpr PhysicalDisplayId DISPLAY_ID_64BIT =
+ PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu);
+
constexpr std::chrono::duration VSYNC_PERIOD(16ms);
+
class MockVSyncSource : public VSyncSource {
public:
const char* getName() const override { return "test"; }
@@ -93,6 +97,8 @@
ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount);
void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount);
+ void expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline);
void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected);
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
@@ -117,6 +123,7 @@
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
sp<MockEventThreadConnection> mThrottledConnection;
+ std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager;
static constexpr uid_t mConnectionUid = 443;
static constexpr uid_t mThrottledConnectionUid = 177;
@@ -170,8 +177,8 @@
return VSYNC_PERIOD.count();
};
- mThread = std::make_unique<impl::EventThread>(std::move(source),
- /*tokenManager=*/nullptr,
+ mTokenManager = std::make_unique<frametimeline::impl::TokenManager>();
+ mThread = std::make_unique<impl::EventThread>(std::move(source), mTokenManager.get(),
mInterceptVSyncCallRecorder.getInvocable(),
throttleVsync, getVsyncPeriod);
@@ -244,6 +251,45 @@
expectedCount);
}
+void EventThreadTest::expectVsyncEventFrameTimelinesCorrect(nsecs_t expectedTimestamp,
+ nsecs_t preferredDeadline) {
+ auto args = mConnectionEventCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value()) << " did not receive an event for timestamp "
+ << expectedTimestamp;
+ const auto& event = std::get<0>(args.value());
+ for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ auto prediction =
+ mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+ EXPECT_TRUE(prediction.has_value());
+ EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+ << "Deadline timestamp does not match cached value";
+ EXPECT_EQ(prediction.value().presentTime,
+ event.vsync.frameTimelines[i].expectedVSyncTimestamp)
+ << "Expected vsync timestamp does not match cached value";
+
+ if (i > 0) {
+ EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
+ event.vsync.frameTimelines[i - 1].deadlineTimestamp)
+ << "Deadline timestamp out of order for frame timeline " << i;
+ EXPECT_GT(event.vsync.frameTimelines[i].expectedVSyncTimestamp,
+ event.vsync.frameTimelines[i - 1].expectedVSyncTimestamp)
+ << "Expected vsync timestamp out of order for frame timeline " << i;
+ }
+ if (event.vsync.frameTimelines[i].deadlineTimestamp == preferredDeadline) {
+ EXPECT_EQ(i, event.vsync.preferredFrameTimelineIndex)
+ << "Preferred frame timeline index should be " << i;
+ // For the platform-preferred frame timeline, the vsync ID is 0 because the first frame
+ // timeline is made before the rest.
+ EXPECT_EQ(0, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ } else {
+ // Vsync ID 0 is used for the preferred frame timeline.
+ EXPECT_EQ(i + 1, event.vsync.frameTimelines[i].vsyncId)
+ << "Vsync ID incorrect for frame timeline " << i;
+ }
+ }
+}
+
void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
bool expectedConnected) {
auto args = mConnectionEventCallRecorder.waitForCall();
@@ -341,6 +387,19 @@
expectVSyncSetEnabledCallReceived(false);
}
+TEST_F(EventThreadTest, requestNextVsyncEventFrameTimelinesCorrect) {
+ // Signal that we want the next vsync event to be posted to the connection
+ mThread->requestNextVsync(mConnection);
+
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a vsync event.
+ // The interceptor should receive the event, as well as the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectVsyncEventFrameTimelinesCorrect(123, 789);
+}
+
TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) {
// Create a first connection, register it, and request a vsync rate of zero.
ConnectionEventRecorder firstConnectionEventRecorder{0};
diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
new file mode 100644
index 0000000..0905cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#include <cstdint>
+#undef LOG_TAG
+#define LOG_TAG "FlagManagerTest"
+
+#include "FlagManager.h"
+
+#include <android-base/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+#include <optional>
+
+namespace android {
+
+using testing::Return;
+
+class MockFlagManager : public FlagManager {
+public:
+ MockFlagManager() = default;
+ ~MockFlagManager() = default;
+
+ MOCK_METHOD(std::string, getServerConfigurableFlag, (const std::string& experimentFlagName),
+ (const, override));
+};
+
+class FlagManagerTest : public testing::Test {
+public:
+ FlagManagerTest();
+ ~FlagManagerTest() override;
+ std::unique_ptr<MockFlagManager> mFlagManager;
+
+ template <typename T>
+ T getValue(const std::string& experimentFlagName, std::optional<T> systemPropertyOpt,
+ T defaultValue);
+};
+
+FlagManagerTest::FlagManagerTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ mFlagManager = std::make_unique<MockFlagManager>();
+}
+
+FlagManagerTest::~FlagManagerTest() {
+ 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());
+}
+
+template <typename T>
+T FlagManagerTest::getValue(const std::string& experimentFlagName,
+ std::optional<T> systemPropertyOpt, T defaultValue) {
+ return mFlagManager->getValue(experimentFlagName, systemPropertyOpt, defaultValue);
+}
+
+namespace {
+TEST_F(FlagManagerTest, getValue_bool_default) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+ const bool defaultValue = false;
+ std::optional<bool> systemPropertyValue = std::nullopt;
+ const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_bool_sysprop) {
+ const bool defaultValue = false;
+ std::optional<bool> systemPropertyValue = std::make_optional(true);
+ const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, true);
+}
+
+TEST_F(FlagManagerTest, getValue_bool_experiment) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("1"));
+ const bool defaultValue = false;
+ std::optional<bool> systemPropertyValue = std::nullopt;
+ const bool result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, true);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_default) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+ int32_t defaultValue = 30;
+ std::optional<int32_t> systemPropertyValue = std::nullopt;
+ int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_sysprop) {
+ int32_t defaultValue = 30;
+ std::optional<int32_t> systemPropertyValue = std::make_optional(10);
+ int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, 10);
+}
+
+TEST_F(FlagManagerTest, getValue_int32_experiment) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
+ std::int32_t defaultValue = 30;
+ std::optional<std::int32_t> systemPropertyValue = std::nullopt;
+ std::int32_t result = FlagManagerTest::getValue("test_flag", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, 50);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_default) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return(""));
+ int64_t defaultValue = 30;
+ std::optional<int64_t> systemPropertyValue = std::nullopt;
+ int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, defaultValue);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_sysprop) {
+ int64_t defaultValue = 30;
+ std::optional<int64_t> systemPropertyValue = std::make_optional(10);
+ int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, 10);
+}
+
+TEST_F(FlagManagerTest, getValue_int64_experiment) {
+ EXPECT_CALL(*mFlagManager, getServerConfigurableFlag).Times(1).WillOnce(Return("50"));
+ int64_t defaultValue = 30;
+ std::optional<int64_t> systemPropertyValue = std::nullopt;
+ int64_t result = getValue("flag_name", systemPropertyValue, defaultValue);
+ ASSERT_EQ(result, 50);
+}
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsOps.h b/services/surfaceflinger/tests/unittests/FpsOps.h
new file mode 100644
index 0000000..7c737dc
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsOps.h
@@ -0,0 +1,49 @@
+/*
+ * 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 <scheduler/Fps.h>
+
+namespace android {
+
+// Pull Fps operators into its namespace to enable ADL for EXPECT_EQ, EXPECT_LT, etc.
+
+inline bool operator==(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator==(lhs, rhs);
+}
+
+inline bool operator<(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator<(lhs, rhs);
+}
+
+inline bool operator!=(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator!=(lhs, rhs);
+}
+
+inline bool operator>(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator>(lhs, rhs);
+}
+
+inline bool operator<=(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator<=(lhs, rhs);
+}
+
+inline bool operator>=(Fps lhs, Fps rhs) {
+ return fps_approx_ops::operator>=(lhs, rhs);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index 010c675..bb1f432 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "FpsReporterTest"
+#include <chrono>
+
#include <android/gui/BnFpsListener.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -36,6 +38,8 @@
namespace android {
+using namespace std::chrono_literals;
+
using testing::_;
using testing::DoAll;
using testing::Mock;
@@ -114,8 +118,7 @@
sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, metadata);
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
return new BufferStateLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp
index db732cf..88b74d2 100644
--- a/services/surfaceflinger/tests/unittests/FpsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp
@@ -14,85 +14,58 @@
* limitations under the License.
*/
-#include "Fps.h"
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <scheduler/Fps.h>
+
+#include "FpsOps.h"
+
namespace android {
TEST(FpsTest, construct) {
- Fps fpsDefault;
- EXPECT_FALSE(fpsDefault.isValid());
+ EXPECT_FALSE(Fps().isValid());
- Fps fps1(60.0f);
- EXPECT_TRUE(fps1.isValid());
- Fps fps2 = Fps::fromPeriodNsecs(static_cast<nsecs_t>(1e9f / 60.0f));
- EXPECT_TRUE(fps2.isValid());
- EXPECT_TRUE(fps1.equalsWithMargin(fps2));
+ EXPECT_FALSE((0_Hz).isValid());
+ EXPECT_TRUE((120_Hz).isValid());
+ EXPECT_TRUE((0.5_Hz).isValid());
+
+ EXPECT_FALSE(Fps::fromPeriodNsecs(0).isValid());
+
+ const Fps fps = Fps::fromPeriodNsecs(16'666'667);
+ EXPECT_TRUE(fps.isValid());
+ EXPECT_EQ(fps, 60_Hz);
}
TEST(FpsTest, compare) {
- constexpr float kEpsilon = 1e-4f;
- const Fps::EqualsInBuckets equalsInBuckets;
- const Fps::EqualsWithMargin equalsWithMargin;
+ EXPECT_EQ(60_Hz, 60_Hz);
+ EXPECT_EQ(60_Hz, 59.9999_Hz);
+ EXPECT_EQ(60_Hz, 60.0001_Hz);
- EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f)));
- EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f - kEpsilon)));
- EXPECT_TRUE(Fps(60.0f).equalsWithMargin(Fps(60.f + kEpsilon)));
+ EXPECT_LE(60_Hz, 60_Hz);
+ EXPECT_LE(60_Hz, 59.9999_Hz);
+ EXPECT_LE(60_Hz, 60.0001_Hz);
- EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f)));
- EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f - kEpsilon)));
- EXPECT_TRUE(equalsInBuckets(Fps(60.0f), Fps(60.0f + kEpsilon)));
+ EXPECT_GE(60_Hz, 60_Hz);
+ EXPECT_GE(60_Hz, 59.9999_Hz);
+ EXPECT_GE(60_Hz, 60.0001_Hz);
- EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f)));
- EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f - kEpsilon)));
- EXPECT_TRUE(equalsWithMargin(Fps(60.0f), Fps(60.0f + kEpsilon)));
-
- EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
- EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f)));
- EXPECT_TRUE(Fps(60.0f).lessThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
-
- EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f + kEpsilon)));
- EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f)));
- EXPECT_TRUE(Fps(60.0f).greaterThanOrEqualWithMargin(Fps(60.f - kEpsilon)));
-
- // Fps with difference of 1 should be different
- EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(61.f)));
- EXPECT_TRUE(Fps(60.0f).lessThanWithMargin(Fps(61.f)));
- EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.f)));
+ // Fps with difference of 1 should be different.
+ EXPECT_NE(60_Hz, 61_Hz);
+ EXPECT_LT(60_Hz, 61_Hz);
+ EXPECT_GT(60_Hz, 59_Hz);
// These are common refresh rates which should be different.
- EXPECT_FALSE(Fps(60.0f).equalsWithMargin(Fps(59.94f)));
- EXPECT_TRUE(Fps(60.0f).greaterThanWithMargin(Fps(59.94f)));
- EXPECT_FALSE(equalsInBuckets(Fps(60.0f), Fps(59.94f)));
- EXPECT_FALSE(equalsWithMargin(Fps(60.0f), Fps(59.94f)));
- EXPECT_NE(std::hash<Fps>()(Fps(60.0f)), std::hash<Fps>()(Fps(59.94f)));
-
- EXPECT_FALSE(Fps(30.0f).equalsWithMargin(Fps(29.97f)));
- EXPECT_TRUE(Fps(30.0f).greaterThanWithMargin(Fps(29.97f)));
- EXPECT_FALSE(equalsInBuckets(Fps(30.0f), Fps(29.97f)));
- EXPECT_FALSE(equalsWithMargin(Fps(30.0f), Fps(29.97f)));
- EXPECT_NE(std::hash<Fps>()(Fps(30.0f)), std::hash<Fps>()(Fps(29.97f)));
+ EXPECT_NE(60_Hz, 59.94_Hz);
+ EXPECT_GT(60_Hz, 59.94_Hz);
+ EXPECT_NE(30_Hz, 29.97_Hz);
+ EXPECT_GT(30_Hz, 29.97_Hz);
}
TEST(FpsTest, getIntValue) {
- EXPECT_EQ(30, Fps(30.1f).getIntValue());
- EXPECT_EQ(31, Fps(30.9f).getIntValue());
- EXPECT_EQ(31, Fps(30.5f).getIntValue());
-}
-
-TEST(FpsTest, equalsInBucketsImpliesEqualHashes) {
- constexpr float kStep = 1e-4f;
- const Fps::EqualsInBuckets equals;
- for (float fps = 30.0f; fps < 31.0f; fps += kStep) {
- const Fps left(fps);
- const Fps right(fps + kStep);
- if (equals(left, right)) {
- ASSERT_EQ(std::hash<Fps>()(left), std::hash<Fps>()(right))
- << "left= " << left << " right=" << right;
- }
- }
+ EXPECT_EQ(30, (30.1_Hz).getIntValue());
+ EXPECT_EQ(31, (30.9_Hz).getIntValue());
+ EXPECT_EQ(31, (30.5_Hz).getIntValue());
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 97b60e0..397c619 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -169,13 +169,14 @@
static const std::string sLayerNameOne = "layer1";
static const std::string sLayerNameTwo = "layer2";
-static constexpr const uid_t sUidOne = 0;
-static constexpr pid_t sPidOne = 10;
-static constexpr pid_t sPidTwo = 20;
-static constexpr int32_t sInputEventId = 5;
-static constexpr int32_t sLayerIdOne = 1;
-static constexpr int32_t sLayerIdTwo = 2;
-static constexpr int32_t sGameMode = 0;
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -559,7 +560,7 @@
}
TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
- Fps refreshRate = Fps(11.0);
+ Fps refreshRate = 11_Hz;
EXPECT_CALL(*mTimeStats,
incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne,
sLayerNameOne, sGameMode,
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index 3fa1a2c..981ca1d 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -53,7 +53,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
@@ -91,8 +91,8 @@
}
// Mocks the behavior of applying a transaction from WMShell
- void setGameModeMetadata(sp<Layer> layer, int gameMode) {
- mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode);
+ void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
+ mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
layer->setMetadata(mLayerMetadata);
layer->setGameModeForTree(gameMode);
}
@@ -109,51 +109,52 @@
sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
rootLayer->addChild(childLayer1);
rootLayer->addChild(childLayer2);
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer1->getGameMode(), 2);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
}
TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
rootLayer->addChild(childLayer);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
}
TEST_F(GameModeTest, RemoveChildResetsGameMode) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
rootLayer->addChild(childLayer);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
rootLayer->removeChild(childLayer);
- EXPECT_EQ(childLayer->getGameMode(), 0);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported);
}
TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 1);
+ rootLayer->setGameModeForTree(GameMode::Standard);
rootLayer->addChild(childLayer1);
- setGameModeMetadata(childLayer2, /*gameMode*/ 2);
+ setGameModeMetadata(childLayer2, GameMode::Performance);
rootLayer->addChild(childLayer2);
- EXPECT_EQ(rootLayer->getGameMode(), 1);
- EXPECT_EQ(childLayer1->getGameMode(), 1);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard);
+ EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
rootLayer->removeChild(childLayer2);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
}
-} // namespace android
\ No newline at end of file
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 02ec7fc..cdb2240 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -26,6 +26,7 @@
#include <gtest/gtest.h>
#include <log/log.h>
+#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
#include "TestableScheduler.h"
@@ -37,9 +38,9 @@
using testing::Return;
using testing::ReturnRef;
-namespace android {
+namespace android::scheduler {
-namespace scheduler {
+using MockLayer = android::mock::MockLayer;
class LayerHistoryTest : public testing::Test {
protected:
@@ -50,56 +51,63 @@
static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
LayerInfo::RefreshRateHistory::HISTORY_DURATION;
- static constexpr Fps LO_FPS{30.f};
+ static constexpr Fps LO_FPS = 30_Hz;
static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
- static constexpr Fps HI_FPS{90.f};
+ static constexpr Fps HI_FPS = 90_Hz;
static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
- void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
-
- LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
- const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
- return history().summarize(*mScheduler->refreshRateConfigs(), now);
+ // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+ // however, for testing only, a stable order is required, therefore we sort the list here.
+ // Any tests requiring ordered results must create layers with names.
+ auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+ std::sort(summary.begin(), summary.end(),
+ [](const RefreshRateConfigs::LayerRequirement& a,
+ const RefreshRateConfigs::LayerRequirement& b) -> bool {
+ return a.name < b.name;
+ });
+ return summary;
}
size_t layerCount() const { return mScheduler->layerHistorySize(); }
- size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+ return history().mActiveLayerInfos.size();
+ }
auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isFrequent(now); });
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now);
+ });
}
auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isAnimating(now); });
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isAnimating(now);
+ });
}
void setDefaultLayerVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- for (auto& [layerUnsafe, info] : history().mLayerInfos) {
- if (layerUnsafe == layer) {
- info->setDefaultLayerVote(vote);
- return;
- }
+ auto [found, layerPair] = history().findLayer(layer->getSequence());
+ if (found != LayerHistory::layerStatus::NotFound) {
+ layerPair->second->setDefaultLayerVote(vote);
}
}
- auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
auto createLayer(std::string name) {
- return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
}
- void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, Fps frameRate,
+ void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
Fps desiredRefreshRate, int numFrames) {
LayerHistory::Summary summary;
for (int i = 0; i < numFrames; i++) {
@@ -111,20 +119,21 @@
ASSERT_EQ(1, summary.size());
ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- ASSERT_TRUE(desiredRefreshRate.equalsWithMargin(summary[0].desiredRefreshRate))
- << "Frame rate is " << frameRate;
+ ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
}
std::shared_ptr<RefreshRateConfigs> mConfigs = std::make_shared<
RefreshRateConfigs>(DisplayModes{DisplayMode::Builder(0)
.setId(DisplayModeId(0))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(
+ PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(int32_t(LO_FPS_PERIOD))
.setGroup(0)
.build(),
DisplayMode::Builder(1)
.setId(DisplayModeId(1))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(
+ PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(int32_t(HI_FPS_PERIOD))
.setGroup(0)
.build()},
@@ -144,10 +153,12 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
+
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- const nsecs_t time = systemTime();
+ nsecs_t time = systemTime();
// No layers returned if no layers are active.
EXPECT_TRUE(summarizeLayerHistory(time).empty());
@@ -159,6 +170,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
+ time += LO_FPS_PERIOD;
}
// Max is returned since we have enough history but there is no timestamp votes.
@@ -167,6 +179,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
+ time += LO_FPS_PERIOD;
}
}
@@ -211,7 +224,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
@@ -304,7 +317,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(
- Return(Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::Default)));
+ Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default)));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -317,7 +330,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -326,7 +339,7 @@
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -336,7 +349,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(Return(
- Layer::FrameRate(Fps(73.4f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+ Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -350,7 +363,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -360,15 +373,15 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(Fps(73.4f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
TEST_F(LayerHistoryTest, multipleLayers) {
- auto layer1 = createLayer();
- auto layer2 = createLayer();
- auto layer3 = createLayer();
+ auto layer1 = createLayer("A");
+ auto layer2 = createLayer("B");
+ auto layer3 = createLayer("C");
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
@@ -412,7 +425,7 @@
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_TRUE(HI_FPS.equalsWithMargin(summarizeLayerHistory(time)[1].desiredRefreshRate));
+ EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -427,7 +440,7 @@
ASSERT_EQ(1, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -446,7 +459,7 @@
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -456,9 +469,9 @@
summary = summarizeLayerHistory(time);
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -468,9 +481,9 @@
ASSERT_EQ(2, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
- EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[1].desiredRefreshRate));
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
EXPECT_EQ(2, layerCount());
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -485,7 +498,7 @@
ASSERT_EQ(1, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(LO_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -506,7 +519,7 @@
ASSERT_EQ(1, summary.size());
EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_TRUE(HI_FPS.equalsWithMargin(summary[0].desiredRefreshRate));
+ EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
EXPECT_EQ(1, layerCount());
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
@@ -583,12 +596,12 @@
EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
.WillRepeatedly(Return(
- Layer::FrameRate(Fps(60.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+ Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
.WillRepeatedly(Return(
- Layer::FrameRate(Fps(90.0f), Layer::FrameRateCompatibility::ExactOrMultiple)));
+ Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
nsecs_t time = systemTime();
@@ -601,7 +614,7 @@
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
summarizeLayerHistory(time)[0].vote);
- EXPECT_TRUE(Fps(60.0f).equalsWithMargin(summarizeLayerHistory(time)[0].desiredRefreshRate));
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
}
@@ -652,6 +665,29 @@
EXPECT_EQ(1, animatingLayerCount(time));
}
+TEST_F(LayerHistoryTest, getFramerate) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -659,7 +695,7 @@
nsecs_t time = systemTime();
for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
- recordFramesAndExpect(layer, time, Fps(fps), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
}
}
@@ -669,13 +705,13 @@
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = systemTime();
- recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(30.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(30.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(60.0f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(60.0f), Fps(60.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
}
TEST_F(LayerHistoryTest, heuristicLayerNotOscillating) {
@@ -685,11 +721,11 @@
nsecs_t time = systemTime();
- recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(26.90f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(26.00f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(26.90f), Fps(24.0f), PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, Fps(27.10f), Fps(30.0f), PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
}
class LayerHistoryTestParameterized : public LayerHistoryTest,
@@ -740,7 +776,7 @@
bool max = false;
bool min = false;
- Fps heuristic{0.0};
+ Fps heuristic;
for (const auto& layer : summarizeLayerHistory(time)) {
if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
heuristic = layer.desiredRefreshRate;
@@ -752,7 +788,7 @@
}
if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
- EXPECT_TRUE(Fps(24.0f).equalsWithMargin(heuristic));
+ EXPECT_EQ(24_Hz, heuristic);
EXPECT_FALSE(max);
if (summarizeLayerHistory(time).size() == 2) {
EXPECT_TRUE(min);
@@ -766,8 +802,7 @@
::testing::Values(1s, 2s, 3s, 4s, 5s));
} // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file
+#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index d6ce5e2..5c2d2e1 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -19,7 +19,9 @@
#include <gtest/gtest.h>
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
+#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
@@ -47,7 +49,7 @@
TEST_F(LayerInfoTest, prefersPresentTime) {
std::deque<FrameTimeData> frameTimes;
- constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedFps = 50_Hz;
constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
constexpr int kNumFrames = 10;
for (int i = 1; i <= kNumFrames; i++) {
@@ -58,14 +60,12 @@
setFrameTimes(frameTimes);
const auto averageFrameTime = calculateAverageFrameTime();
ASSERT_TRUE(averageFrameTime.has_value());
- const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
- ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
- << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+ ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
}
TEST_F(LayerInfoTest, fallbacksToQueueTimeIfNoPresentTime) {
std::deque<FrameTimeData> frameTimes;
- constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedFps = 50_Hz;
constexpr auto kPeriod = kExpectedFps.getPeriodNsecs();
constexpr int kNumFrames = 10;
for (int i = 1; i <= kNumFrames; i++) {
@@ -74,17 +74,15 @@
.pendingModeChange = false});
}
setFrameTimes(frameTimes);
- setLastRefreshRate(Fps(20.0f)); // Set to some valid value
+ setLastRefreshRate(20_Hz); // Set to some valid value.
const auto averageFrameTime = calculateAverageFrameTime();
ASSERT_TRUE(averageFrameTime.has_value());
- const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
- ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
- << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+ ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
}
TEST_F(LayerInfoTest, returnsNulloptIfThereWasConfigChange) {
std::deque<FrameTimeData> frameTimesWithoutConfigChange;
- const auto period = Fps(50.0f).getPeriodNsecs();
+ const auto period = (50_Hz).getPeriodNsecs();
constexpr int kNumFrames = 10;
for (int i = 1; i <= kNumFrames; i++) {
frameTimesWithoutConfigChange.push_back(FrameTimeData{.presentTime = period * i,
@@ -124,9 +122,9 @@
// Make sure that this doesn't influence the calculated average FPS.
TEST_F(LayerInfoTest, ignoresSmallPeriods) {
std::deque<FrameTimeData> frameTimes;
- constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedFps = 50_Hz;
constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
- constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs();
+ constexpr auto kSmallPeriod = (250_Hz).getPeriodNsecs();
constexpr int kNumIterations = 10;
for (int i = 1; i <= kNumIterations; i++) {
frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i,
@@ -141,18 +139,16 @@
setFrameTimes(frameTimes);
const auto averageFrameTime = calculateAverageFrameTime();
ASSERT_TRUE(averageFrameTime.has_value());
- const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
- ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
- << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+ ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
}
// There may be a big period of time between two frames. Make sure that
// this doesn't influence the calculated average FPS.
TEST_F(LayerInfoTest, ignoresLargePeriods) {
std::deque<FrameTimeData> frameTimes;
- constexpr auto kExpectedFps = Fps(50.0f);
+ constexpr auto kExpectedFps = 50_Hz;
constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs();
- constexpr auto kLargePeriod = Fps(9.0f).getPeriodNsecs();
+ constexpr auto kLargePeriod = (9_Hz).getPeriodNsecs();
auto record = [&](nsecs_t time) {
frameTimes.push_back(
@@ -172,9 +168,7 @@
setFrameTimes(frameTimes);
const auto averageFrameTime = calculateAverageFrameTime();
ASSERT_TRUE(averageFrameTime.has_value());
- const auto averageFps = Fps::fromPeriodNsecs(*averageFrameTime);
- ASSERT_TRUE(kExpectedFps.equalsWithMargin(averageFps))
- << "Expected " << averageFps << " to be equal to " << kExpectedFps;
+ ASSERT_EQ(kExpectedFps, Fps::fromPeriodNsecs(*averageFrameTime));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index dbd51fe..bd4dc59 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -31,62 +31,51 @@
using CallbackToken = scheduler::VSyncDispatch::CallbackToken;
+struct NoOpCompositor final : ICompositor {
+ bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
+ void composite(nsecs_t) override {}
+ void sample() override {}
+} gNoOpCompositor;
+
class TestableMessageQueue : public impl::MessageQueue {
-public:
- class MockHandler : public MessageQueue::Handler {
- public:
- explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {}
- ~MockHandler() override = default;
- MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp));
+ struct MockHandler : MessageQueue::Handler {
+ using MessageQueue::Handler::Handler;
+
+ MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override));
};
- TestableMessageQueue() = default;
- ~TestableMessageQueue() override = default;
+ explicit TestableMessageQueue(sp<MockHandler> handler)
+ : impl::MessageQueue(gNoOpCompositor, handler), mHandler(std::move(handler)) {}
- void initHandler(const sp<MockHandler>& handler) { mHandler = handler; }
+public:
+ TestableMessageQueue() : TestableMessageQueue(sp<MockHandler>::make(*this)) {}
- void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
- vsyncCallback(vsyncTime, targetWakeupTime, readyTime);
- }
+ using impl::MessageQueue::vsyncCallback;
+
+ const sp<MockHandler> mHandler;
};
-class MockVSyncDispatch : public scheduler::VSyncDispatch {
-public:
- MockVSyncDispatch() = default;
- ~MockVSyncDispatch() override = default;
-
+struct MockVSyncDispatch : scheduler::VSyncDispatch {
MOCK_METHOD2(registerCallback,
- CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string));
+ CallbackToken(const std::function<void(nsecs_t, nsecs_t, nsecs_t)>&, std::string));
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming));
MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
-class MockTokenManager : public frametimeline::TokenManager {
-public:
- MockTokenManager() = default;
- ~MockTokenManager() override = default;
-
+struct MockTokenManager : frametimeline::TokenManager {
MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction));
MOCK_CONST_METHOD1(getPredictionsForToken, std::optional<frametimeline::TimelineItem>(int64_t));
};
-class MessageQueueTest : public testing::Test {
-public:
- MessageQueueTest() = default;
- ~MessageQueueTest() override = default;
-
+struct MessageQueueTest : testing::Test {
void SetUp() override {
- EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler));
-
EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken));
EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration));
EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1);
}
- sp<TestableMessageQueue::MockHandler> mHandler =
- new TestableMessageQueue::MockHandler(mEventQueue);
MockVSyncDispatch mVSyncDispatch;
MockTokenManager mTokenManager;
TestableMessageQueue mEventQueue;
@@ -100,45 +89,49 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(MessageQueueTest, invalidate) {
+TEST_F(MessageQueueTest, commit) {
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
- EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
- EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
- EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+ ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+ EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
}
-TEST_F(MessageQueueTest, invalidateTwice) {
+TEST_F(MessageQueueTest, commitTwice) {
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
- EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
- EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+ ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+ EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
- EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
- EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+ ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+ EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
}
-TEST_F(MessageQueueTest, invalidateTwiceWithCallback) {
+TEST_F(MessageQueueTest, commitTwiceWithCallback) {
InSequence s;
const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
.readyDuration = 0,
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
- EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value());
- EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
+
+ ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
+ EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
const auto startTime = 100;
const auto endTime = startTime + mDuration.count();
@@ -148,10 +141,10 @@
generateTokenForPredictions(
frametimeline::TimelineItem(startTime, endTime, presentTime)))
.WillOnce(Return(vsyncId));
- EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1);
- EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime));
+ EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1);
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime));
- EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value());
+ EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
const auto timingAfterCallback =
scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(),
@@ -159,10 +152,10 @@
.earliestVsync = presentTime};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
-TEST_F(MessageQueueTest, invalidateWithDurationChange) {
+TEST_F(MessageQueueTest, commitWithDurationChange) {
EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration));
const auto timing =
@@ -171,7 +164,7 @@
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index 6916764..597e5e7 100644
--- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -72,7 +72,8 @@
mIdleTimer->stop();
}
-TEST_F(OneShotTimerTest, resetTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -94,7 +95,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, resetBackToBackTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_resetBackToBackTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -144,7 +146,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_idleTimerIdlesTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
@@ -169,7 +172,8 @@
EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value());
}
-TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+// TODO(b/186417847) This test is flaky. Reenable once fixed.
+TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) {
fake::FakeClock* clock = new fake::FakeClock();
mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms,
mResetTimerCallback.getInvocable(),
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index c1dba2b..98746bc 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,21 +21,18 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
+#include <ftl/enum.h>
#include <gmock/gmock.h>
#include <log/log.h>
-#include <thread>
-
#include <ui/Size.h>
-#include "../../Scheduler/RefreshRateConfigs.h"
#include "DisplayHardware/HWC2.h"
+#include "FpsOps.h"
#include "Scheduler/RefreshRateConfigs.h"
using namespace std::chrono_literals;
-namespace android {
-
-namespace scheduler {
+namespace android::scheduler {
namespace hal = android::hardware::graphics::composer::hal;
@@ -98,27 +95,38 @@
static inline const DisplayModeId HWC_CONFIG_ID_30 = DisplayModeId(4);
static inline const DisplayModeId HWC_CONFIG_ID_25 = DisplayModeId(5);
static inline const DisplayModeId HWC_CONFIG_ID_50 = DisplayModeId(6);
+ static inline const DisplayModeId HWC_CONFIG_ID_24 = DisplayModeId(7);
+ static inline const DisplayModeId HWC_CONFIG_ID_24_FRAC = DisplayModeId(8);
+ static inline const DisplayModeId HWC_CONFIG_ID_30_FRAC = DisplayModeId(9);
+ static inline const DisplayModeId HWC_CONFIG_ID_60_FRAC = DisplayModeId(10);
// Test configs
- DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, Fps(60.0f).getPeriodNsecs());
- DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs());
+ DisplayModePtr mConfig60 = createDisplayMode(HWC_CONFIG_ID_60, 0, (60_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig60Frac =
+ createDisplayMode(HWC_CONFIG_ID_60_FRAC, 0, (59.94_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig90 = createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs());
DisplayModePtr mConfig90DifferentGroup =
- createDisplayMode(HWC_CONFIG_ID_90, 1, Fps(90.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_90, 1, (90_Hz).getPeriodNsecs());
DisplayModePtr mConfig90DifferentResolution =
- createDisplayMode(HWC_CONFIG_ID_90, 0, Fps(90.0f).getPeriodNsecs(), ui::Size(111, 222));
- DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, Fps(72.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_90, 0, (90_Hz).getPeriodNsecs(), ui::Size(111, 222));
+ DisplayModePtr mConfig72 = createDisplayMode(HWC_CONFIG_ID_72, 0, (72_Hz).getPeriodNsecs());
DisplayModePtr mConfig72DifferentGroup =
- createDisplayMode(HWC_CONFIG_ID_72, 1, Fps(72.0f).getPeriodNsecs());
- DisplayModePtr mConfig120 =
- createDisplayMode(HWC_CONFIG_ID_120, 0, Fps(120.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_72, 1, (72_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig120 = createDisplayMode(HWC_CONFIG_ID_120, 0, (120_Hz).getPeriodNsecs());
DisplayModePtr mConfig120DifferentGroup =
- createDisplayMode(HWC_CONFIG_ID_120, 1, Fps(120.0f).getPeriodNsecs());
- DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, Fps(30.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_120, 1, (120_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig30 = createDisplayMode(HWC_CONFIG_ID_30, 0, (30_Hz).getPeriodNsecs());
DisplayModePtr mConfig30DifferentGroup =
- createDisplayMode(HWC_CONFIG_ID_30, 1, Fps(30.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_30, 1, (30_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig30Frac =
+ createDisplayMode(HWC_CONFIG_ID_30_FRAC, 0, (29.97_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig25 = createDisplayMode(HWC_CONFIG_ID_25, 0, (25_Hz).getPeriodNsecs());
DisplayModePtr mConfig25DifferentGroup =
- createDisplayMode(HWC_CONFIG_ID_25, 1, Fps(25.0f).getPeriodNsecs());
- DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, Fps(50.0f).getPeriodNsecs());
+ createDisplayMode(HWC_CONFIG_ID_25, 1, (25_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig50 = createDisplayMode(HWC_CONFIG_ID_50, 0, (50_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig24 = createDisplayMode(HWC_CONFIG_ID_24, 0, (24_Hz).getPeriodNsecs());
+ DisplayModePtr mConfig24Frac =
+ createDisplayMode(HWC_CONFIG_ID_24_FRAC, 0, (23.976_Hz).getPeriodNsecs());
// Test device configurations
// The positions of the configs in the arrays below MUST match their IDs. For example,
@@ -145,6 +153,11 @@
mConfig50};
DisplayModes m60_120Device = {mConfig60, mConfig120};
+ // This is a typical TV configuration.
+ DisplayModes m24_25_30_50_60WithFracDevice = {mConfig24, mConfig24Frac, mConfig25,
+ mConfig30, mConfig30Frac, mConfig50,
+ mConfig60, mConfig60Frac};
+
// Expected RefreshRate objects
RefreshRate mExpected60Config = {mConfig60, RefreshRate::ConstructorTag(0)};
RefreshRate mExpectedAlmost60Config = {createDisplayMode(HWC_CONFIG_ID_60, 0, 16666665),
@@ -158,7 +171,6 @@
RefreshRate mExpected30Config = {mConfig30, RefreshRate::ConstructorTag(0)};
RefreshRate mExpected120Config = {mConfig120, RefreshRate::ConstructorTag(0)};
-private:
DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod,
ui::Size resolution = ui::Size());
};
@@ -181,7 +193,7 @@
int64_t vsyncPeriod, ui::Size resolution) {
return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
.setId(modeId)
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(int32_t(vsyncPeriod))
.setGroup(group)
.setHeight(resolution.height)
@@ -190,9 +202,7 @@
}
namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
@@ -203,10 +213,8 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {Fps(60), Fps(60)}}),
- 0);
- ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(20), Fps(40)}}),
- 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(10), {60_Hz, 60_Hz}}), 0);
+ ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20_Hz, 40_Hz}}), 0);
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
@@ -240,8 +248,7 @@
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
@@ -266,8 +273,7 @@
ASSERT_EQ(mExpected60Config, minRate60);
ASSERT_EQ(mExpected60Config, performanceRate60);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(60), Fps(90)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60_Hz, 90_Hz}}), 0);
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs);
@@ -289,8 +295,7 @@
ASSERT_EQ(mExpected60Config, minRate);
ASSERT_EQ(mExpected90Config, performanceRate);
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs);
auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -313,8 +318,7 @@
EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
}
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
{
auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.getModeId(), HWC_CONFIG_ID_90);
@@ -328,14 +332,19 @@
// If there are no layers we select the default frame rate, which is the max of the primary
// range.
- auto layers = std::vector<LayerRequirement>{};
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate({}, {}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}),
+ NO_ERROR);
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate({}, {}));
+
+ // We select max even when this will cause a non-seamless switch.
+ refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_EQ(refreshRateConfigs->setDisplayManagerPolicy(
+ {HWC_CONFIG_ID_90, /*allowGroupSwitching*/ true, {0_Hz, 90_Hz}}),
+ NO_ERROR);
+ EXPECT_EQ(mExpected90DifferentGroupConfig, refreshRateConfigs->getBestRefreshRate({}, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
@@ -343,142 +352,109 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
+ lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
+ lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
+ lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.name = "";
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_90, {Fps(90.0f), Fps(90.0f)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {Fps(0.0f), Fps(120.0f)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 120_Hz}}), 0);
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_multipleThreshold_60_90) {
@@ -487,44 +463,37 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
+ lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
+ lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
+ lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
@@ -532,37 +501,30 @@
std::make_unique<RefreshRateConfigs>(m60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
@@ -570,31 +532,27 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(48.0f);
+ lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(48.0f);
+ lr2.desiredRefreshRate = 48_Hz;
lr2.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
@@ -602,91 +560,81 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr1.name = "24Hz Heuristic";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_multipleThreshold) {
@@ -695,91 +643,81 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::Heuristic;
lr1.name = "24Hz Heuristic";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.name = "24Hz ExplicitExactOrMultiple";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr1.desiredRefreshRate = Fps(24.0f);
+ lr1.desiredRefreshRate = 24_Hz;
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.name = "24Hz ExplicitDefault";
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
@@ -787,37 +725,30 @@
std::make_unique<RefreshRateConfigs>(m30_60Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(30.0f);
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 30_Hz;
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
@@ -825,60 +756,47 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::Min;
lr.name = "Min";
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- lr.desiredRefreshRate = Fps(45.0f);
+ lr.desiredRefreshRate = 45_Hz;
lr.name = "45Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- lr.desiredRefreshRate = Fps(30.0f);
+ lr.desiredRefreshRate = 30_Hz;
lr.name = "30Hz Heuristic";
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- lr.desiredRefreshRate = Fps(24.0f);
+ lr.desiredRefreshRate = 24_Hz;
lr.name = "24Hz Heuristic";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- lr.desiredRefreshRate = Fps(24.0f);
+ lr.desiredRefreshRate = 24_Hz;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
@@ -886,53 +804,45 @@
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(24.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 24_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = Fps(15.0f);
+ lr1.desiredRefreshRate = 15_Hz;
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = Fps(30.0f);
+ lr1.desiredRefreshRate = 30_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(45.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 45_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
@@ -940,15 +850,15 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps(fps);
- const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+ EXPECT_EQ(mExpected60Config, refreshRate)
+ << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
}
}
@@ -958,15 +868,15 @@
std::make_unique<RefreshRateConfigs>(m60_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps(fps);
- const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+ EXPECT_EQ(mExpected60Config, refreshRate)
+ << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
}
}
@@ -975,39 +885,35 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(90.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 90_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = Fps(90.0f);
+ lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::Heuristic;
- lr1.desiredRefreshRate = Fps(90.0f);
+ lr1.desiredRefreshRate = 90_Hz;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr2.desiredRefreshRate = Fps(60.0f);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ lr2.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
- ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(60.000004f), Fps(60.000004f)));
- ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(Fps(59.0f), Fps(60.1f)));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(75.0f), Fps(90.0f)));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(60.0011f), Fps(90.0f)));
- ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(Fps(50.0f), Fps(59.998f)));
+ ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004_Hz, 60.000004_Hz));
+ ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59_Hz, 60.1_Hz));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75_Hz, 90_Hz));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011_Hz, 90_Hz));
+ ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50_Hz, 59.998_Hz));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
@@ -1015,15 +921,15 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
- lr.desiredRefreshRate = Fps(fps);
- const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+ lr.desiredRefreshRate = Fps::fromValue(fps);
+ const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+ EXPECT_EQ(mExpected90Config, refreshRate)
+ << lr.desiredRefreshRate << " chooses " << refreshRate.getName();
}
}
@@ -1032,53 +938,47 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::ExplicitDefault;
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz ExplicitDefault";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(30.0f);
+ lr1.desiredRefreshRate = 30_Hz;
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(30.0f);
+ lr1.desiredRefreshRate = 30_Hz;
lr1.name = "30Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
@@ -1086,52 +986,46 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(90.0f);
+ lr2.desiredRefreshRate = 90_Hz;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, touchConsidered) {
@@ -1140,55 +1034,50 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals);
EXPECT_EQ(false, consideredSignals.touch);
- refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true}, &consideredSignals);
EXPECT_EQ(true, consideredSignals.touch);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
- &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
- &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
EXPECT_EQ(false, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
- &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
EXPECT_EQ(true, consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitDefault;
- lr1.desiredRefreshRate = Fps(60.0f);
+ lr1.desiredRefreshRate = 60_Hz;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Heuristic;
- lr2.desiredRefreshRate = Fps(60.0f);
+ lr2.desiredRefreshRate = 60_Hz;
lr2.name = "60Hz Heuristic";
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
- &consideredSignals);
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}, &consideredSignals);
EXPECT_EQ(false, consideredSignals.touch);
}
@@ -1197,40 +1086,128 @@
std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
// Prepare a table with the vote and the expected refresh rate
- const std::vector<std::pair<float, float>> testCases = {
- {130, 120}, {120, 120}, {119, 120}, {110, 120},
+ const std::initializer_list<std::pair<Fps, Fps>> testCases = {
+ {130_Hz, 120_Hz}, {120_Hz, 120_Hz}, {119_Hz, 120_Hz}, {110_Hz, 120_Hz},
- {100, 90}, {90, 90}, {89, 90},
+ {100_Hz, 90_Hz}, {90_Hz, 90_Hz}, {89_Hz, 90_Hz},
- {80, 72}, {73, 72}, {72, 72}, {71, 72}, {70, 72},
+ {80_Hz, 72_Hz}, {73_Hz, 72_Hz}, {72_Hz, 72_Hz}, {71_Hz, 72_Hz}, {70_Hz, 72_Hz},
- {65, 60}, {60, 60}, {59, 60}, {58, 60},
+ {65_Hz, 60_Hz}, {60_Hz, 60_Hz}, {59_Hz, 60_Hz}, {58_Hz, 60_Hz},
- {55, 90}, {50, 90}, {45, 90},
+ {55_Hz, 90_Hz}, {50_Hz, 90_Hz}, {45_Hz, 90_Hz},
- {42, 120}, {40, 120}, {39, 120},
+ {42_Hz, 120_Hz}, {40_Hz, 120_Hz}, {39_Hz, 120_Hz},
- {37, 72}, {36, 72}, {35, 72},
+ {37_Hz, 72_Hz}, {36_Hz, 72_Hz}, {35_Hz, 72_Hz},
- {30, 60},
+ {30_Hz, 60_Hz},
};
- for (const auto& test : testCases) {
+ for (auto [desired, expected] : testCases) {
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = Fps(test.first);
+ lr.desiredRefreshRate = desired;
std::stringstream ss;
- ss << "ExplicitDefault " << test.first << " fps";
+ ss << "ExplicitDefault " << desired;
lr.name = ss.str();
- const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_TRUE(refreshRate.getFps().equalsWithMargin(Fps(test.second)))
- << "Expecting " << test.first << "fps => " << test.second << "Hz";
+ const auto refreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+ EXPECT_EQ(refreshRate.getFps(), expected);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest,
+ getBestRefreshRate_ExplicitExactOrMultiple_WithFractionalRefreshRates) {
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ // Test that 23.976 will choose 24 if 23.976 is not supported
+ {
+ android::DisplayModes modes = {mConfig24, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr.desiredRefreshRate = 23.976_Hz;
+ lr.name = "ExplicitExactOrMultiple 23.976 Hz";
+ EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+ }
+
+ // Test that 24 will choose 23.976 if 24 is not supported
+ {
+ android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "ExplicitExactOrMultiple 24 Hz";
+ EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+ }
+
+ // Test that 29.97 will prefer 59.94 over 60 and 30
+ {
+ android::DisplayModes modes = {mConfig24, mConfig24Frac, mConfig25,
+ mConfig30, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = 29.97_Hz;
+ lr.name = "ExplicitExactOrMultiple 29.97 Hz";
+ EXPECT_EQ(HWC_CONFIG_ID_60_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExact_WithFractionalRefreshRates) {
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ auto& lr = layers[0];
+
+ // Test that voting for supported refresh rate will select this refresh rate
+ {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ for (auto desired : {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz}) {
+ lr.vote = LayerVoteType::ExplicitExact;
+ lr.desiredRefreshRate = desired;
+ std::stringstream ss;
+ ss << "ExplicitExact " << desired;
+ lr.name = ss.str();
+
+ auto selectedRefreshRate = refreshRateConfigs->getBestRefreshRate(layers, {});
+ EXPECT_EQ(selectedRefreshRate.getFps(), lr.desiredRefreshRate);
+ }
+ }
+
+ // Test that 23.976 will choose 24 if 23.976 is not supported
+ {
+ android::DisplayModes modes = {mConfig24, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.vote = LayerVoteType::ExplicitExact;
+ lr.desiredRefreshRate = 23.976_Hz;
+ lr.name = "ExplicitExact 23.976 Hz";
+ EXPECT_EQ(HWC_CONFIG_ID_24, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
+ }
+
+ // Test that 24 will choose 23.976 if 24 is not supported
+ {
+ android::DisplayModes modes = {mConfig24Frac, mConfig25, mConfig30,
+ mConfig30Frac, mConfig60, mConfig60Frac};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(modes, /*currentConfigId=*/HWC_CONFIG_ID_60);
+ lr.desiredRefreshRate = 24_Hz;
+ lr.name = "ExplicitExact 24 Hz";
+ EXPECT_EQ(HWC_CONFIG_ID_24_FRAC,
+ refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
}
@@ -1241,15 +1218,15 @@
/*currentConfigId=*/HWC_CONFIG_ID_90);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+ {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
0);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
RefreshRateConfigs::GlobalSignals consideredSignals;
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
EXPECT_EQ(mExpected60Config,
@@ -1265,18 +1242,17 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(90.f)}}),
+ {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 90_Hz}}),
0);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = Fps(90.0f);
+ lr.desiredRefreshRate = 90_Hz;
lr.name = "90Hz ExplicitDefault";
lr.focused = true;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {.idle = true}));
}
TEST_F(RefreshRateConfigsTest,
@@ -1286,72 +1262,61 @@
/*currentConfigId=*/HWC_CONFIG_ID_90);
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_90, {Fps(90.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+ {HWC_CONFIG_ID_90, {90_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
0);
RefreshRateConfigs::GlobalSignals consideredSignals;
EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
- &consideredSignals));
+ refreshRateConfigs->getBestRefreshRate({}, {}, &consideredSignals));
EXPECT_EQ(false, consideredSignals.touch);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& lr = layers[0];
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitExactOrMultiple";
lr.focused = false;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.focused = true;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::ExplicitDefault;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz ExplicitDefault";
lr.focused = false;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.focused = true;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Heuristic;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Heuristic";
lr.focused = false;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.focused = true;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Max;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Max";
lr.focused = false;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.focused = true;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.vote = LayerVoteType::Min;
- lr.desiredRefreshRate = Fps(60.0f);
+ lr.desiredRefreshRate = 60_Hz;
lr.name = "60Hz Min";
lr.focused = false;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
lr.focused = true;
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
@@ -1361,17 +1326,15 @@
// The default policy doesn't allow group switching. Verify that no
// group switches are performed.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = Fps(90.0f);
+ layer.desiredRefreshRate = 90_Hz;
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
@@ -1383,16 +1346,14 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = Fps(90.0f);
+ layer.desiredRefreshRate = 90_Hz;
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
@@ -1405,16 +1366,14 @@
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
// Verify that we won't change the group if seamless switch is required.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = Fps(90.0f);
+ layer.desiredRefreshRate = 90_Hz;
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "90Hz ExplicitDefault";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
@@ -1429,16 +1388,14 @@
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// Verify that we won't do a seamless switch if we request the same mode as the default
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = Fps(60.0f);
+ layer.desiredRefreshRate = 60_Hz;
layer.seamlessness = Seamlessness::OnlySeamless;
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
@@ -1455,17 +1412,15 @@
// Verify that if the current config is in another group and there are no layers with
// seamlessness=SeamedAndSeamless we'll go back to the default group.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
- layer.desiredRefreshRate = Fps(60.0f);
+ layer.desiredRefreshRate = 60_Hz;
layer.seamlessness = Seamlessness::Default;
layer.name = "60Hz ExplicitDefault";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
@@ -1481,9 +1436,9 @@
// If there's a layer with seamlessness=SeamedAndSeamless, another layer with
// seamlessness=OnlySeamless can't change the mode group.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].vote = LayerVoteType::ExplicitDefault;
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].seamlessness = Seamlessness::OnlySeamless;
layers[0].name = "60Hz ExplicitDefault";
layers[0].focused = true;
@@ -1491,13 +1446,11 @@
layers.push_back(LayerRequirement{.weight = 0.5f});
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].desiredRefreshRate = 90_Hz;
layers[1].name = "90Hz ExplicitDefault";
layers[1].focused = false;
- ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
@@ -1517,23 +1470,21 @@
// For example, this may happen when a video playback requests and gets a seamed switch,
// but another layer (with default seamlessness) starts animating. The animating layer
// should not cause a seamed switch.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].seamlessness = Seamlessness::Default;
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].focused = true;
layers[0].vote = LayerVoteType::ExplicitDefault;
layers[0].name = "60Hz ExplicitDefault";
layers.push_back(LayerRequirement{.weight = 0.1f});
layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].desiredRefreshRate = 90_Hz;
layers[1].focused = true;
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
@@ -1550,23 +1501,21 @@
// Layer with seamlessness=Default can change the mode group if there's a not
// focused layer with seamlessness=SeamedAndSeamless. This happens for example,
// when in split screen mode the user switches between the two visible applications.
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].seamlessness = Seamlessness::Default;
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].focused = true;
layers[0].vote = LayerVoteType::ExplicitDefault;
layers[0].name = "60Hz ExplicitDefault";
layers.push_back(LayerRequirement{.weight = 0.7f});
layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
- layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].desiredRefreshRate = 90_Hz;
layers[1].focused = false;
layers[1].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "90Hz ExplicitDefault";
- ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
@@ -1580,22 +1529,18 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitExactOrMultiple;
- layer.desiredRefreshRate = Fps(60.0f);
+ layer.desiredRefreshRate = 60_Hz;
layer.seamlessness = Seamlessness::SeamedAndSeamless;
layer.name = "60Hz ExplicitExactOrMultiple";
layer.focused = true;
- ASSERT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_120);
- ASSERT_EQ(HWC_CONFIG_ID_120,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_120, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) {
@@ -1609,31 +1554,27 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
- auto layers = std::vector<
- LayerRequirement>{LayerRequirement{.name = "60Hz ExplicitDefault",
- .vote = LayerVoteType::ExplicitDefault,
- .desiredRefreshRate = Fps(60.0f),
- .seamlessness = Seamlessness::SeamedAndSeamless,
- .weight = 0.5f,
- .focused = false},
- LayerRequirement{.name = "25Hz ExplicitExactOrMultiple",
- .vote = LayerVoteType::ExplicitExactOrMultiple,
- .desiredRefreshRate = Fps(25.0f),
- .seamlessness = Seamlessness::OnlySeamless,
- .weight = 1.0f,
- .focused = true}};
+ std::vector<LayerRequirement> layers = {{.name = "60Hz ExplicitDefault",
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = 60_Hz,
+ .seamlessness = Seamlessness::SeamedAndSeamless,
+ .weight = 0.5f,
+ .focused = false},
+ {.name = "25Hz ExplicitExactOrMultiple",
+ .vote = LayerVoteType::ExplicitExactOrMultiple,
+ .desiredRefreshRate = 25_Hz,
+ .seamlessness = Seamlessness::OnlySeamless,
+ .weight = 1.f,
+ .focused = true}};
- ASSERT_EQ(HWC_CONFIG_ID_50,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_50, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
auto& seamedLayer = layers[0];
- seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f);
+ seamedLayer.desiredRefreshRate = 30_Hz;
+ seamedLayer.name = "30Hz ExplicitDefault";
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30);
- ASSERT_EQ(HWC_CONFIG_ID_25,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_25, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) {
@@ -1647,14 +1588,10 @@
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.name = "Min",
- .vote = LayerVoteType::Min,
- .weight = 1.f,
- .focused = true}};
+ std::vector<LayerRequirement> layers = {
+ {.name = "Min", .vote = LayerVoteType::Min, .weight = 1.f, .focused = true}};
- ASSERT_EQ(HWC_CONFIG_ID_90,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
- .getModeId());
+ ASSERT_EQ(HWC_CONFIG_ID_90, refreshRateConfigs->getBestRefreshRate(layers, {}).getModeId());
}
TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
@@ -1662,59 +1599,58 @@
std::make_unique<RefreshRateConfigs>(m30_60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
+ struct Args {
+ bool touch = false;
+ bool focused = true;
+ };
+
// Return the config ID from calling getBestRefreshRate() for a single layer with the
// given voteType and fps.
- auto getFrameRate = [&](LayerVoteType voteType, Fps fps, bool touchActive = false,
- bool focused = true) -> DisplayModeId {
+ auto getFrameRate = [&](LayerVoteType voteType, Fps fps, Args args = {}) -> DisplayModeId {
layers[0].vote = voteType;
layers[0].desiredRefreshRate = fps;
- layers[0].focused = focused;
- return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
- .getModeId();
+ layers[0].focused = args.focused;
+ return refreshRateConfigs->getBestRefreshRate(layers, {.touch = args.touch}).getModeId();
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {Fps(30.f), Fps(60.f)}, {Fps(30.f), Fps(90.f)}}),
+ {HWC_CONFIG_ID_60, {30_Hz, 60_Hz}, {30_Hz, 90_Hz}}),
0);
- EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
- .getModeId());
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, refreshRateConfigs->getBestRefreshRate({}, {}).getModeId());
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_30, getFrameRate(LayerVoteType::Min, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
- // Layers not focused are not allowed to override primary config
+ // Unfocused layers are not allowed to override primary config.
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/false,
- /*focused=*/false));
+ getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.focused = false}));
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/false,
- /*focused=*/false));
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.focused = false}));
// Touch boost should be restricted to the primary range.
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f), /*touch=*/true));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz, {.touch = true}));
// When we're higher than the primary range max due to a layer frame rate setting, touch boost
// shouldn't drag us back down to the primary range max.
EXPECT_EQ(HWC_CONFIG_ID_90,
- getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f), /*touch=*/true));
+ getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz, {.touch = true}));
EXPECT_EQ(HWC_CONFIG_ID_60,
- getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f), /*touch=*/true));
+ getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz, {.touch = true}));
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {Fps(60.f), Fps(60.f)}, {Fps(60.f), Fps(60.f)}}),
+ {HWC_CONFIG_ID_60, {60_Hz, 60_Hz}, {60_Hz, 60_Hz}}),
0);
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, Fps(90.f)));
- EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, Fps(90.f)));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90_Hz));
+ EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90_Hz));
}
TEST_F(RefreshRateConfigsTest, idle) {
@@ -1722,12 +1658,12 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
const auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> DisplayModeId {
layers[0].vote = voteType;
- layers[0].desiredRefreshRate = Fps(90.f);
+ layers[0].desiredRefreshRate = 90_Hz;
RefreshRateConfigs::GlobalSignals consideredSignals;
const auto configId =
refreshRateConfigs
@@ -1740,7 +1676,7 @@
};
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_60, {Fps(60.f), Fps(90.f)}, {Fps(60.f), Fps(90.f)}}),
+ {HWC_CONFIG_ID_60, {60_Hz, 90_Hz}, {60_Hz, 90_Hz}}),
0);
// Idle should be lower priority than touch boost.
@@ -1771,8 +1707,7 @@
// Idle should be applied rather than the current config when there are no layers.
EXPECT_EQ(HWC_CONFIG_ID_60,
- refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
- .getModeId());
+ refreshRateConfigs->getBestRefreshRate({}, {.idle = true}).getModeId());
}
TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
@@ -1781,23 +1716,23 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
- const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, Fps(fps));
+ const auto knownFrameRate =
+ findClosestKnownFrameRate(*refreshRateConfigs, Fps::fromValue(fps));
Fps expectedFrameRate;
if (fps < 26.91f) {
- expectedFrameRate = Fps(24.0f);
+ expectedFrameRate = 24_Hz;
} else if (fps < 37.51f) {
- expectedFrameRate = Fps(30.0f);
+ expectedFrameRate = 30_Hz;
} else if (fps < 52.51f) {
- expectedFrameRate = Fps(45.0f);
+ expectedFrameRate = 45_Hz;
} else if (fps < 66.01f) {
- expectedFrameRate = Fps(60.0f);
+ expectedFrameRate = 60_Hz;
} else if (fps < 81.01f) {
- expectedFrameRate = Fps(72.0f);
+ expectedFrameRate = 72_Hz;
} else {
- expectedFrameRate = Fps(90.0f);
+ expectedFrameRate = 90_Hz;
}
- EXPECT_TRUE(expectedFrameRate.equalsWithMargin(knownFrameRate))
- << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+ EXPECT_EQ(expectedFrameRate, knownFrameRate);
}
}
@@ -1806,38 +1741,32 @@
std::make_unique<RefreshRateConfigs>(m60_90Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- struct ExpectedRate {
- Fps rate;
- const RefreshRate& expected;
+ struct Expectation {
+ Fps fps;
+ const RefreshRate& refreshRate;
};
- /* clang-format off */
- std::vector<ExpectedRate> knownFrameRatesExpectations = {
- {Fps(24.0f), mExpected60Config},
- {Fps(30.0f), mExpected60Config},
- {Fps(45.0f), mExpected90Config},
- {Fps(60.0f), mExpected60Config},
- {Fps(72.0f), mExpected90Config},
- {Fps(90.0f), mExpected90Config},
+ const std::initializer_list<Expectation> knownFrameRatesExpectations = {
+ {24_Hz, mExpected60Config}, {30_Hz, mExpected60Config}, {45_Hz, mExpected90Config},
+ {60_Hz, mExpected60Config}, {72_Hz, mExpected90Config}, {90_Hz, mExpected90Config},
};
- /* clang-format on */
// Make sure the test tests all the known frame rate
const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
- const auto equal =
- std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
- knownFrameRatesExpectations.begin(),
- [](Fps a, const ExpectedRate& b) { return a.equalsWithMargin(b.rate); });
+ const bool equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+ knownFrameRatesExpectations.begin(),
+ [](Fps fps, const Expectation& expected) {
+ return isApproxEqual(fps, expected.fps);
+ });
EXPECT_TRUE(equal);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::Heuristic;
- for (const auto& expectedRate : knownFrameRatesExpectations) {
- layer.desiredRefreshRate = expectedRate.rate;
- const auto& refreshRate =
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
- EXPECT_EQ(expectedRate.expected, refreshRate);
+
+ for (const auto& [fps, refreshRate] : knownFrameRatesExpectations) {
+ layer.desiredRefreshRate = fps;
+ EXPECT_EQ(refreshRate, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
}
@@ -1846,40 +1775,33 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 0.5f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
auto& explicitExactOrMultipleLayer = layers[1];
explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
explicitExactLayer.vote = LayerVoteType::ExplicitExact;
explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = Fps(30);
+ explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected30Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected30Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
- explicitExactLayer.desiredRefreshRate = Fps(60);
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+ explicitExactLayer.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(72);
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 72_Hz;
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(90);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 90_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(120);
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 120_Hz;
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitExactEnableFrameRateOverride) {
@@ -1888,40 +1810,33 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 0.5f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
auto& explicitExactOrMultipleLayer = layers[1];
explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
explicitExactLayer.vote = LayerVoteType::ExplicitExact;
explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = Fps(30);
+ explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
- explicitExactOrMultipleLayer.desiredRefreshRate = Fps(120);
- explicitExactLayer.desiredRefreshRate = Fps(60);
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactOrMultipleLayer.desiredRefreshRate = 120_Hz;
+ explicitExactLayer.desiredRefreshRate = 60_Hz;
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(72);
- EXPECT_EQ(mExpected72Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 72_Hz;
+ EXPECT_EQ(mExpected72Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(90);
- EXPECT_EQ(mExpected90Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 90_Hz;
+ EXPECT_EQ(mExpected90Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
- explicitExactLayer.desiredRefreshRate = Fps(120);
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+ explicitExactLayer.desiredRefreshRate = 120_Hz;
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
}
TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ReadsCached) {
@@ -1932,26 +1847,20 @@
/*currentConfigId=*/HWC_CONFIG_ID_60);
setLastBestRefreshRateInvocation(*refreshRateConfigs,
- GetBestRefreshRateInvocation{.layerRequirements = std::vector<
- LayerRequirement>(),
- .globalSignals = {.touch = true,
+ GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
.idle = true},
.outSignalsConsidered =
- {.touch = true,
- .idle = false},
+ {.touch = true},
.resultingBestRefreshRate =
createRefreshRate(
mConfig90)});
EXPECT_EQ(createRefreshRate(mConfig90),
- refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
- {.touch = true, .idle = true}));
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true}));
- const GlobalSignals cachedSignalsConsidered{.touch = true, .idle = false};
+ const GlobalSignals cachedSignalsConsidered{.touch = true};
setLastBestRefreshRateInvocation(*refreshRateConfigs,
- GetBestRefreshRateInvocation{.layerRequirements = std::vector<
- LayerRequirement>(),
- .globalSignals = {.touch = true,
+ GetBestRefreshRateInvocation{.globalSignals = {.touch = true,
.idle = true},
.outSignalsConsidered =
cachedSignalsConsidered,
@@ -1961,8 +1870,7 @@
GlobalSignals signalsConsidered;
EXPECT_EQ(createRefreshRate(mConfig30),
- refreshRateConfigs->getBestRefreshRate(std::vector<LayerRequirement>(),
- {.touch = true, .idle = true},
+ refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true},
&signalsConsidered));
EXPECT_EQ(cachedSignalsConsidered, signalsConsidered);
@@ -1977,8 +1885,7 @@
ASSERT_FALSE(getLastBestRefreshRateInvocation(*refreshRateConfigs).has_value());
GlobalSignals globalSignals{.touch = true, .idle = true};
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 0.5f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
const auto lastResult =
refreshRateConfigs->getBestRefreshRate(layers, globalSignals,
/* outSignalsConsidered */ nullptr);
@@ -2002,30 +1909,81 @@
std::make_unique<RefreshRateConfigs>(m60_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_60, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
- LayerRequirement{.weight = 0.5f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
auto& explicitExactLayer = layers[0];
auto& explicitExactOrMultipleLayer = layers[1];
explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
- explicitExactOrMultipleLayer.desiredRefreshRate = Fps(60);
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
explicitExactLayer.vote = LayerVoteType::ExplicitExact;
explicitExactLayer.name = "ExplicitExact";
- explicitExactLayer.desiredRefreshRate = Fps(30);
+ explicitExactLayer.desiredRefreshRate = 30_Hz;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected120Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected120Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
explicitExactOrMultipleLayer.vote = LayerVoteType::NoVote;
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
- EXPECT_EQ(mExpected60Config,
- refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {.touch = true}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_FractionalRefreshRates_ExactAndDefault) {
+ RefreshRateConfigs::Config config = {.enableFrameRateOverride = true};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m24_25_30_50_60WithFracDevice,
+ /*currentConfigId=*/HWC_CONFIG_ID_60, config);
+
+ std::vector<LayerRequirement> layers = {{.weight = 0.5f}, {.weight = 0.5f}};
+ auto& explicitDefaultLayer = layers[0];
+ auto& explicitExactOrMultipleLayer = layers[1];
+
+ explicitExactOrMultipleLayer.vote = LayerVoteType::ExplicitExactOrMultiple;
+ explicitExactOrMultipleLayer.name = "ExplicitExactOrMultiple";
+ explicitExactOrMultipleLayer.desiredRefreshRate = 60_Hz;
+
+ explicitDefaultLayer.vote = LayerVoteType::ExplicitDefault;
+ explicitDefaultLayer.name = "ExplicitDefault";
+ explicitDefaultLayer.desiredRefreshRate = 59.94_Hz;
+
+ EXPECT_EQ(mExpected60Config, refreshRateConfigs->getBestRefreshRate(layers, {}));
+}
+
+// b/190578904
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_deviceWithCloseRefreshRates) {
+ constexpr int kMinRefreshRate = 10;
+ constexpr int kMaxRefreshRate = 240;
+
+ DisplayModes displayModes;
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ constexpr int32_t kGroup = 0;
+ const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+ displayModes.push_back(
+ createDisplayMode(DisplayModeId(fps), kGroup, refreshRate.getPeriodNsecs()));
+ }
+
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(displayModes,
+ /*currentConfigId=*/displayModes[0]->getId());
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ const auto testRefreshRate = [&](Fps fps, LayerVoteType vote) {
+ layers[0].desiredRefreshRate = fps;
+ layers[0].vote = vote;
+ EXPECT_EQ(fps.getIntValue(),
+ refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue())
+ << "Failed for " << ftl::enum_string(vote);
+ };
+
+ for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
+ const auto refreshRate = Fps::fromValue(static_cast<float>(fps));
+ testRefreshRate(refreshRate, LayerVoteType::Heuristic);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitDefault);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExactOrMultiple);
+ testRefreshRate(refreshRate, LayerVoteType::ExplicitExact);
+ }
}
TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
@@ -2044,18 +2002,15 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 90), current 60Hz => TurnOn.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(90)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 90_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 60), current 60Hz => TurnOff
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(90, 90), current 90Hz => TurnOff.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90_Hz, 90_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
@@ -2067,23 +2022,19 @@
std::make_unique<RefreshRateConfigs>(m60_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_120);
// SetPolicy(0, 60), current 60Hz => TurnOn.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(0), Fps(60)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0_Hz, 60_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 60), current 60Hz => TurnOff.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 60_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(60, 120), current 60Hz => TurnOn.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(120)}}),
- 0);
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60_Hz, 120_Hz}}), 0);
EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
// SetPolicy(120, 120), current 120Hz => TurnOff.
- ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
- {HWC_CONFIG_ID_120, {Fps(120), Fps(120)}}),
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_120, {120_Hz, 120_Hz}}),
0);
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
@@ -2093,7 +2044,7 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
/*currentConfigId=*/HWC_CONFIG_ID_30);
- const auto frameRate = Fps(30.f);
+ const auto frameRate = 30_Hz;
Fps displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
EXPECT_EQ(1, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, frameRate));
@@ -2115,12 +2066,38 @@
refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
- EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
+ EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, 22.5_Hz));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
- EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 25_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(24_Hz, 23.976_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(30_Hz, 29.97_Hz));
+ EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(60_Hz, 59.94_Hz));
+}
+
+TEST_F(RefreshRateConfigsTest, isFractionalPairOrMultiple) {
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(23.976_Hz, 24_Hz));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 23.976_Hz));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 30_Hz));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 29.97_Hz));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 60_Hz));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 59.94_Hz));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 60_Hz));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(60_Hz, 29.97_Hz));
+
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(59.94_Hz, 30_Hz));
+ EXPECT_TRUE(RefreshRateConfigs::isFractionalPairOrMultiple(30_Hz, 59.94_Hz));
+
+ const auto refreshRates = {23.976_Hz, 24_Hz, 25_Hz, 29.97_Hz, 30_Hz, 50_Hz, 59.94_Hz, 60_Hz};
+ for (auto refreshRate : refreshRates) {
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(refreshRate, refreshRate));
+ }
+
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(24_Hz, 25_Hz));
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(23.978_Hz, 25_Hz));
+ EXPECT_FALSE(RefreshRateConfigs::isFractionalPairOrMultiple(29.97_Hz, 59.94_Hz));
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
@@ -2128,9 +2105,7 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_120);
- auto layers = std::vector<LayerRequirement>{};
- ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false)
- .empty());
+ ASSERT_TRUE(refreshRateConfigs->getFrameRateOverrides({}, 120_Hz, {}).empty());
}
TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_60on120) {
@@ -2139,42 +2114,36 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_120, config);
- auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
layers[0].name = "Test layer";
layers[0].ownerUid = 1234;
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].vote = LayerVoteType::ExplicitDefault;
- auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::NoVote;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_TRUE(frameRateOverrides.empty());
layers[0].vote = LayerVoteType::Min;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_TRUE(frameRateOverrides.empty());
layers[0].vote = LayerVoteType::Max;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_TRUE(frameRateOverrides.empty());
layers[0].vote = LayerVoteType::Heuristic;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_TRUE(frameRateOverrides.empty());
}
@@ -2184,37 +2153,32 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_120, config);
- auto layers = std::vector<LayerRequirement>{
- LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
- LayerRequirement{.ownerUid = 5678, .weight = 1.0f},
- };
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f},
+ {.ownerUid = 5678, .weight = 1.f}};
layers[0].name = "Test layer 1234";
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].vote = LayerVoteType::ExplicitDefault;
layers[1].name = "Test layer 5678";
- layers[1].desiredRefreshRate = Fps(30.0f);
+ layers[1].desiredRefreshRate = 30_Hz;
layers[1].vote = LayerVoteType::ExplicitDefault;
- auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(2, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
ASSERT_EQ(1, frameRateOverrides.count(5678));
- ASSERT_EQ(30.0f, frameRateOverrides.at(5678).getValue());
+ ASSERT_EQ(30_Hz, frameRateOverrides.at(5678));
layers[1].vote = LayerVoteType::Heuristic;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
layers[1].ownerUid = 1234;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_TRUE(frameRateOverrides.empty());
}
@@ -2224,54 +2188,44 @@
std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, /*currentConfigId=*/
HWC_CONFIG_ID_120, config);
- auto layers = std::vector<LayerRequirement>{
- LayerRequirement{.ownerUid = 1234, .weight = 1.0f},
- };
-
+ std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}};
layers[0].name = "Test layer";
- layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].desiredRefreshRate = 60_Hz;
layers[0].vote = LayerVoteType::ExplicitDefault;
- auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ auto frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::ExplicitExact;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
layers[0].vote = LayerVoteType::ExplicitExactOrMultiple;
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/false);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {});
ASSERT_EQ(1, frameRateOverrides.size());
ASSERT_EQ(1, frameRateOverrides.count(1234));
- ASSERT_EQ(60.0f, frameRateOverrides.at(1234).getValue());
+ ASSERT_EQ(60_Hz, frameRateOverrides.at(1234));
- frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(layers, Fps(120.0f), /*touch=*/true);
+ frameRateOverrides = refreshRateConfigs->getFrameRateOverrides(layers, 120_Hz, {.touch = true});
ASSERT_TRUE(frameRateOverrides.empty());
}
} // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 35033ea..1e6e336 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -21,7 +21,6 @@
#include <gtest/gtest.h>
#include <gui/LayerMetadata.h>
-#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
#include "EffectLayer.h"
#include "Layer.h"
@@ -60,7 +59,6 @@
static constexpr int32_t PRIORITY_UNSET = -1;
void setupScheduler();
- sp<BufferQueueLayer> createBufferQueueLayer();
sp<BufferStateLayer> createBufferStateLayer();
sp<EffectLayer> createEffectLayer();
@@ -90,24 +88,17 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
-sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
- return new BufferQueueLayer(args);
-}
sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
+ LayerMetadata());
return new BufferStateLayer(args);
}
sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
- LayerMetadata());
+ LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
return new EffectLayer(args);
}
@@ -149,46 +140,6 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) {
- mParent = createBufferQueueLayer();
- mChild = createBufferQueueLayer();
- setParent(mChild.get(), mParent.get());
- mGrandChild = createBufferQueueLayer();
- setParent(mGrandChild.get(), mChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
- // Child has its own priority.
- mGrandChild->setFrameRateSelectionPriority(1);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Child inherits from his parent.
- mChild->setFrameRateSelectionPriority(1);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Grandchild inherits from his grand parent.
- mParent->setFrameRateSelectionPriority(1);
- commitTransaction(mParent.get());
- mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
mParent = createBufferStateLayer();
mChild = createBufferStateLayer();
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 12b155b..df4a9c4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
@@ -35,8 +31,7 @@
using testing::_;
using testing::AtLeast;
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
class RefreshRateStatsTest : public testing::Test {
protected:
@@ -81,61 +76,60 @@
int64_t vsyncPeriod) {
return DisplayMode::Builder(static_cast<hal::HWConfigId>(modeId.value()))
.setId(modeId)
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(static_cast<int32_t>(vsyncPeriod))
.setGroup(group)
.build();
}
namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
TEST_F(RefreshRateStatsTest, oneConfigTest) {
init({createDisplayMode(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
- ASSERT_EQ(1, times.size());
- EXPECT_NE(0u, times.count("ScreenOff"));
- // Setting up tests on mobile harness can be flaky with time passing, so testing for
- // exact time changes can result in flaxy numbers. To avoid that remember old
- // numbers to make sure the correct values are increasing in the next test.
- auto screenOff = times["ScreenOff"];
+ auto times = mRefreshRateStats->getTotalTimes();
+ ASSERT_TRUE(times.contains("ScreenOff"));
+ EXPECT_EQ(1u, times.size());
// Screen is off by default.
+ std::chrono::milliseconds screenOff = times.get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(0u, times.count("90.00fps"));
+
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_FALSE(times.contains("90.00 Hz"));
const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
mRefreshRateStats->setRefreshRate(config0Fps);
mRefreshRateStats->setPowerMode(PowerMode::ON);
- screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90.00fps"));
- EXPECT_LT(0, times["90.00fps"]);
+
+ EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+ ASSERT_TRUE(times.contains("90.00 Hz"));
+ EXPECT_LT(0ms, times.get("90.00 Hz")->get());
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
- auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+ const auto ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
+
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
mRefreshRateStats->setRefreshRate(config0Fps);
- screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
+
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
- EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
@@ -146,78 +140,81 @@
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
- std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
- ASSERT_EQ(1, times.size());
- EXPECT_NE(0u, times.count("ScreenOff"));
- // Setting up tests on mobile harness can be flaky with time passing, so testing for
- // exact time changes can result in flaxy numbers. To avoid that remember old
- // numbers to make sure the correct values are increasing in the next test.
- auto screenOff = times["ScreenOff"];
+ auto times = mRefreshRateStats->getTotalTimes();
+ ASSERT_TRUE(times.contains("ScreenOff"));
+ EXPECT_EQ(1u, times.size());
// Screen is off by default.
+ std::chrono::milliseconds screenOff = times.get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_LT(screenOff, times["ScreenOff"]);
+
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_FALSE(times.contains("60.00 Hz"));
+ EXPECT_FALSE(times.contains("90.00 Hz"));
const auto config0Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_0).getFps();
const auto config1Fps = mRefreshRateConfigs->getRefreshRateFromModeId(CONFIG_ID_1).getFps();
mRefreshRateStats->setRefreshRate(config0Fps);
mRefreshRateStats->setPowerMode(PowerMode::ON);
- screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_EQ(screenOff, times["ScreenOff"]);
- ASSERT_EQ(1u, times.count("90.00fps"));
- EXPECT_LT(0, times["90.00fps"]);
+
+ EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+ ASSERT_TRUE(times.contains("90.00 Hz"));
+ EXPECT_LT(0ms, times.get("90.00 Hz")->get());
// When power mode is normal, time for configs updates.
mRefreshRateStats->setRefreshRate(config1Fps);
- auto ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+ auto ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
- ASSERT_EQ(1u, times.count("60.00fps"));
- EXPECT_LT(0, times["60.00fps"]);
+
+ EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+ ASSERT_TRUE(times.contains("60.00 Hz"));
+ EXPECT_LT(0ms, times.get("60.00 Hz")->get());
mRefreshRateStats->setRefreshRate(config0Fps);
- auto sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
+ auto sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_LT(ninety, times["90.00fps"]);
- EXPECT_EQ(sixty, times["60.00fps"]);
+
+ EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+ EXPECT_LT(ninety, times.get("90.00 Hz")->get());
+ EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
mRefreshRateStats->setRefreshRate(config1Fps);
- ninety = mRefreshRateStats->getTotalTimes()["90.00fps"];
+ ninety = mRefreshRateStats->getTotalTimes().get("90.00 Hz")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_EQ(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
- EXPECT_LT(sixty, times["60.00fps"]);
+
+ EXPECT_EQ(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+ EXPECT_LT(sixty, times.get("60.00 Hz")->get());
// Because the power mode is not PowerMode::ON, switching the config
// does not update refresh rates that come from the config.
mRefreshRateStats->setPowerMode(PowerMode::DOZE);
mRefreshRateStats->setRefreshRate(config0Fps);
- sixty = mRefreshRateStats->getTotalTimes()["60.00fps"];
+ sixty = mRefreshRateStats->getTotalTimes().get("60.00 Hz")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
- EXPECT_EQ(sixty, times["60.00fps"]);
+
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+ EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
mRefreshRateStats->setRefreshRate(config1Fps);
- screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
+ screenOff = mRefreshRateStats->getTotalTimes().get("ScreenOff")->get();
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
- EXPECT_LT(screenOff, times["ScreenOff"]);
- EXPECT_EQ(ninety, times["90.00fps"]);
- EXPECT_EQ(sixty, times["60.00fps"]);
-}
-} // namespace
-} // namespace scheduler
-} // namespace android
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
+ EXPECT_LT(screenOff, times.get("ScreenOff")->get());
+ EXPECT_EQ(ninety, times.get("90.00 Hz")->get());
+ EXPECT_EQ(sixty, times.get("60.00 Hz")->get());
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 5713c2f..f48abb7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -28,13 +28,17 @@
#include "mock/MockLayer.h"
#include "mock/MockSchedulerCallback.h"
+namespace android::scheduler {
+
using testing::_;
using testing::Return;
-namespace android {
namespace {
-constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999);
+using MockEventThread = android::mock::EventThread;
+using MockLayer = android::mock::MockLayer;
+
+constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
class SchedulerTest : public testing::Test {
protected:
@@ -44,49 +48,41 @@
: EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
~MockEventThreadConnection() = default;
- MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
- MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
- MOCK_METHOD0(requestNextVsync, void());
+ MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
+ MOCK_METHOD1(setVsyncRate, binder::Status(int count));
+ MOCK_METHOD0(requestNextVsync, binder::Status());
};
SchedulerTest();
const DisplayModePtr mode60 = DisplayMode::Builder(0)
.setId(DisplayModeId(0))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
- .setVsyncPeriod(Fps(60.f).getPeriodNsecs())
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+ .setVsyncPeriod((60_Hz).getPeriodNsecs())
.setGroup(0)
.build();
const DisplayModePtr mode120 = DisplayMode::Builder(1)
.setId(DisplayModeId(1))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
- .setVsyncPeriod(Fps(120.f).getPeriodNsecs())
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
+ .setVsyncPeriod((120_Hz).getPeriodNsecs())
.setGroup(0)
.build();
- std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs =
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
+ std::shared_ptr<RefreshRateConfigs> mConfigs =
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
-
- // The scheduler should initially disable VSYNC.
- struct ExpectDisableVsync {
- ExpectDisableVsync(mock::SchedulerCallback& callback) {
- EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
- }
- } mExpectDisableVsync{mSchedulerCallback};
-
TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
- Scheduler::ConnectionHandle mConnectionHandle;
- mock::EventThread* mEventThread;
+ ConnectionHandle mConnectionHandle;
+ MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
TestableSurfaceFlinger mFlinger;
};
SchedulerTest::SchedulerTest() {
- auto eventThread = std::make_unique<mock::EventThread>();
+ auto eventThread = std::make_unique<MockEventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -106,7 +102,7 @@
} // namespace
TEST_F(SchedulerTest, invalidConnectionHandle) {
- Scheduler::ConnectionHandle handle;
+ ConnectionHandle handle;
const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
@@ -163,12 +159,12 @@
TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
// The layer is registered at creation time and deregistered at destruction time.
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
// recordLayerHistory should be a noop
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
constexpr bool kPowerStateNormal = true;
mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -181,25 +177,23 @@
}
TEST_F(SchedulerTest, updateDisplayModes) {
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
- ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+ ASSERT_EQ(0u, mScheduler->layerHistorySize());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
+ ASSERT_EQ(1u, mScheduler->layerHistorySize());
mScheduler->setRefreshRateConfigs(
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
- mode60->getId()));
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
-TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
- // If the optional fields are cleared, the function should return before
- // onModeChange is called.
- mScheduler->clearOptionalFieldsInFeatures();
- EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+TEST_F(SchedulerTest, dispatchCachedReportedMode) {
+ mScheduler->clearCachedReportedMode();
+
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
+ EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
}
TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
@@ -211,31 +205,30 @@
// If the handle is incorrect, the function should return before
// onModeChange is called.
- Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+ ConnectionHandle invalidHandle = {.id = 123};
EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 30ms));
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(Fps(90), 30ms));
- EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(Fps(120), 30ms));
+ EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 30ms));
+ EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(90_Hz, 30ms));
+ EXPECT_EQ(3, mFlinger.calculateMaxAcquiredBufferCount(120_Hz, 30ms));
- EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 40ms));
+ EXPECT_EQ(2, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 40ms));
- EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(Fps(60), 10ms));
+ EXPECT_EQ(1, mFlinger.calculateMaxAcquiredBufferCount(60_Hz, 10ms));
}
MATCHER(Is120Hz, "") {
- return arg.getFps().equalsWithMargin(Fps(120.f));
+ return isApproxEqual(arg.getFps(), 120_Hz);
}
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
mScheduler->setRefreshRateConfigs(
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
- mode60->getId()));
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -249,4 +242,4 @@
mScheduler->chooseRefreshRateForContent();
}
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index a4e9d20..fe5f9e0 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -24,16 +24,15 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
-#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
#include "EffectLayer.h"
#include "Layer.h"
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+#include "FpsOps.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockVsyncController.h"
namespace android {
@@ -49,6 +48,8 @@
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using scheduler::LayerHistory;
+
using FrameRate = Layer::FrameRate;
using FrameRateCompatibility = Layer::FrameRateCompatibility;
@@ -65,24 +66,13 @@
static constexpr uint32_t LAYER_FLAGS = 0;
};
-class BufferQueueLayerFactory : public LayerFactory {
-public:
- std::string name() override { return "BufferQueueLayer"; }
- sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
- sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
- return new BufferQueueLayer(args);
- }
-};
-
class BufferStateLayerFactory : public LayerFactory {
public:
std::string name() override { return "BufferStateLayer"; }
sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
+ LayerMetadata());
return new BufferStateLayer(args);
}
};
@@ -92,7 +82,7 @@
std::string name() override { return "EffectLayer"; }
sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+ LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS,
LayerMetadata());
return new EffectLayer(args);
}
@@ -108,12 +98,11 @@
*/
class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(Fps(67.f), FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 =
- FrameRate(Fps(14.f), FrameRateCompatibility::ExactOrMultiple);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(Fps(99.f), FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_TREE = FrameRate(Fps(0.f), FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(0.f), FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default);
SetFrameRateTest();
@@ -124,7 +113,6 @@
void commitTransaction();
TestableSurfaceFlinger mFlinger;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
std::vector<sp<Layer>> mLayers;
};
@@ -134,12 +122,9 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableUseFrameRateApi() = true;
-
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
}
void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
@@ -184,11 +169,9 @@
}
namespace {
-/* ------------------------------------------------------------------------
- * Test cases
- */
+
TEST_P(SetFrameRateTest, SetAndGet) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -199,7 +182,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParent) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -224,7 +207,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -263,7 +246,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChild) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -288,7 +271,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -327,7 +310,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -357,7 +340,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -388,7 +371,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -417,8 +400,7 @@
}
INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
- testing::Values(std::make_shared<BufferQueueLayerFactory>(),
- std::make_shared<BufferStateLayerFactory>(),
+ testing::Values(std::make_shared<BufferStateLayerFactory>(),
std::make_shared<EffectLayerFactory>()),
PrintToStringParamName);
@@ -471,24 +453,20 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
+ history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- const auto layerHistorySummary =
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0);
- ASSERT_EQ(2u, layerHistorySummary.size());
- EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate));
- EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate));
+ const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
+ const auto summary = history.summarize(*configs, 0);
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
+ EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[1].desiredRefreshRate);
}
TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 2362a31..f7d34ac 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -51,8 +51,8 @@
// --------------------------------------------------------------------
// Cleanup conditions
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // Creating the display commits a display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -86,9 +86,9 @@
// --------------------------------------------------------------------
// Cleanup conditions
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // Creating the display commits a display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index e2be074..c7e61c9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -40,8 +40,8 @@
// The call should notify the interceptor that a display was created.
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // Destroying the display commits a display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
new file mode 100644
index 0000000..9796a70
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+#include "mock/MockEventThread.h"
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <scheduler/Fps.h>
+
+namespace android {
+namespace {
+
+using android::hardware::graphics::composer::V2_4::Error;
+using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline;
+
+class DisplayModeSwitchingTest : public DisplayTransactionTest {
+public:
+ void SetUp() override {
+ injectFakeBufferQueueFactory();
+ injectFakeNativeWindowSurfaceFactory();
+
+ PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
+ PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
+ PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+ PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
+
+ mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120,
+ kDisplayMode90DifferentResolution})
+ .setActiveMode(kDisplayModeId60)
+ .inject();
+
+ setupScheduler();
+
+ // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
+ // will call setActiveConfig instead of setActiveConfigWithConstraints.
+ ON_CALL(*mComposer, isVsyncPeriodSwitchSupported()).WillByDefault(Return(true));
+ }
+
+protected:
+ void setupScheduler();
+ void testChangeRefreshRate(bool isDisplayActive, bool isRefreshRequired);
+
+ sp<DisplayDevice> mDisplay;
+ mock::EventThread* mAppEventThread;
+
+ const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
+ const DisplayModePtr kDisplayMode60 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
+ .setId(kDisplayModeId60)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((60_Hz).getPeriodNsecs())
+ .setGroup(0)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
+ const DisplayModePtr kDisplayMode90 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
+ .setId(kDisplayModeId90)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((90_Hz).getPeriodNsecs())
+ .setGroup(1)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
+ const DisplayModePtr kDisplayMode120 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
+ .setId(kDisplayModeId120)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((120_Hz).getPeriodNsecs())
+ .setGroup(2)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId90DifferentResolution = DisplayModeId(3);
+ const DisplayModePtr kDisplayMode90DifferentResolution =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90DifferentResolution.value()))
+ .setId(kDisplayModeId90DifferentResolution)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((90_Hz).getPeriodNsecs())
+ .setGroup(3)
+ .setHeight(2000)
+ .setWidth(2000)
+ .build();
+};
+
+void DisplayModeSwitchingTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ mAppEventThread = eventThread.get();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(
+ Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
+ /*hasMultipleModes*/ true);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ Mock::VerifyAndClearExpectations(mComposer);
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that the next commit will complete the mode change and send
+ // a onModeChanged event to the framework.
+
+ EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+ mFlinger.commit();
+ Mock::VerifyAndClearExpectations(mAppEventThread);
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), true, 0.f, 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ // and complete the mode change.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+
+ mFlinger.commit();
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+ // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
+ // is still being processed the later call will be respected.
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId120.value(), false, 0.f, 180.f, 0.f, 180.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId120.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+ mFlinger.commit();
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId120);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90DifferentResolution.value(), false, 0.f,
+ 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90DifferentResolution);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ // and complete the mode change.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(
+ kDisplayModeId90DifferentResolution.value()),
+ _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
+
+ // Misc expecations. We don't need to enforce these method calls, but since the helper methods
+ // already set expectations we should add new ones here, otherwise the test will fail.
+ EXPECT_CALL(*mConsumer, setDefaultBufferSize(2000, 2000)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(hal::Error::NONE));
+
+ // Create a new native surface to be used by the recreated display.
+ mNativeWindowSurface = nullptr;
+ injectFakeNativeWindowSurfaceFactory();
+ PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+ const auto displayToken = mDisplay->getDisplayToken().promote();
+
+ mFlinger.commit();
+
+ // The DisplayDevice will be destroyed and recreated,
+ // so we need to update with the new instance.
+ mDisplay = mFlinger.getDisplay(displayToken);
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90DifferentResolution);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
similarity index 74%
rename from services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
rename to services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
index b713334..6959ee3 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayTransactionCommitTest.cpp
@@ -22,8 +22,7 @@
namespace android {
namespace {
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
+struct DisplayTransactionCommitTest : DisplayTransactionTest {
template <typename Case>
void setupCommonPreconditions();
@@ -55,7 +54,7 @@
};
template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
+void DisplayTransactionCommitTest::setupCommonPreconditions() {
// Wide color displays support is configured appropriately
Case::WideColorSupport::injectConfigChange(this);
@@ -68,7 +67,7 @@
}
template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+void DisplayTransactionCommitTest::expectHotplugReceived(mock::EventThread* eventThread) {
const auto convert = [](auto physicalDisplayId) {
return std::make_optional(DisplayId{physicalDisplayId});
};
@@ -79,7 +78,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForConnectProcessing() {
Case::Display::setupHwcHotplugCallExpectations(this);
Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
@@ -97,7 +96,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+void DisplayTransactionCommitTest::setupCommonCallExpectationsForDisconnectProcessing() {
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
expectHotplugReceived<Case, false>(mEventThread);
@@ -105,7 +104,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
// The display device should have been set up in the list of displays.
ASSERT_TRUE(hasDisplayDevice(displayToken));
const auto& device = getDisplayDevice(displayToken);
@@ -137,7 +136,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+void DisplayTransactionCommitTest::verifyPhysicalDisplayIsConnected() {
// HWComposer should have an entry for the display
EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
@@ -150,14 +149,14 @@
verifyDisplayIsConnected<Case>(displayToken);
}
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+void DisplayTransactionCommitTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
EXPECT_FALSE(hasDisplayDevice(displayToken));
EXPECT_FALSE(hasCurrentDisplayState(displayToken));
EXPECT_FALSE(hasDrawingDisplayState(displayToken));
}
template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugConnectCommon() {
// --------------------------------------------------------------------
// Preconditions
@@ -174,7 +173,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -191,7 +190,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+void DisplayTransactionCommitTest::ignoresHotplugConnectCommon() {
// --------------------------------------------------------------------
// Preconditions
@@ -203,7 +202,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -213,7 +212,7 @@
}
template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+void DisplayTransactionCommitTest::processesHotplugDisconnectCommon() {
// --------------------------------------------------------------------
// Preconditions
@@ -238,7 +237,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -255,18 +254,18 @@
verifyDisplayIsNotConnected(existing.token());
}
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectPrimaryDisplay) {
processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectExternalDisplay) {
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
processesHotplugConnectCommon<SimpleExternalDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+TEST_F(DisplayTransactionCommitTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
// Inject both a primary and external display.
PrimaryDisplayVariant::injectHwcDisplay(this);
ExternalDisplayVariant::injectHwcDisplay(this);
@@ -281,108 +280,119 @@
ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
- processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectPrimaryDisplay) {
+ EXPECT_EXIT(processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(),
+ testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectExternalDisplay) {
processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
}
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugConnectThenDisconnectPrimary) {
+ EXPECT_EXIT(
+ [this] {
+ using Case = SimplePrimaryDisplayCase;
- // --------------------------------------------------------------------
- // Preconditions
+ // --------------------------------------------------------------------
+ // Preconditions
- setupCommonPreconditions<Case>();
+ setupCommonPreconditions<Case>();
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
- // A hotplug disconnect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ // A hotplug disconnect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- // --------------------------------------------------------------------
- // Call Expectations
+ // --------------------------------------------------------------------
+ // Call Expectations
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+ IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
- // --------------------------------------------------------------------
- // Invocation
+ // --------------------------------------------------------------------
+ // Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
- // --------------------------------------------------------------------
- // Postconditions
+ // --------------------------------------------------------------------
+ // Postconditions
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+ }(),
+ testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
+TEST_F(DisplayTransactionCommitTest, processesHotplugDisconnectThenConnectPrimary) {
+ EXPECT_EXIT(
+ [this] {
+ using Case = SimplePrimaryDisplayCase;
- // --------------------------------------------------------------------
- // Preconditions
+ // --------------------------------------------------------------------
+ // Preconditions
- setupCommonPreconditions<Case>();
+ setupCommonPreconditions<Case>();
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- // A hotplug connect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ // A hotplug connect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
- // --------------------------------------------------------------------
- // Call Expectations
+ // --------------------------------------------------------------------
+ // Call Expectations
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
- // --------------------------------------------------------------------
- // Invocation
+ // --------------------------------------------------------------------
+ // Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
- // --------------------------------------------------------------------
- // Postconditions
+ // --------------------------------------------------------------------
+ // Postconditions
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
- EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
- // A new display should be connected in its place
+ // A new display should be connected in its place
- verifyPhysicalDisplayIsConnected<Case>();
+ verifyPhysicalDisplayIsConnected<Case>();
- // --------------------------------------------------------------------
- // Cleanup conditions
+ // --------------------------------------------------------------------
+ // Cleanup conditions
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID,
+ IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+ }(),
+ testing::KilledBySignal(SIGABRT), "Primary display cannot be disconnected.");
}
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAdded) {
using Case = HwcVirtualDisplayCase;
// --------------------------------------------------------------------
@@ -433,7 +443,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -453,7 +463,7 @@
mFlinger.mutableDrawingState().displays.removeItem(displayToken);
}
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayAddedWithNoSurface) {
using Case = HwcVirtualDisplayCase;
// --------------------------------------------------------------------
@@ -479,7 +489,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -493,7 +503,7 @@
EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
}
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+TEST_F(DisplayTransactionCommitTest, processesVirtualDisplayRemoval) {
using Case = HwcVirtualDisplayCase;
// --------------------------------------------------------------------
@@ -511,7 +521,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -520,11 +530,11 @@
verifyDisplayIsNotConnected(existing.token());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackChanges) {
using Case = NonHwcVirtualDisplayCase;
- constexpr uint32_t oldLayerStack = 0u;
- constexpr uint32_t newLayerStack = 123u;
+ constexpr ui::LayerStack oldLayerStack = ui::DEFAULT_LAYER_STACK;
+ constexpr ui::LayerStack newLayerStack{123u};
// --------------------------------------------------------------------
// Preconditions
@@ -540,7 +550,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -548,7 +558,7 @@
EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayTransformChanges) {
using Case = NonHwcVirtualDisplayCase;
constexpr ui::Rotation oldTransform = ui::ROTATION_0;
@@ -568,7 +578,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -576,7 +586,7 @@
EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayLayerStackRectChanges) {
using Case = NonHwcVirtualDisplayCase;
const Rect oldLayerStackRect(0, 0, 0, 0);
@@ -596,7 +606,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -604,7 +614,7 @@
EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayRectChanges) {
using Case = NonHwcVirtualDisplayCase;
const Rect oldDisplayRect(0, 0);
@@ -624,7 +634,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
// --------------------------------------------------------------------
// Postconditions
@@ -632,7 +642,7 @@
EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
}
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayWidthChanges) {
using Case = NonHwcVirtualDisplayCase;
constexpr int oldWidth = 0;
@@ -674,10 +684,10 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
}
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplayHeightChanges) {
using Case = NonHwcVirtualDisplayCase;
constexpr int oldWidth = 0;
@@ -719,10 +729,10 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
}
-TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
+TEST_F(DisplayTransactionCommitTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) {
using Case = NonHwcVirtualDisplayCase;
constexpr uint32_t kOldWidth = 567;
@@ -769,7 +779,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+ mFlinger.commitTransactionsLocked(eDisplayTransactionNeeded);
EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize);
EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index bd89397..c9a2b00 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -38,9 +38,8 @@
// --------------------------------------------------------------------
// Call Expectations
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // We expect a scheduled commit for the display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
@@ -86,9 +85,8 @@
// --------------------------------------------------------------------
// Call Expectations
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // We expect a scheduled commit for the display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 69e0501..ec7e8a7 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -17,6 +17,9 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <chrono>
+#include <thread>
+
#include "DisplayTransactionTestHelpers.h"
#include <android/hardware/power/Boost.h>
@@ -27,6 +30,8 @@
using android::hardware::power::Boost;
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ using namespace std::chrono_literals;
+
mFlinger.scheduler()->replaceTouchTimer(100);
std::this_thread::sleep_for(10ms); // wait for callback to be triggered
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
@@ -47,4 +52,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index ef8b149..37cf05e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -46,9 +46,8 @@
// We expect a call to get the active display config.
Case::Display::setupHwcGetActiveConfigCallExpectations(this);
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ // We expect a scheduled commit for the display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
@@ -63,15 +62,11 @@
// The primary display should have a current state
ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
- // The layer stack state should be set to zero
- EXPECT_EQ(0u, primaryDisplayState.layerStack);
- // The orientation state should be set to zero
+
+ // The primary display state should be reset
+ EXPECT_EQ(ui::DEFAULT_LAYER_STACK, primaryDisplayState.layerStack);
EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
- // The orientedDisplaySpaceRect state should be set to INVALID
EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
-
- // The layerStackSpaceRect state should be set to INVALID
EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
// The width and height should both be zero
@@ -99,4 +94,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
index fc40818..7d9e22b 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -25,6 +25,8 @@
namespace android {
namespace {
+constexpr ui::LayerStack LAYER_STACK{456u};
+
class SetDisplayStateLockedTest : public DisplayTransactionTest {};
TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
@@ -38,7 +40,7 @@
DisplayState state;
state.what = DisplayState::eLayerStackChanged;
state.token = displayToken;
- state.layerStack = 456;
+ state.layerStack = LAYER_STACK;
// --------------------------------------------------------------------
// Invocation
@@ -167,13 +169,13 @@
display.inject();
// The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 456u;
+ display.mutableCurrentDisplayState().layerStack = LAYER_STACK;
// The incoming request sets the same layer stack
DisplayState state;
state.what = DisplayState::eLayerStackChanged;
state.token = display.token();
- state.layerStack = 456u;
+ state.layerStack = LAYER_STACK;
// --------------------------------------------------------------------
// Invocation
@@ -187,7 +189,7 @@
EXPECT_EQ(0u, flags);
// The current display state is unchanged
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+ EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
}
TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
@@ -201,13 +203,13 @@
display.inject();
// The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 654u;
+ display.mutableCurrentDisplayState().layerStack = ui::LayerStack{LAYER_STACK.id + 1};
// The incoming request sets a different layer stack
DisplayState state;
state.what = DisplayState::eLayerStackChanged;
state.token = display.token();
- state.layerStack = 456u;
+ state.layerStack = LAYER_STACK;
// --------------------------------------------------------------------
// Invocation
@@ -221,7 +223,7 @@
EXPECT_EQ(eDisplayTransactionNeeded, flags);
// The desired display state has been set to the new value.
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+ EXPECT_EQ(LAYER_STACK, display.getCurrentDisplayState().layerStack);
}
TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfFlagsNotChanged) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index eea1002..b57feff 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -273,7 +273,7 @@
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
}
static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index e32c4bf..38dceb9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -34,7 +34,6 @@
static constexpr bool WIDE_COLOR_SUPPORTED = true;
static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
test->mFlinger.mutableHasWideColorDisplay() = true;
test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
}
@@ -257,6 +256,7 @@
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
+ state.flags = Case::Display::DISPLAY_FLAGS;
auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
displaySurface, producer);
@@ -279,6 +279,8 @@
EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
device->getSupportedPerFrameMetadata());
+ EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput,
+ device->receivesInput());
if constexpr (Case::Display::CONNECTION_TYPE::value) {
EXPECT_EQ(1, device->getSupportedModes().size());
@@ -292,7 +294,9 @@
}
TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
- setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+ // External displays must be secondary, as the primary display cannot be disconnected.
+ EXPECT_EXIT(setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(),
+ testing::KilledBySignal(SIGABRT), "Missing primary display");
}
TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1d21bd4..364d8f1 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,23 +28,30 @@
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
-namespace android {
+namespace android::scheduler {
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback)
+ TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_unique<mock::VSyncTracker>(), refreshRateConfigs,
+ std::make_unique<mock::VSyncTracker>(), std::move(configs),
callback) {}
- TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
- const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback)
- : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
- refreshRateConfigs, callback, createLayerHistory(),
- {.useContentDetection = true}) {}
+ TestableScheduler(std::unique_ptr<VsyncController> controller,
+ std::unique_ptr<VSyncTracker> tracker,
+ std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+ : Scheduler(*this, callback, Feature::kContentDetection) {
+ mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
+ setRefreshRateConfigs(std::move(configs));
+
+ ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
+ // Execute task to prevent broken promise exception on destruction.
+ handler->handleMessage(Message());
+ });
+ }
+
+ MOCK_METHOD(void, scheduleFrame, (), (override));
+ MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -58,20 +65,16 @@
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
-
- auto* mutableLayerHistory() { return mLayerHistory.get(); }
+ auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
- if (!mLayerHistory) return 0;
- return mutableLayerHistory()->mLayerInfos.size();
+ return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
}
auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
- if (!mLayerHistory) return 0;
- return mutableLayerHistory()->mActiveLayersEnd;
+ return mLayerHistory.mActiveLayerInfos.size();
}
void replaceTouchTimer(int64_t millis) {
@@ -86,32 +89,29 @@
}
bool isTouchActive() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- return mFeatures.touch == Scheduler::TouchState::Active;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ return mPolicy.touch == Scheduler::TouchState::Active;
}
void dispatchCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
return Scheduler::dispatchCachedReportedMode();
}
- void clearOptionalFieldsInFeatures() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.cachedModeChangedParams.reset();
+ void clearCachedReportedMode() {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.cachedModeChangedParams.reset();
}
void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
- ~TestableScheduler() {
- // All these pointer and container clears help ensure that GMock does
- // not report a leaked object, since the Scheduler instance may
- // still be referenced by something despite our best efforts to destroy
- // it after each test is done.
- mVsyncSchedule.controller.reset();
- mConnections.clear();
- }
+private:
+ // ICompositor overrides:
+ bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
+ void composite(nsecs_t) override {}
+ void sample() override {}
};
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a23361e..0067997 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -24,6 +24,7 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
#include <gui/ScreenCaptureResults.h>
+#include <algorithm>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -73,20 +74,11 @@
return nullptr;
}
- std::unique_ptr<MessageQueue> createMessageQueue() override {
- return std::make_unique<android::impl::MessageQueue>();
- }
-
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps /*currentRefreshRate*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override {
- return nullptr;
- }
-
sp<SurfaceInterceptor> createSurfaceInterceptor() override {
return new android::impl::SurfaceInterceptor();
}
@@ -178,12 +170,12 @@
} // namespace surfaceflinger::test
-class TestableSurfaceFlinger final : private ISchedulerCallback {
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
public:
using HotplugEvent = SurfaceFlinger::HotplugEvent;
SurfaceFlinger* flinger() { return mFlinger.get(); }
- TestableScheduler* scheduler() { return mScheduler; }
+ scheduler::TestableScheduler* scheduler() { return mScheduler; }
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
@@ -206,10 +198,11 @@
std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
+ scheduler::ISchedulerCallback* callback = nullptr,
+ bool hasMultipleModes = false) {
DisplayModes modes{DisplayMode::Builder(0)
.setId(DisplayModeId(0))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(16'666'667)
.setGroup(0)
.build()};
@@ -217,7 +210,7 @@
if (hasMultipleModes) {
modes.emplace_back(DisplayMode::Builder(1)
.setId(DisplayModeId(1))
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setVsyncPeriod(11'111'111)
.setGroup(0)
.build());
@@ -233,17 +226,18 @@
std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
/*powerMode=*/hal::PowerMode::OFF);
- mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
- mRefreshRateConfigs, *(callback ?: this));
+ mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
+ std::move(vsyncTracker), mRefreshRateConfigs,
+ *(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
}
- void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+ void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
- TestableScheduler& mutableScheduler() const { return *mScheduler; }
+ scheduler::TestableScheduler& mutableScheduler() const { return *mScheduler; }
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
@@ -276,7 +270,8 @@
layer->editCompositionState()->sidebandStream = sidebandStream;
}
- void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+ void setLayerCompositionType(const sp<Layer>& layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
LOG_ALWAYS_FATAL_IF(!outputLayer);
auto& state = outputLayer->editState();
@@ -296,6 +291,16 @@
* Forwarding for functions being tested
*/
+ nsecs_t commit() {
+ constexpr int64_t kVsyncId = 123;
+ const nsecs_t now = systemTime();
+ const nsecs_t expectedVsyncTime = now + 10'000'000;
+ mFlinger->commit(now, kVsyncId, expectedVsyncTime);
+ return now;
+ }
+
+ void commitAndComposite() { mFlinger->composite(commit()); }
+
auto createDisplay(const String8& displayName, bool secure) {
return mFlinger->createDisplay(displayName, secure);
}
@@ -304,6 +309,11 @@
return mFlinger->destroyDisplay(displayToken);
}
+ auto getDisplay(const sp<IBinder>& displayToken) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ return mFlinger->getDisplayDeviceLocked(displayToken);
+ }
+
void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
auto setupNewDisplayDeviceInternal(
@@ -316,9 +326,9 @@
dispSurface, producer);
}
- auto handleTransactionLocked(uint32_t transactionFlags) {
- Mutex::Autolock _l(mFlinger->mStateLock);
- return mFlinger->handleTransactionLocked(transactionFlags);
+ auto commitTransactionsLocked(uint32_t transactionFlags) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ return mFlinger->commitTransactionsLocked(transactionFlags);
}
void onComposerHalHotplug(hal::HWDisplayId hwcDisplayId, hal::Connection connection) {
@@ -326,7 +336,7 @@
}
auto setDisplayStateLocked(const DisplayState& s) {
- Mutex::Autolock _l(mFlinger->mStateLock);
+ Mutex::Autolock lock(mFlinger->mStateLock);
return mFlinger->setDisplayStateLocked(s);
}
@@ -343,10 +353,6 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto onMessageReceived(int32_t what) {
- return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime());
- }
-
auto renderScreenImplLocked(const RenderArea& renderArea,
SurfaceFlinger::TraverseLayersFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
@@ -369,6 +375,7 @@
auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -382,7 +389,7 @@
listenerCallbacks, transactionId);
}
- auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+ auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
return mFlinger->onTransact(code, data, reply, flags);
@@ -395,6 +402,21 @@
return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
+ auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
+ bool allowGroupSwitching, float primaryRefreshRateMin,
+ float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
+ return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
+ }
+
+ void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -419,14 +441,12 @@
*/
auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
- auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
auto& mutableDisplays() { return mFlinger->mDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
- auto& mutableEventQueue() { return mFlinger->mEventQueue; }
- auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
+ auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
auto& mutableInterceptor() { return mFlinger->mInterceptor; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
@@ -439,9 +459,7 @@
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
- auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
- auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
- auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
+ auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
auto fromHandle(const sp<IBinder>& handle) {
@@ -456,7 +474,6 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
- mutableEventQueue().reset();
mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
@@ -487,7 +504,7 @@
static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
static constexpr int32_t DEFAULT_WIDTH = 1920;
static constexpr int32_t DEFAULT_HEIGHT = 1280;
- static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'666;
+ static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
@@ -601,13 +618,12 @@
LOG_ALWAYS_FATAL_IF(!physicalId);
flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId);
if (mIsPrimary) {
- flinger->mutableInternalHwcDisplayId() = mHwcDisplayId;
+ flinger->mutablePrimaryHwcDisplayId() = mHwcDisplayId;
} else {
- // If there is an external HWC display there should always be an internal ID
+ // If there is an external HWC display, there should always be a primary ID
// as well. Set it to some arbitrary value.
- auto& internalId = flinger->mutableInternalHwcDisplayId();
- if (!internalId) internalId = mHwcDisplayId - 1;
- flinger->mutableExternalHwcDisplayId() = mHwcDisplayId;
+ auto& primaryId = flinger->mutablePrimaryHwcDisplayId();
+ if (!primaryId) primaryId = mHwcDisplayId - 1;
}
}
}
@@ -642,11 +658,11 @@
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
- mActiveModeId = DisplayModeId(0);
+ mCreationArgs.activeModeId = DisplayModeId(0);
DisplayModePtr activeMode =
DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
- .setId(mActiveModeId)
- .setPhysicalDisplayId(PhysicalDisplayId(0))
+ .setId(mCreationArgs.activeModeId)
+ .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
.setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
.setVsyncPeriod(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)
@@ -681,7 +697,7 @@
auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
auto& setActiveMode(DisplayModeId mode) {
- mActiveModeId = mode;
+ mCreationArgs.activeModeId = mode;
return *this;
}
@@ -736,14 +752,29 @@
const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
LOG_ALWAYS_FATAL_IF(!physicalId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
+
+ const DisplayModePtr activeModePtr =
+ *std::find_if(mCreationArgs.supportedModes.begin(),
+ mCreationArgs.supportedModes.end(), [&](DisplayModePtr mode) {
+ return mode->getId() == mCreationArgs.activeModeId;
+ });
+ state.physical = {.id = *physicalId,
+ .type = *type,
+ .hwcDisplayId = *mHwcDisplayId,
+ .deviceProductInfo = {},
+ .supportedModes = mCreationArgs.supportedModes,
+ .activeMode = activeModePtr};
}
state.isSecure = mCreationArgs.isSecure;
+ mCreationArgs.refreshRateConfigs =
+ std::make_shared<scheduler::RefreshRateConfigs>(mCreationArgs.supportedModes,
+ mCreationArgs.activeModeId);
+
sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
if (!device->isVirtual()) {
- device->setActiveMode(mActiveModeId);
+ device->setActiveMode(mCreationArgs.activeModeId);
}
mFlinger.mutableDisplays().emplace(mDisplayToken, device);
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
@@ -761,19 +792,18 @@
sp<BBinder> mDisplayToken = new BBinder();
DisplayDeviceCreationArgs mCreationArgs;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
- DisplayModeId mActiveModeId;
};
private:
+ void scheduleComposite(FrameHint) override {}
void setVsyncEnabled(bool) override {}
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
- void repaintEverythingForHWC() override {}
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() {}
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
- TestableScheduler* mScheduler = nullptr;
+ scheduler::TestableScheduler* mScheduler = nullptr;
std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
};
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 317cdf1..0ef8456 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -64,17 +64,15 @@
#define LAYER_ID_0 0
#define LAYER_ID_1 1
#define UID_0 123
-#define REFRESH_RATE_0 61
-#define RENDER_RATE_0 31
#define REFRESH_RATE_BUCKET_0 60
#define RENDER_RATE_BUCKET_0 30
#define LAYER_ID_INVALID -1
#define NUM_LAYERS 1
#define NUM_LAYERS_INVALID "INVALID"
-const constexpr Fps kRefreshRate0 = Fps(static_cast<float>(REFRESH_RATE_0));
-const constexpr Fps kRenderRate0 = Fps(static_cast<float>(RENDER_RATE_0));
-static constexpr int32_t kGameMode = TimeStatsHelper::GameModeUnsupported;
+constexpr Fps kRefreshRate0 = 61_Hz;
+constexpr Fps kRenderRate0 = 31_Hz;
+constexpr GameMode kGameMode = GameMode::Unsupported;
enum InputCommand : int32_t {
ENABLE = 0,
@@ -147,14 +145,14 @@
std::string inputCommand(InputCommand cmd, bool useProto);
void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode);
+ TimeStats::SetFrameRateVote, GameMode);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {},
- int32_t gameMode = kGameMode) {
+ GameMode gameMode = kGameMode) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote, gameMode);
}
@@ -205,7 +203,7 @@
}
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode) {
+ TimeStats::SetFrameRateVote frameRateVote, GameMode gameMode) {
switch (type) {
case TimeStamp::POST:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id),
@@ -1170,8 +1168,7 @@
constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
}
@@ -1184,42 +1181,39 @@
TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
.seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
};
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60,
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60, GameMode::Standard);
- mTimeStats->incrementJankyFrames(
- {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerCpuDeadlineMissed,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames(
- {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerGpuDeadlineMissed,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::DisplayHAL,
+ GameMode::Standard, JankType::SurfaceFlingerCpuDeadlineMissed,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ GameMode::Standard, JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::DisplayHAL,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::AppDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::SurfaceFlingerScheduling,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::PredictionError,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), GameMode::Standard,
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ APP_DEADLINE_DELTA_2MS, APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::PredictionError,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
- APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::AppDeadlineMissed | JankType::BufferStuffing,
- DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
- APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::None,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
- APP_DEADLINE_DELTA_3MS});
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
@@ -1295,22 +1289,18 @@
constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
}
for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
}
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {},
- TimeStatsHelper::GameModeStandard);
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {},
- TimeStatsHelper::GameModePerformance);
-
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, TimeStatsHelper::GameModeBattery);
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, TimeStatsHelper::GameModeBattery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {}, GameMode::Standard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
@@ -1498,14 +1488,14 @@
EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps;
};
- verifyRefreshRateBucket(Fps(91.f), 90);
- verifyRefreshRateBucket(Fps(89.f), 90);
+ verifyRefreshRateBucket(91_Hz, 90);
+ verifyRefreshRateBucket(89_Hz, 90);
- verifyRefreshRateBucket(Fps(61.f), 60);
- verifyRefreshRateBucket(Fps(59.f), 60);
+ verifyRefreshRateBucket(61_Hz, 60);
+ verifyRefreshRateBucket(59_Hz, 60);
- verifyRefreshRateBucket(Fps(31.f), 30);
- verifyRefreshRateBucket(Fps(29.f), 30);
+ verifyRefreshRateBucket(31_Hz, 30);
+ verifyRefreshRateBucket(29_Hz, 30);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 1a50427..16d4b59 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -24,12 +24,11 @@
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
+#include <ui/MockFence.h>
#include <utils/String8.h>
-#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockVsyncController.h"
namespace android {
@@ -46,7 +45,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
}
@@ -74,20 +72,27 @@
EXPECT_CALL(*mVSyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*mFenceUnsignaled, getStatus())
+ .WillRepeatedly(Return(Fence::Status::Unsignaled));
+ EXPECT_CALL(*mFenceUnsignaled2, getStatus())
+ .WillRepeatedly(Return(Fence::Status::Unsignaled));
+ EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+ EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
}
- TestableScheduler* mScheduler;
TestableSurfaceFlinger mFlinger;
- std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::VsyncController* mVsyncController = new mock::VsyncController();
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
+ mock::MockFence* mFenceUnsignaled = new mock::MockFence();
+ mock::MockFence* mFenceSignaled = new mock::MockFence();
+ mock::MockFence* mFenceUnsignaled2 = new mock::MockFence();
+ mock::MockFence* mFenceSignaled2 = new mock::MockFence();
struct TransactionInfo {
Vector<ComposerState> states;
@@ -103,7 +108,7 @@
static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
- void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
+ void checkEqual(TransactionInfo info, TransactionState state) {
EXPECT_EQ(0u, info.states.size());
EXPECT_EQ(0u, state.states.size());
@@ -124,10 +129,18 @@
transaction.frameTimelineInfo = frameTimelineInfo;
}
+ void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags,
+ bool syncInputWindows, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>* states) {
+ setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp,
+ frameTimelineInfo);
+ transaction.states = *states;
+ }
+
void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- // called in SurfaceFlinger::signalTransaction
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -157,8 +170,7 @@
void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- // called in SurfaceFlinger::signalTransaction
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
@@ -187,12 +199,11 @@
void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- // called in SurfaceFlinger::signalTransaction
nsecs_t time = systemTime();
if (!syncInputWindows) {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(2);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
} else {
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
// transaction that should go on the pending thread
TransactionInfo transactionA;
@@ -248,6 +259,188 @@
EXPECT_EQ(0u, transactionQueue.size());
}
+ void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1,
+ Vector<ComposerState> state2,
+ bool updateApplyToken = true) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+
+ TransactionInfo transactionA;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+
+ TransactionInfo transactionB;
+ if (updateApplyToken) {
+ transactionB.applyToken = sp<IBinder>();
+ }
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_removesFromTheQueue(const Vector<ComposerState>& state) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ TransactionInfo transaction;
+ setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state);
+
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_keepsInTheQueue(const Vector<ComposerState>& state) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ TransactionInfo transaction;
+ setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state);
+
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1,
+ const Vector<ComposerState>& state2,
+ bool updateApplyToken = true,
+ uint32_t pendingTransactionQueueSize = 1u) {
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ auto time = systemTime();
+ TransactionInfo transactionA;
+ TransactionInfo transactionB;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+ if (updateApplyToken) {
+ transactionB.applyToken = sp<IBinder>();
+ }
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ }
+
+ void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1,
+ const Vector<ComposerState>& state2) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ auto time = systemTime();
+ TransactionInfo transactionA;
+ TransactionInfo transactionB;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ static Vector<ComposerState> createComposerStateVector(const ComposerState& state1,
+ const ComposerState& state2) {
+ Vector<ComposerState> states;
+ states.push_back(state1);
+ states.push_back(state2);
+ return states;
+ }
+
+ static Vector<ComposerState> createComposerStateVector(const ComposerState& state) {
+ Vector<ComposerState> states;
+ states.push_back(state);
+ return states;
+ }
+
+ static ComposerState createComposerState(int layerId, sp<Fence> fence,
+ uint32_t stateFlags = layer_state_t::eBufferChanged) {
+ ComposerState composer_state;
+ composer_state.state.bufferData.acquireFence = std::move(fence);
+ composer_state.state.layerId = layerId;
+ composer_state.state.bufferData.flags = BufferData::BufferDataChange::fenceChanged;
+ composer_state.state.flags = stateFlags;
+ return composer_state;
+ }
+
bool mHasListenerCallbacks = false;
std::vector<ListenerCallbacks> mCallbacks;
int mTransactionNumber = 0;
@@ -255,8 +448,7 @@
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- // called in SurfaceFlinger::signalTransaction
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
@@ -331,4 +523,216 @@
auto ret = mFlinger.fromHandle(badHandle);
EXPECT_EQ(nullptr, ret.promote().get());
}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled,
+ layer_state_t::eCropChanged | layer_state_t::eBufferChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ true,
+ /*pendingTransactionQueueSize*/ 2u);
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2,
+ mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1,
+ mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)),
+ /*updateApplyToken*/ false);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1,
+ mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2,
+ mFenceUnsignaled)));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index a749ece..deeb785 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -46,6 +46,7 @@
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
}
~TransactionFrameTracerTest() {
@@ -56,7 +57,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
@@ -92,21 +93,17 @@
}
TestableSurfaceFlinger mFlinger;
- renderengine::mock::RenderEngine mRenderEngine;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
FenceToFenceTimeMap fenceFactory;
- client_cache_t mClientCache;
void BLASTTransactionSendsFrameTracerEvents() {
sp<BufferStateLayer> layer = createBufferStateLayer();
sp<Fence> fence(new Fence());
- const auto buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
int32_t layerId = layer->getSequence();
- uint64_t bufferId = buffer->getBuffer()->getId();
+ uint64_t bufferId = buffer->getId();
uint64_t frameNumber = 5;
nsecs_t dequeueTime = 10;
nsecs_t postTime = 20;
@@ -117,9 +114,14 @@
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceTimestamp(layerId, bufferId, frameNumber, postTime,
FrameTracer::FrameEvent::QUEUE, /*duration*/ 0));
- layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache,
- frameNumber, dequeueTime, FrameTimelineInfo{},
- nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = frameNumber;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, postTime, /*desiredPresentTime*/ 30, false, dequeueTime,
+ FrameTimelineInfo{});
commitTransaction(layer.get());
bool computeVisisbleRegions;
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
new file mode 100644
index 0000000..6e00748
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <limits> // std::numeric_limits
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+TEST(TransactionProtoParserTest, parse) {
+ const sp<IBinder> layerHandle = new BBinder();
+ const sp<IBinder> displayHandle = new BBinder();
+ TransactionState t1;
+ t1.originPid = 1;
+ t1.originUid = 2;
+ t1.frameTimelineInfo.vsyncId = 3;
+ t1.frameTimelineInfo.inputEventId = 4;
+ t1.postTime = 5;
+
+ layer_state_t layer;
+ layer.layerId = 6;
+ layer.what = std::numeric_limits<uint64_t>::max();
+ layer.x = 7;
+ layer.matrix.dsdx = 15;
+
+ size_t layerCount = 2;
+ t1.states.reserve(layerCount);
+ for (uint32_t i = 0; i < layerCount; i++) {
+ ComposerState s;
+ if (i == 1) {
+ layer.parentSurfaceControlForChild =
+ new SurfaceControl(SurfaceComposerClient::getDefault(), layerHandle, nullptr,
+ 42);
+ }
+ s.state = layer;
+ t1.states.add(s);
+ }
+
+ size_t displayCount = 2;
+ t1.displays.reserve(displayCount);
+ for (uint32_t i = 0; i < displayCount; i++) {
+ DisplayState display;
+ display.what = std::numeric_limits<uint32_t>::max();
+ if (i == 0) {
+ display.token = displayHandle;
+ } else {
+ display.token = nullptr;
+ }
+ display.width = 85;
+ t1.displays.add(display);
+ }
+
+ TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) {
+ return (handle == layerHandle) ? 42 : -1;
+ };
+ TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) {
+ return (handle == displayHandle) ? 43 : -1;
+ };
+ TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) {
+ return (id == 42) ? layerHandle : nullptr;
+ };
+ TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) {
+ return (id == 43) ? displayHandle : nullptr;
+ };
+
+ proto::TransactionState proto =
+ TransactionProtoParser::toProto(t1, getLayerIdFn, getDisplayIdFn);
+ TransactionState t2 =
+ TransactionProtoParser::fromProto(proto, getLayerHandleFn, getDisplayHandleFn);
+
+ ASSERT_EQ(t1.originPid, t2.originPid);
+ ASSERT_EQ(t1.originUid, t2.originUid);
+ ASSERT_EQ(t1.frameTimelineInfo.vsyncId, t2.frameTimelineInfo.vsyncId);
+ ASSERT_EQ(t1.frameTimelineInfo.inputEventId, t2.frameTimelineInfo.inputEventId);
+ ASSERT_EQ(t1.postTime, t2.postTime);
+ ASSERT_EQ(t1.states.size(), t2.states.size());
+ ASSERT_EQ(t1.states[0].state.x, t2.states[0].state.x);
+ ASSERT_EQ(t1.states[0].state.matrix.dsdx, t2.states[0].state.matrix.dsdx);
+ ASSERT_EQ(t1.states[1].state.parentSurfaceControlForChild->getHandle(),
+ t2.states[1].state.parentSurfaceControlForChild->getHandle());
+
+ ASSERT_EQ(t1.displays.size(), t2.displays.size());
+ ASSERT_EQ(t1.displays[1].width, t2.displays[1].width);
+ ASSERT_EQ(t1.displays[0].token, t2.displays[0].token);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 2a7921f..704340d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -46,6 +46,7 @@
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
}
~TransactionSurfaceFrameTest() {
@@ -56,7 +57,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
@@ -92,10 +93,9 @@
}
TestableSurfaceFlinger mFlinger;
- renderengine::mock::RenderEngine mRenderEngine;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
FenceToFenceTimeMap fenceFactory;
- client_cache_t mClientCache;
void PresentedSurfaceFrameForBufferlessTransaction() {
sp<BufferStateLayer> layer = createBufferStateLayer();
@@ -114,13 +114,15 @@
sp<BufferStateLayer> layer = createBufferStateLayer();
sp<Fence> fence(new Fence());
auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
- const auto buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -143,27 +145,30 @@
sp<Fence> fence1(new Fence());
auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
- const auto buffer1 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer1;
+ bufferData.acquireFence = fence1;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
sp<Fence> fence2(new Fence());
auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
- const auto buffer2 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
+ const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
nsecs_t start = systemTime();
- layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ bufferData.buffer = buffer2;
+ bufferData.acquireFence = fence2;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -198,13 +203,15 @@
sp<Fence> fence(new Fence());
auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
- const auto buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -227,13 +234,15 @@
sp<BufferStateLayer> layer = createBufferStateLayer();
sp<Fence> fence(new Fence());
auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
- const auto buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -260,13 +269,15 @@
sp<Fence> fence(new Fence());
auto acquireFence = fenceFactory.createFenceTimeForTest(fence);
- const auto buffer = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 3, /*inputEventId*/ 0});
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -299,25 +310,28 @@
sp<Fence> fence1(new Fence());
auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
- const auto buffer1 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer1;
+ bufferData.acquireFence = fence1;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
sp<Fence> fence2(new Fence());
auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
- const auto buffer2 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ bufferData.buffer = buffer2;
+ bufferData.acquireFence = fence2;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -330,7 +344,7 @@
// Both the droppedSurfaceFrame and presentedSurfaceFrame should be in
// pendingJankClassifications.
EXPECT_EQ(2u, layer->mPendingJankClassifications.size());
- presentedSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ presentedSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
/*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
layer->releasePendingBuffer(25);
@@ -342,27 +356,30 @@
sp<Fence> fence1(new Fence());
auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1);
- const auto buffer1 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
- layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ const auto buffer1 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer1;
+ bufferData.acquireFence = fence1;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
sp<Fence> fence2(new Fence());
auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2);
- const auto buffer2 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
+ const auto buffer2 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
auto dropStartTime1 = systemTime();
- layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0},
- nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */);
+ bufferData.buffer = buffer2;
+ bufferData.acquireFence = fence2;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0});
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -370,14 +387,15 @@
sp<Fence> fence3(new Fence());
auto acquireFence3 = fenceFactory.createFenceTimeForTest(fence3);
- const auto buffer3 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888,
- 1, 0),
- mRenderEngine, false);
+ const auto buffer3 = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
auto dropStartTime2 = systemTime();
- layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ bufferData.buffer = buffer3;
+ bufferData.acquireFence = fence3;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 2, /*inputEventId*/ 0});
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -413,16 +431,16 @@
uint32_t surfaceFramesPendingClassification = 0;
std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
for (int i = 0; i < 10; i += 2) {
- sp<Fence> fence1(new Fence());
- const auto buffer1 = std::make_shared<
- renderengine::ExternalTexture>(new GraphicBuffer(1, 1,
- HAL_PIXEL_FORMAT_RGBA_8888, 1,
- 0),
- mRenderEngine, false);
- layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
- {/*vsyncId*/ 1, /*inputEventId*/ 0},
- nullptr /* releaseBufferCallback */,
- nullptr /* releaseBufferEndpoint */);
+ sp<Fence> fence(new Fence());
+ const auto buffer = new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
+ BufferData bufferData;
+ bufferData.buffer = buffer;
+ bufferData.acquireFence = fence;
+ bufferData.frameNumber = 1;
+ bufferData.flags |= BufferData::BufferDataChange::fenceChanged;
+ bufferData.flags |= BufferData::BufferDataChange::frameNumberChanged;
+ layer->setBuffer(bufferData, 10, 20, false, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
/*inputEventId*/ 0},
10);
@@ -444,10 +462,10 @@
// BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
// Since we don't have access to DisplayFrame here, trigger an onPresent directly.
for (auto& surfaceFrame : bufferlessSurfaceFrames) {
- surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ surfaceFrame->onPresent(20, JankType::None, 90_Hz,
/*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
}
- presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ presentedBufferSurfaceFrame->onPresent(20, JankType::None, 90_Hz,
/*displayDeadlineDelta*/ 0,
/*displayPresentDelta*/ 0);
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
new file mode 100644
index 0000000..43b09fd
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/RingBuffer.h"
+#include "Tracing/TransactionTracing.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+class TransactionTracingTest : public testing::Test {
+protected:
+ static constexpr size_t SMALL_BUFFER_SIZE = 1024;
+ std::unique_ptr<android::TransactionTracing> mTracing;
+ void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
+
+ void TearDown() override {
+ mTracing->disable();
+ mTracing.reset();
+ }
+
+ auto getCommittedTransactions() {
+ std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
+ return mTracing->mCommittedTransactions;
+ }
+
+ auto getQueuedTransactions() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mQueuedTransactions;
+ }
+
+ auto getUsedBufferSize() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mBuffer->used();
+ }
+
+ auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
+
+ auto bufferFront() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ proto::TransactionTraceEntry entry;
+ entry.ParseFromString(mTracing->mBuffer->front());
+ return entry;
+ }
+
+ bool threadIsJoinable() {
+ std::scoped_lock lock(mTracing->mMainThreadLock);
+ return mTracing->mThread.joinable();
+ }
+
+ proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
+
+ auto getCreatedLayers() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mCreatedLayers;
+ }
+
+ auto getStartingStates() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mStartingStates;
+ }
+
+ void queueAndCommitTransaction(int64_t vsyncId) {
+ TransactionState transaction;
+ transaction.id = static_cast<uint64_t>(vsyncId * 3);
+ transaction.originUid = 1;
+ transaction.originPid = 2;
+ mTracing->addQueuedTransaction(transaction);
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ mTracing->addCommittedTransactions(transactions, vsyncId);
+ flush(vsyncId);
+ }
+
+ // Test that we clean up the tracing thread and free any memory allocated.
+ void verifyDisabledTracingState() {
+ EXPECT_FALSE(mTracing->isEnabled());
+ EXPECT_FALSE(threadIsJoinable());
+ EXPECT_EQ(getCommittedTransactions().size(), 0u);
+ EXPECT_EQ(getQueuedTransactions().size(), 0u);
+ EXPECT_EQ(getUsedBufferSize(), 0u);
+ EXPECT_EQ(getStartingStates().size(), 0u);
+ }
+
+ void verifyEntry(const proto::TransactionTraceEntry& actualProto,
+ const std::vector<TransactionState> expectedTransactions,
+ int64_t expectedVsyncId) {
+ EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
+ EXPECT_EQ(actualProto.transactions().size(),
+ static_cast<int32_t>(expectedTransactions.size()));
+ for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
+ EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
+ expectedTransactions[i].originPid);
+ }
+ }
+};
+
+TEST_F(TransactionTracingTest, enable) {
+ EXPECT_FALSE(mTracing->isEnabled());
+ mTracing->enable();
+ EXPECT_TRUE(mTracing->isEnabled());
+ mTracing->disable();
+ verifyDisabledTracingState();
+}
+
+TEST_F(TransactionTracingTest, addTransactions) {
+ mTracing->enable();
+ std::vector<TransactionState> transactions;
+ transactions.reserve(100);
+ for (uint64_t i = 0; i < 100; i++) {
+ TransactionState transaction;
+ transaction.id = i;
+ transaction.originPid = static_cast<int32_t>(i);
+ transactions.emplace_back(transaction);
+ mTracing->addQueuedTransaction(transaction);
+ }
+
+ // Split incoming transactions into two and commit them in reverse order to test out of order
+ // commits.
+ std::vector<TransactionState> firstTransactionSet =
+ std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+ int64_t firstTransactionSetVsyncId = 42;
+ mTracing->addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+
+ int64_t secondTransactionSetVsyncId = 43;
+ std::vector<TransactionState> secondTransactionSet =
+ std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
+ mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+ flush(secondTransactionSetVsyncId);
+
+ proto::TransactionTraceFile proto = writeToProto();
+ EXPECT_EQ(proto.entry().size(), 3);
+ // skip starting entry
+ verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
+ verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
+
+ mTracing->disable();
+ verifyDisabledTracingState();
+}
+
+class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
+protected:
+ void SetUp() override {
+ TransactionTracingTest::SetUp();
+ mTracing->enable();
+ // add layers
+ mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ const sp<IBinder> fakeLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+ 123 /* flags */, -1 /* parentId */);
+ const sp<IBinder> fakeChildLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), mChildLayerId, "child",
+ 456 /* flags */, mParentLayerId);
+
+ // add some layer transaction
+ {
+ TransactionState transaction;
+ transaction.id = 50;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged;
+ layerState.state.z = 42;
+ transaction.states.add(layerState);
+ ComposerState childState;
+ childState.state.surface = fakeChildLayerHandle;
+ childState.state.what = layer_state_t::eLayerChanged;
+ childState.state.z = 43;
+ transaction.states.add(childState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+ flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+ }
+
+ // add transactions that modify the layer state further so we can test that layer state
+ // gets merged
+ {
+ TransactionState transaction;
+ transaction.id = 51;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
+ layerState.state.z = 41;
+ layerState.state.x = 22;
+ transaction.states.add(layerState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+ flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+ }
+
+ // remove child layer
+ mTracing->onLayerRemoved(2);
+ VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
+ queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
+
+ // remove layer
+ mTracing->onLayerRemoved(1);
+ queueAndCommitTransaction(++mVsyncId);
+ }
+
+ void TearDown() override {
+ mTracing->disable();
+ verifyDisabledTracingState();
+ TransactionTracingTest::TearDown();
+ }
+
+ int mParentLayerId = 1;
+ int mChildLayerId = 2;
+ int64_t mVsyncId = 0;
+ int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+ int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+ int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingLayerHandlingTest, addStartingState) {
+ // add transactions until we drop the transaction with the first layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we can still retrieve the layer change from the first entry containing starting
+ // states.
+ EXPECT_GT(proto.entry().size(), 0);
+ EXPECT_GT(proto.entry(0).transactions().size(), 0);
+ EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify starting states are updated correctly
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
+ // add transactions until we drop the transaction which removes the child layer
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify the child layer has been removed from the trace
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we have two starting states
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+
+ // Continue adding transactions until child layer is removed
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto = writeToProto();
+ // verify we still have the parent layer state
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+class TransactionTracingMirrorLayerTest : public TransactionTracingTest {
+protected:
+ void SetUp() override {
+ TransactionTracingTest::SetUp();
+ mTracing->enable();
+ // add layers
+ mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ const sp<IBinder> fakeLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mLayerId, "Test Layer",
+ 123 /* flags */, -1 /* parentId */);
+ const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
+ mTracing->onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
+ mLayerId);
+
+ // add some layer transaction
+ {
+ TransactionState transaction;
+ transaction.id = 50;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged;
+ layerState.state.z = 42;
+ transaction.states.add(layerState);
+ ComposerState mirrorState;
+ mirrorState.state.surface = fakeMirrorLayerHandle;
+ mirrorState.state.what = layer_state_t::eLayerChanged;
+ mirrorState.state.z = 43;
+ transaction.states.add(mirrorState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ mTracing->addCommittedTransactions(transactions, ++mVsyncId);
+ flush(mVsyncId);
+ }
+ }
+
+ void TearDown() override {
+ mTracing->disable();
+ verifyDisabledTracingState();
+ TransactionTracingTest::TearDown();
+ }
+
+ int mLayerId = 5;
+ int mMirrorLayerId = 55;
+ int64_t mVsyncId = 0;
+ int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+ int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+ int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingMirrorLayerTest, canAddMirrorLayers) {
+ proto::TransactionTraceFile proto = writeToProto();
+ // We don't have any starting states since no layer was removed from.
+ EXPECT_EQ(proto.entry().size(), 2);
+ EXPECT_EQ(proto.entry(0).transactions().size(), 0);
+ EXPECT_EQ(proto.entry(0).added_layers().size(), 0);
+
+ // Verify the mirror layer was added
+ EXPECT_EQ(proto.entry(1).transactions().size(), 1);
+ EXPECT_EQ(proto.entry(1).added_layers().size(), 2);
+ EXPECT_EQ(proto.entry(1).added_layers(1).layer_id(), mMirrorLayerId);
+ EXPECT_EQ(proto.entry(1).transactions(0).layer_changes().size(), 2);
+ EXPECT_EQ(proto.entry(1).transactions(0).layer_changes(1).z(), 43);
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index e4f7469..15fea9c 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -27,7 +27,6 @@
#include "TunnelModeEnabledReporter.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
namespace android {
@@ -69,12 +68,12 @@
TestableSurfaceFlinger mFlinger;
Hwc2::mock::Composer* mComposer = nullptr;
- sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
- new TestableTunnelModeEnabledListener();
- sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
- new TunnelModeEnabledReporter();
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
+ sp<TestableTunnelModeEnabledListener>::make();
+
+ sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
+ sp<TunnelModeEnabledReporter>::make();
};
TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
@@ -82,7 +81,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
@@ -100,8 +98,7 @@
sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
LayerMetadata metadata = {}) {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, metadata);
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
return new BufferStateLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
index 41a4d30..21ee071 100644
--- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp
@@ -44,7 +44,7 @@
class WorkDurationTest : public testing::Test {
protected:
WorkDurationTest()
- : mWorkDuration(Fps(60.0f), 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+ : mWorkDuration(60_Hz, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
21'000'000, 1234) {}
~WorkDurationTest() = default;
@@ -56,9 +56,9 @@
* Test cases
*/
TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) {
- mWorkDuration.setRefreshRateFps(Fps(60.0f));
+ mWorkDuration.setRefreshRateFps(60_Hz);
auto currentOffsets = mWorkDuration.getCurrentConfigs();
- auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(60.0f));
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(60_Hz);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sfOffset, 6'166'667);
@@ -81,9 +81,9 @@
}
TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) {
- mWorkDuration.setRefreshRateFps(Fps(90.0f));
+ mWorkDuration.setRefreshRateFps(90_Hz);
auto currentOffsets = mWorkDuration.getCurrentConfigs();
- auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(90.0f));
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(90_Hz);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sfOffset, 611'111);
@@ -106,7 +106,7 @@
}
TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) {
- TestableWorkDuration phaseOffsetsWithDefaultValues(Fps(60.0f), -1, -1, -1, -1, -1, -1, 0);
+ TestableWorkDuration phaseOffsetsWithDefaultValues(60_Hz, -1, -1, -1, -1, -1, -1, 0);
auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) {
EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
@@ -138,12 +138,12 @@
validateOffsets(offsets, std::chrono::nanoseconds(refreshRate.getPeriodNsecs()));
};
- testForRefreshRate(Fps(90.0f));
- testForRefreshRate(Fps(60.0f));
+ testForRefreshRate(90_Hz);
+ testForRefreshRate(60_Hz);
}
TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) {
- auto offsets = mWorkDuration.getConfigsForRefreshRate(Fps(14.7f));
+ auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7_Hz);
EXPECT_EQ(offsets.late.sfOffset, 57'527'208);
EXPECT_EQ(offsets.late.appOffset, 37'027'208);
@@ -181,13 +181,12 @@
std::optional<nsecs_t> highFpsEarlyAppOffsetNs,
std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs,
nsecs_t thresholdForNextVsync, nsecs_t hwcMinWorkDuration)
- : impl::PhaseOffsets(Fps(60.0f), vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs,
- earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs,
- earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs,
- highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs,
- highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs,
- highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync,
- hwcMinWorkDuration) {}
+ : impl::PhaseOffsets(60_Hz, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs, earlySfOffsetNs,
+ earlyGpuSfOffsetNs, earlyAppOffsetNs, earlyGpuAppOffsetNs,
+ highFpsVsyncPhaseOffsetNs, highFpsSfVSyncPhaseOffsetNs,
+ highFpsEarlySfOffsetNs, highFpsEarlyGpuSfOffsetNs,
+ highFpsEarlyAppOffsetNs, highFpsEarlyGpuAppOffsetNs,
+ thresholdForNextVsync, hwcMinWorkDuration) {}
};
class PhaseOffsetsTest : public testing::Test {
@@ -201,7 +200,7 @@
};
TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) {
- auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(14.7f));
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7_Hz);
EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -223,7 +222,7 @@
}
TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) {
- auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60_Hz);
EXPECT_EQ(offsets.late.sfOffset, 6'000'000);
EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -245,7 +244,7 @@
}
TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) {
- auto offsets = mPhaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+ auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90_Hz);
EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
EXPECT_EQ(offsets.late.appOffset, 2'000'000);
@@ -269,7 +268,7 @@
TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) {
TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
1'000'000, {}, {}, {}, {}, 10'000'000, 1234};
- auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(60.0f));
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(60_Hz);
EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
EXPECT_EQ(offsets.late.appOffset, 1'000'000);
@@ -293,7 +292,7 @@
TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) {
TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000,
1'000'000, {}, {}, {}, {}, 10'000'000, 1234};
- auto offsets = phaseOffsets.getConfigsForRefreshRate(Fps(90.0f));
+ auto offsets = phaseOffsets.getConfigsForRefreshRate(90_Hz);
EXPECT_EQ(offsets.late.sfOffset, 1'000'000);
EXPECT_EQ(offsets.late.appOffset, 2'000'000);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 7de1872..20d41e6 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#undef LOG_TAG
#define LOG_TAG "MockComposer"
#include "mock/DisplayHardware/MockComposer.h"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 1ba3c0f..c3250d5 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -61,7 +61,8 @@
MOCK_METHOD2(destroyLayer, Error(Display, Layer));
MOCK_METHOD2(getActiveConfig, Error(Display, Config*));
MOCK_METHOD3(getChangedCompositionTypes,
- Error(Display, std::vector<Layer>*, std::vector<IComposerClient::Composition>*));
+ Error(Display, std::vector<Layer>*,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>*));
MOCK_METHOD2(getColorModes, Error(Display, std::vector<ColorMode>*));
MOCK_METHOD4(getDisplayAttribute,
Error(Display, Config config, IComposerClient::Attribute, int32_t*));
@@ -95,7 +96,8 @@
Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
MOCK_METHOD3(setLayerColor, Error(Display, Layer, const IComposerClient::Color&));
- MOCK_METHOD3(setLayerCompositionType, Error(Display, Layer, IComposerClient::Composition));
+ MOCK_METHOD3(setLayerCompositionType,
+ Error(Display, Layer, aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD3(setLayerDataspace, Error(Display, Layer, Dataspace));
MOCK_METHOD3(setLayerPerFrameMetadata,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadata>&));
@@ -136,7 +138,9 @@
const std::vector<uint8_t>&));
MOCK_METHOD1(getLayerGenericMetadataKeys,
V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
- MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
+ MOCK_METHOD3(getClientTargetProperty,
+ Error(Display, IComposerClient::ClientTargetProperty*, float*));
+ MOCK_METHOD3(setLayerWhitePointNits, Error(Display, Layer, float));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index c3919d9..d4fefee 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -30,8 +30,7 @@
MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override));
MOCK_METHOD(bool, isConnected, (), (const, override));
MOCK_METHOD(void, setConnected, (bool), (override));
- MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (),
- (const, override));
+ MOCK_METHOD(bool, hasCapability, (hal::DisplayCapability), (const, override));
MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
@@ -39,7 +38,9 @@
MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
(override));
MOCK_METHOD(hal::Error, getChangedCompositionTypes,
- ((std::unordered_map<Layer *, hal::Composition> *)), (override));
+ ((std::unordered_map<Layer *,
+ aidl::android::hardware::graphics::composer3::Composition> *)),
+ (override));
MOCK_METHOD(hal::Error, getColorModes, (std::vector<hal::ColorMode> *), (const, override));
MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (), (const, override));
MOCK_METHOD(hal::Error, getRenderIntents, (hal::ColorMode, std::vector<hal::RenderIntent> *),
@@ -86,7 +87,8 @@
MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
(const, override));
MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
- MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override));
+ MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *, float *),
+ (override));
};
class Layer : public HWC2::Layer {
@@ -103,7 +105,8 @@
MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
MOCK_METHOD(hal::Error, setColor, (hal::Color), (override));
- MOCK_METHOD(hal::Error, setCompositionType, (hal::Composition), (override));
+ MOCK_METHOD(hal::Error, setCompositionType,
+ (aidl::android::hardware::graphics::composer3::Composition), (override));
MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override));
MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &),
(override));
@@ -117,6 +120,7 @@
MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
MOCK_METHOD(hal::Error, setLayerGenericMetadata,
(const std::string &, bool, const std::vector<uint8_t> &), (override));
+ MOCK_METHOD(hal::Error, setWhitePointNits, (float whitePointNits), (override));
};
} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 159bdf1..23b849a 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -27,11 +27,21 @@
PowerAdvisor();
~PowerAdvisor() override;
- MOCK_METHOD0(init, void());
- MOCK_METHOD0(onBootFinished, void());
- MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
- MOCK_METHOD0(isUsingExpensiveRendering, bool());
- MOCK_METHOD0(notifyDisplayUpdateImminent, void());
+ MOCK_METHOD(void, init, (), (override));
+ MOCK_METHOD(void, onBootFinished, (), (override));
+ MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
+ (override));
+ MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
+ MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
+ MOCK_METHOD(bool, usePowerHintSession, (), (override));
+ MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
+ MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
+ MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
+ (override));
+ MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
+ (override));
+ MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
};
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index ba2e4db..8b48e1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,7 @@
class MockLayer : public Layer {
public:
MockLayer(SurfaceFlinger* flinger, std::string name)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
deleted file mode 100644
index 5fb06fd..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 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 "mock/MockMessageQueue.h"
-
-namespace android::mock {
-
-MessageQueue::MessageQueue() {
- ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
- // Execute task to prevent broken promise exception on destruction.
- handler->handleMessage(Message());
- });
-}
-
-MessageQueue::~MessageQueue() = default;
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
deleted file mode 100644
index 0e7b320..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 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 "FrameTimeline.h"
-#include "Scheduler/EventThread.h"
-#include "Scheduler/MessageQueue.h"
-
-namespace android::mock {
-
-class MessageQueue : public android::MessageQueue {
-public:
- MessageQueue();
- ~MessageQueue() override;
-
- MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
- MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
- MOCK_METHOD0(waitMessage, void());
- MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
- MOCK_METHOD0(invalidate, void());
- MOCK_METHOD0(refresh, void());
- MOCK_METHOD3(initVsync,
- void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
- std::chrono::nanoseconds));
- MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
- MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>());
-};
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index ab19886..849e308 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -20,25 +20,22 @@
#include "Scheduler/Scheduler.h"
-namespace android::mock {
+namespace android::scheduler::mock {
struct SchedulerCallback final : ISchedulerCallback {
- MOCK_METHOD1(setVsyncEnabled, void(bool));
- MOCK_METHOD2(changeRefreshRate,
- void(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent));
- MOCK_METHOD0(repaintEverythingForHWC, void());
- MOCK_METHOD1(kernelTimerChanged, void(bool));
- MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
+ MOCK_METHOD(void, scheduleComposite, (FrameHint), (override));
+ MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
+ MOCK_METHOD(void, changeRefreshRate, (const RefreshRate&, DisplayModeEvent), (override));
+ MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
+ MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
+ void scheduleComposite(FrameHint) override {}
void setVsyncEnabled(bool) override {}
- void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent) override {}
- void repaintEverythingForHWC() override {}
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() {}
};
-} // namespace android::mock
+} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 5aebd2f..0a69b56 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,19 +41,21 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD6(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t, int32_t));
+ MOCK_METHOD(void, setPostTime,
+ (int32_t, uint64_t, const std::string&, uid_t, nsecs_t, GameMode), (override));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD7(setPresentTime,
- void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote,
- int32_t));
- MOCK_METHOD7(setPresentFence,
- void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
- SetFrameRateVote, int32_t));
+ MOCK_METHOD(void, setPresentTime,
+ (int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote, GameMode),
+ (override));
+ MOCK_METHOD(void, setPresentFence,
+ (int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+ SetFrameRateVote, GameMode),
+ (override));
MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 94d9966..314f681 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -27,7 +27,7 @@
VsyncController();
~VsyncController() override;
- MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index a375808..63ecaec 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -402,12 +402,21 @@
auto primitiveIdx = static_cast<size_t>(primitive);
if (primitiveIdx >= durations.size()) {
// Safety check, should not happen if enum_range is correct.
+ ALOGE("Supported primitive %zu is outside range [0,%zu), skipping load duration",
+ primitiveIdx, durations.size());
continue;
}
int32_t duration = 0;
- auto status = getHal()->getPrimitiveDuration(primitive, &duration);
- if (!status.isOk()) {
- return HalResult<std::vector<milliseconds>>::failed(status.toString8().c_str());
+ auto result = getHal()->getPrimitiveDuration(primitive, &duration);
+ auto halResult = HalResult<int32_t>::fromStatus(result, duration);
+ if (halResult.isUnsupported()) {
+ // Should not happen, supported primitives should always support requesting duration.
+ ALOGE("Supported primitive %zu returned unsupported for getPrimitiveDuration",
+ primitiveIdx);
+ }
+ if (halResult.isFailed()) {
+ // Fail entire request if one request has failed.
+ return HalResult<std::vector<milliseconds>>::failed(result.toString8().c_str());
}
durations[primitiveIdx] = milliseconds(duration);
}
diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp
index 6bf6581..0df0bfa 100644
--- a/services/vibratorservice/VibratorManagerHalController.cpp
+++ b/services/vibratorservice/VibratorManagerHalController.cpp
@@ -45,7 +45,7 @@
template <typename T>
HalResult<T> ManagerHalController::processHalResult(HalResult<T> result, const char* functionName) {
if (result.isFailed()) {
- ALOGE("%s failed: %s", functionName, result.errorMessage());
+ ALOGE("VibratorManager HAL %s failed: %s", functionName, result.errorMessage());
std::lock_guard<std::mutex> lock(mConnectedHalMutex);
mConnectedHal->tryReconnect();
}
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 6c31e2b..6b73d17 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -103,7 +103,7 @@
for (int i = 0; i < MAX_RETRIES; i++) {
T result = halFn(hal.get());
- if (result.checkAndLogFailure(functionName)) {
+ if (result.isFailedLogged(functionName)) {
tryReconnect();
} else {
return result;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 68d6647..d2cc9ad 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -69,9 +69,9 @@
bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
bool isUnsupported() const { return mUnsupported; }
const char* errorMessage() const { return mErrorMessage.c_str(); }
- bool checkAndLogFailure(const char* functionName) const {
+ bool isFailedLogged(const char* functionNameForLogging) const {
if (isFailed()) {
- ALOGE("%s failed: %s", functionName, errorMessage());
+ ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
return true;
}
return false;
@@ -107,9 +107,9 @@
bool isFailed() const { return !mUnsupported && mFailed; }
bool isUnsupported() const { return mUnsupported; }
const char* errorMessage() const { return mErrorMessage.c_str(); }
- bool checkAndLogFailure(const char* functionName) const {
+ bool isFailedLogged(const char* functionNameForLogging) const {
if (isFailed()) {
- ALOGE("%s failed: %s", functionName, errorMessage());
+ ALOGE("Vibrator HAL %s failed: %s", functionNameForLogging, errorMessage());
return true;
}
return false;
@@ -192,21 +192,21 @@
const HalResult<float> qFactor;
const HalResult<std::vector<float>> maxAmplitudes;
- bool checkAndLogFailure(const char*) const {
- return capabilities.checkAndLogFailure("getCapabilities") ||
- supportedEffects.checkAndLogFailure("getSupportedEffects") ||
- supportedBraking.checkAndLogFailure("getSupportedBraking") ||
- supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
- primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
- primitiveDelayMax.checkAndLogFailure("getPrimitiveDelayMax") ||
- pwlePrimitiveDurationMax.checkAndLogFailure("getPwlePrimitiveDurationMax") ||
- compositionSizeMax.checkAndLogFailure("getCompositionSizeMax") ||
- pwleSizeMax.checkAndLogFailure("getPwleSizeMax") ||
- minFrequency.checkAndLogFailure("getMinFrequency") ||
- resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
- frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
- qFactor.checkAndLogFailure("getQFactor") ||
- maxAmplitudes.checkAndLogFailure("getMaxAmplitudes");
+ bool isFailedLogged(const char*) const {
+ return capabilities.isFailedLogged("getCapabilities") ||
+ supportedEffects.isFailedLogged("getSupportedEffects") ||
+ supportedBraking.isFailedLogged("getSupportedBraking") ||
+ supportedPrimitives.isFailedLogged("getSupportedPrimitives") ||
+ primitiveDurations.isFailedLogged("getPrimitiveDuration") ||
+ primitiveDelayMax.isFailedLogged("getPrimitiveDelayMax") ||
+ pwlePrimitiveDurationMax.isFailedLogged("getPwlePrimitiveDurationMax") ||
+ compositionSizeMax.isFailedLogged("getCompositionSizeMax") ||
+ pwleSizeMax.isFailedLogged("getPwleSizeMax") ||
+ minFrequency.isFailedLogged("getMinFrequency") ||
+ resonantFrequency.isFailedLogged("getResonantFrequency") ||
+ frequencyResolution.isFailedLogged("getFrequencyResolution") ||
+ qFactor.isFailedLogged("getQFactor") ||
+ maxAmplitudes.isFailedLogged("getMaxAmplitudes");
}
};
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index 26052fb..33401d2 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -560,6 +560,7 @@
}
static const char* const known_non_device_names[] = {
+ "vkAcquireDrmDisplayEXT",
"vkCreateAndroidSurfaceKHR",
"vkCreateDebugReportCallbackEXT",
"vkCreateDebugUtilsMessengerEXT",
@@ -581,6 +582,7 @@
"vkEnumeratePhysicalDevices",
"vkGetDisplayModeProperties2KHR",
"vkGetDisplayPlaneCapabilities2KHR",
+ "vkGetDrmDisplayEXT",
"vkGetInstanceProcAddr",
"vkGetPhysicalDeviceCalibrateableTimeDomainsEXT",
"vkGetPhysicalDeviceDisplayPlaneProperties2KHR",
@@ -624,6 +626,8 @@
"vkGetPhysicalDeviceSurfacePresentModesKHR",
"vkGetPhysicalDeviceSurfaceSupportKHR",
"vkGetPhysicalDeviceToolPropertiesEXT",
+ "vkGetPhysicalDeviceVideoCapabilitiesKHR",
+ "vkGetPhysicalDeviceVideoFormatPropertiesKHR",
"vkSubmitDebugUtilsMessageEXT",
};
// clang-format on
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index e5b1587..416c0bc 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -78,8 +78,7 @@
VkDebugReportCallbackEXT driver_handle;
};
- // TODO(b/143295577): use std::shared_mutex when available in libc++
- mutable std::shared_timed_mutex rwmutex_;
+ mutable std::shared_mutex rwmutex_;
Node head_;
};
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index d7fdab5..cf774fd 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -979,6 +979,8 @@
void QueryPresentationProperties(
VkPhysicalDevice physicalDevice,
VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) {
+ ATRACE_CALL();
+
// Request the android-specific presentation properties via GPDP2
VkPhysicalDeviceProperties2 properties = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
@@ -994,7 +996,17 @@
presentation_properties->pNext = nullptr;
presentation_properties->sharedImage = VK_FALSE;
- GetPhysicalDeviceProperties2(physicalDevice, &properties);
+ const auto& driver = GetData(physicalDevice).driver;
+
+ if (driver.GetPhysicalDeviceProperties2) {
+ // >= 1.1 driver, supports core GPDP2 entrypoint.
+ driver.GetPhysicalDeviceProperties2(physicalDevice, &properties);
+ } else if (driver.GetPhysicalDeviceProperties2KHR) {
+ // Old driver, but may support presentation properties
+ // if we have the GPDP2 extension. Otherwise, no presentation
+ // properties supported.
+ driver.GetPhysicalDeviceProperties2KHR(physicalDevice, &properties);
+ }
}
VkResult EnumerateDeviceExtensionProperties(
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a320411..eb4befd 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -23,6 +23,8 @@
#include <sync/sync.h>
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
+#include <ui/DebugUtils.h>
+#include <ui/PixelFormat.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -462,21 +464,24 @@
*count = num_copied;
}
-android_pixel_format GetNativePixelFormat(VkFormat format) {
- android_pixel_format native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+android::PixelFormat GetNativePixelFormat(VkFormat format) {
+ android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888;
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SRGB:
- native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+ native_format = android::PIXEL_FORMAT_RGBA_8888;
break;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
- native_format = HAL_PIXEL_FORMAT_RGB_565;
+ native_format = android::PIXEL_FORMAT_RGB_565;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT:
- native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ native_format = android::PIXEL_FORMAT_RGBA_FP16;
break;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
- native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ native_format = android::PIXEL_FORMAT_RGBA_1010102;
+ break;
+ case VK_FORMAT_R8_UNORM:
+ native_format = android::PIXEL_FORMAT_R_8;
break;
default:
ALOGV("unsupported swapchain format %d", format);
@@ -696,10 +701,10 @@
if (err) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- ALOGV("wide_color_support is: %d", wide_color_support);
- wide_color_support =
- wide_color_support &&
+ bool swapchain_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
+ ALOGV("wide_color_support is: %d", wide_color_support);
+ wide_color_support = wide_color_support && swapchain_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
@@ -714,6 +719,11 @@
{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+ if (swapchain_ext) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
+ }
+
if (wide_color_support) {
all_formats.emplace_back(VkSurfaceFormatKHR{
VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
@@ -753,6 +763,13 @@
}
}
+ desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ }
+
VkResult result = VK_SUCCESS;
if (formats) {
uint32_t transfer_count = all_formats.size();
@@ -1029,7 +1046,7 @@
if (!allocator)
allocator = &GetData(device).allocator;
- android_pixel_format native_pixel_format =
+ android::PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
android_dataspace native_dataspace =
GetNativeDataspace(create_info->imageColorSpace);
@@ -1126,8 +1143,8 @@
err = native_window_set_buffers_format(window, native_pixel_format);
if (err != android::OK) {
- ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
- native_pixel_format, strerror(-err), err);
+ ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)",
+ decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = native_window_set_buffers_data_space(window, native_dataspace);
@@ -1217,12 +1234,6 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
- // In shared mode the num_images must be one regardless of how many
- // buffers were allocated for the buffer queue.
- if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) {
- num_images = 1;
- }
-
int32_t legacy_usage = 0;
if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index 72fd4fb..4176509 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -33,6 +33,7 @@
'VK_EXT_metal_surface',
'VK_FUCHSIA_imagepipe_surface',
'VK_GGP_stream_descriptor_surface',
+ 'VK_HUAWEI_subpass_shading',
'VK_KHR_display',
'VK_KHR_display_swapchain',
'VK_KHR_external_fence_win32',
@@ -47,11 +48,13 @@
'VK_MVK_ios_surface',
'VK_MVK_macos_surface',
'VK_NN_vi_surface',
+ 'VK_NV_acquire_winrt_display',
'VK_NV_cooperative_matrix',
'VK_NV_coverage_reduction_mode',
'VK_NV_external_memory_win32',
'VK_NV_win32_keyed_mutex',
'VK_NVX_image_view_handle',
+ 'VK_QNX_screen_surface',
]
# Extensions having functions exported by the loader.