diff options
472 files changed, 30210 insertions, 14986 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 2d780f52f7..08218b8d69 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,3 +1,18 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Only turn on clang-format check for the following subfolders. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + libs/graphicsenv/ + libs/gui/ + libs/renderengine/ + libs/ui/ + libs/vr/ + services/bufferhub/ + services/surfaceflinger/ + services/vr/ + [Hook Scripts] owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" installd_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/installd/" diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp index 75855cfee1..ded0ed35cb 100644 --- a/cmds/bugreportz/bugreportz.cpp +++ b/cmds/bugreportz/bugreportz.cpp @@ -55,7 +55,7 @@ int bugreportz(int s, bool show_progress) { errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); - break; + return EXIT_FAILURE; } // Writes line by line. @@ -71,8 +71,5 @@ int bugreportz(int s, bool show_progress) { // Process final line, in case it didn't finish with newline write_line(line, show_progress); - if (close(s) == -1) { - fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); - } return EXIT_SUCCESS; } diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h index 304e4b3dc3..7af289b2be 100644 --- a/cmds/bugreportz/bugreportz.h +++ b/cmds/bugreportz/bugreportz.h @@ -16,6 +16,7 @@ #define BUGREPORTZ_H // Calls dumpstate using the given socket and output its result to stdout. +// Ownership of the socket is not transferred. int bugreportz(int s, bool show_progress); #endif // BUGREPORTZ_H diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp index a3ae1ffa4d..74a95b0b57 100644 --- a/cmds/bugreportz/main.cpp +++ b/cmds/bugreportz/main.cpp @@ -82,7 +82,7 @@ int main(int argc, char* argv[]) { if (s == -1) { printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno)); - return EXIT_SUCCESS; + return EXIT_FAILURE; } // Set a timeout so that if nothing is read in 10 minutes, we'll stop @@ -92,8 +92,16 @@ int main(int argc, char* argv[]) { tv.tv_sec = 10 * 60; tv.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { - fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno)); + fprintf(stderr, + "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n", + strerror(errno)); } - bugreportz(s, show_progress); + int ret = bugreportz(s, show_progress); + + if (close(s) == -1) { + fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); + ret = EXIT_FAILURE; + } + return ret; } diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 423853175b..0616adda61 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -103,12 +103,12 @@ public: } if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); - security_context_t tmp = NULL; + security_context_t tmp = nullptr; getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); if (checkWrite) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), - "file", "write", NULL); + "file", "write", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux write check!"); @@ -122,7 +122,7 @@ public: } if (checkRead) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), - "file", "read", NULL); + "file", "read", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux read check!"); @@ -174,7 +174,7 @@ int main(int argc, char* const argv[]) #endif sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); - if (sm == NULL) { + if (sm == nullptr) { ALOGW("Unable to get default service manager!"); aerr << "cmd: Unable to get default service manager!" << endl; return 20; @@ -192,7 +192,7 @@ int main(int argc, char* const argv[]) for (size_t i=0; i<services.size(); i++) { sp<IBinder> service = sm->checkService(services[i]); - if (service != NULL) { + if (service != nullptr) { aout << " " << services[i] << endl; } } @@ -205,7 +205,7 @@ int main(int argc, char* const argv[]) } String16 cmd = String16(argv[1]); sp<IBinder> service = sm->checkService(cmd); - if (service == NULL) { + if (service == nullptr) { ALOGW("Can't find service %s", argv[1]); aerr << "cmd: Can't find service: " << argv[1] << endl; return 20; diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 5412d4df7b..9bfd710309 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -65,7 +65,7 @@ static void usage() { " -l: only list services, do not dump them\n" " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" - " --proto: filter services that support dumping data in proto format. Dumps" + " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" " --priority LEVEL: filter services based on specified priority\n" " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 8f60881bfe..3ada15398c 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -371,8 +371,8 @@ TEST_F(DumpsysTest, PassAllFlagsToNormalServices) { IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); - ExpectDumpWithArgs("Locksmith", {"-a", "--dump-priority", "NORMAL"}, "dump1"); - ExpectDumpWithArgs("Valet", {"-a", "--dump-priority", "NORMAL"}, "dump2"); + ExpectDumpWithArgs("Locksmith", {"--dump-priority", "NORMAL", "-a"}, "dump1"); + ExpectDumpWithArgs("Valet", {"--dump-priority", "NORMAL", "-a"}, "dump2"); CallMain({"--priority", "NORMAL"}); diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 2439dff54e..81055d854e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -94,15 +94,6 @@ static constexpr int kVerityPageSize = 4096; static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; -// NOTE: keep in sync with Installer -static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; -static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; -static constexpr int FLAG_USE_QUOTA = 1 << 12; -static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; -static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; -static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15; -static constexpr int FLAG_FORCE = 1 << 16; - namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -554,6 +545,35 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st remove_path_xattr(path, kXattrInodeCodeCache); } } + + auto extPath = findDataMediaPath(uuid, userId); + if (flags & FLAG_CLEAR_CACHE_ONLY) { + // Clear only cached data from shared storage + path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + // No code cache on shared storage + } else { + // Clear everything on shared storage + path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } } if (flags & FLAG_STORAGE_DE) { std::string suffix = ""; @@ -622,6 +642,24 @@ binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std:: if (delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } + + auto extPath = findDataMediaPath(uuid, userId); + path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); @@ -1413,6 +1451,8 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri } ATRACE_BEGIN("external"); + auto sandboxPath = create_data_media_package_path(uuid_, userId, "sandbox", pkgname); + calculate_tree_size(sandboxPath, &extStats.dataSize); auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname); collectManualStats(extPath, &extStats); auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 91e20b75ae..89b08e5ba8 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -102,4 +102,17 @@ interface IInstalld { boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); + + const int FLAG_STORAGE_DE = 0x1; + const int FLAG_STORAGE_CE = 0x2; + + const int FLAG_CLEAR_CACHE_ONLY = 0x10; + const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; + + const int FLAG_FREE_CACHE_V2 = 0x100; + const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; + const int FLAG_FREE_CACHE_NOOP = 0x400; + + const int FLAG_USE_QUOTA = 0x1000; + const int FLAG_FORCE = 0x2000; } diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 2d58515b11..db0907017c 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -40,8 +40,8 @@ constexpr int64_t kMbInBytes = 1024 * kKbInBytes; constexpr int64_t kGbInBytes = 1024 * kMbInBytes; constexpr int64_t kTbInBytes = 1024 * kGbInBytes; -static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; -static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; +#define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2 +#define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index a5af5d740e..9dad99563d 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -37,7 +37,7 @@ namespace installd { constexpr const char* kTestUuid = "TEST"; -static constexpr int FLAG_FORCE = 1 << 16; +#define FLAG_FORCE InstalldNativeService::FLAG_FORCE int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index d776682d74..79cd6b5e1f 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -274,6 +274,7 @@ int svcmgr_handler(struct binder_state *bs, // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); + bio_get_uint32(msg); // Ignore worksource header. s = bio_get_string16(msg, &len); if (s == NULL) { return -1; diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 4d93cb4c73..152ac28ba4 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -13,5 +13,6 @@ service servicemanager /system/bin/servicemanager onrestart restart cameraserver onrestart restart keystore onrestart restart gatekeeperd + onrestart restart thermalservice writepid /dev/cpuset/system-background/tasks shutdown critical diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index 0bc08a91ab..68ddeb02d6 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -30,14 +30,13 @@ message Transaction { message SurfaceChange { required int32 id = 1; - + reserved 7; oneof SurfaceChange { PositionChange position = 2; SizeChange size = 3; AlphaChange alpha = 4; LayerChange layer = 5; CropChange crop = 6; - FinalCropChange final_crop = 7; MatrixChange matrix = 8; OverrideScalingModeChange override_scaling_mode = 9; TransparentRegionHintChange transparent_region_hint = 10; @@ -71,10 +70,6 @@ message CropChange { required Rectangle rectangle = 1; } -message FinalCropChange { - required Rectangle rectangle = 1; -} - message MatrixChange { required float dsdx = 1; required float dtdx = 2; diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 4140f40888..66025468ea 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -388,9 +388,6 @@ status_t Replayer::doSurfaceTransaction( case SurfaceChange::SurfaceChangeCase::kMatrix: setMatrix(transaction, change.id(), change.matrix()); break; - case SurfaceChange::SurfaceChangeCase::kFinalCrop: - setFinalCrop(transaction, change.id(), change.final_crop()); - break; case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: setOverrideScalingMode(transaction, change.id(), change.override_scaling_mode()); @@ -489,17 +486,7 @@ void Replayer::setCrop(SurfaceComposerClient::Transaction& t, Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); - t.setCrop(mLayers[id], r); -} - -void Replayer::setFinalCrop(SurfaceComposerClient::Transaction& t, - layer_id id, const FinalCropChange& fcc) { - ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id, - fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), - fcc.rectangle().bottom()); - Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), - fcc.rectangle().bottom()); - t.setFinalCrop(mLayers[id], r); + t.setCrop_legacy(mLayers[id], r); } void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, @@ -570,7 +557,7 @@ void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t, auto handle = mLayers[dtc.layer_id()]->getHandle(); - t.deferTransactionUntil(mLayers[id], handle, dtc.frame_number()); + t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number()); } void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index 295403eace..68390d33ae 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -92,8 +92,6 @@ class Replayer { layer_id id, const LayerChange& lc); void setCrop(SurfaceComposerClient::Transaction& t, layer_id id, const CropChange& cc); - void setFinalCrop(SurfaceComposerClient::Transaction& t, - layer_id id, const FinalCropChange& fcc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, diff --git a/data/etc/android.hardware.face.xml b/data/etc/android.hardware.face.xml new file mode 100644 index 0000000000..abd23fb1c4 --- /dev/null +++ b/data/etc/android.hardware.face.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This is the standard set of features for a biometric face authentication sensor. --> +<permissions> + <feature name="android.hardware.face" /> +</permissions> diff --git a/include/android/input.h b/include/android/input.h index 681090188f..cfade6c806 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -83,7 +83,7 @@ enum { }; /** - * Meta key / modifer state. + * Meta key / modifier state. */ enum { /** No meta keys are pressed. */ diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h new file mode 100644 index 0000000000..c54151132a --- /dev/null +++ b/include/android/system_fonts.h @@ -0,0 +1,412 @@ +/* + * 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. + */ + +/** + * @file system_fonts.h + * @brief Provides the system font configurations. + * + * These APIs provides the list of system installed font files with additional metadata about the + * font. + * + * The ASystemFontIterator_open method will give you an iterator which can iterate all system + * installed font files as shown in the following example. + * + * <code> + * ASystemFontIterator* iterator = ASystemFontIterator_open(); + * ASystemFont* font = NULL; + * + * while ((font = ASystemFontIterator_next(iterator)) != nullptr) { + * // Look if the font is your desired one. + * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font) + * && ASystemFont_getLocale(font) == NULL) { + * break; + * } + * ASystemFont_close(font); + * } + * ASystemFontIterator_close(iterator); + * + * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY); + * int collectionIndex = ASystemFont_getCollectionINdex(font); + * std::vector<std::pair<uint32_t, float>> variationSettings; + * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) { + * variationSettings.push_back(std::make_pair( + * ASystemFont_getAxisTag(font, i), + * ASystemFont_getAxisValue(font, i))); + * } + * ASystemFont_close(font); + * + * // Use this font for your text rendering engine. + * + * </code> + * + * Available since API level 29. + */ + +#ifndef ANDROID_SYSTEM_FONTS_H +#define ANDROID_SYSTEM_FONTS_H + +#include <stdbool.h> +#include <stddef.h> +#include <sys/cdefs.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +enum { + /** The minimum value fot the font weight value. */ + ASYSTEM_FONT_WEIGHT_MIN = 0, + + /** A font weight value for the thin weight. */ + ASYSTEM_FONT_WEIGHT_THIN = 100, + + /** A font weight value for the extra-light weight. */ + ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT = 200, + + /** A font weight value for the light weight. */ + ASYSTEM_FONT_WEIGHT_LIGHT = 300, + + /** A font weight value for the normal weight. */ + ASYSTEM_FONT_WEIGHT_NORMAL = 400, + + /** A font weight value for the medium weight. */ + ASYSTEM_FONT_WEIGHT_MEDIUM = 500, + + /** A font weight value for the semi-bold weight. */ + ASYSTEM_FONT_WEIGHT_SEMI_BOLD = 600, + + /** A font weight value for the bold weight. */ + ASYSTEM_FONT_WEIGHT_BOLD = 700, + + /** A font weight value for the extra-bold weight. */ + ASYSTEM_FONT_WEIGHT_EXTRA_BOLD = 800, + + /** A font weight value for the black weight. */ + ASYSTEM_FONT_WEIGHT_BLACK = 900, + + /** The maximum value for the font weight value. */ + ASYSTEM_FONT_WEIGHT_MAX = 1000 +}; + +/** + * ASystemFontIterator provides access to the system font configuration. + * + * ASystemFontIterator is an iterator for all available system font settings. + * This iterator is not a thread-safe object. Do not pass this iterator to other threads. + */ +struct ASystemFontIterator; + +/** + * ASystemFont provides information of the single system font configuration. + */ +struct ASystemFont; + +/** + * Create a system font iterator. + * + * Use ASystemFont_close() to close the iterator. + * + * \return a pointer for a newly allocated iterator, nullptr on failure. + */ +ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); + +/** + * Close an opened system font iterator, freeing any related resources. + * + * \param a pointer of an iterator for the system fonts. Do nothing if NULL is passed. + */ +void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); + +/** + * Move to the next system font. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a font. If no more font is available, returns nullptr. You need to release the returned + * font by ASystemFont_close when it is no longer needed. + */ +ASystemFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29); + +/** + * Close an ASystemFont returned by ASystemFontIterator_next. + * + * \param font a font returned by ASystemFontIterator_next or ASystemFont_matchFamilyStyleCharacter. + * Do nothing if NULL is passed. + */ +void ASystemFont_close(ASystemFont* _Nullable font) __INTRODUCED_IN(29); + + +/** + * Select the best font from given parameters. + * + * Only generic font families are supported. + * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families) + * + * Even if no font can render the given text, this function will return a non-null result for + * drawing Tofu character. + * + * Examples: + * <code> + * // Simple font query for the ASCII character. + * std::vector<uint16_t> text = { 'A' }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a valid font file. + * + * // Querying font for CJK characters + * std::vector<uint16_t> text = { 0x9AA8 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "zh-CN,ja-JP", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Simplified Chinese font. + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "ja-JP,zh-CN", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Japanese font. + * + * // Querying font for text/color emoji + * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 8 and the font will points a color emoji font. + * + * // Mixture of multiple script of characters. + * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character. + * std::vector<uint16_t> text = { 0x05D0, 0x0E01 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Hebrew font. + * </code> + * + * \param familyName a null character terminated font family name + * \param weight a font weight value. Only from 0 to 1000 value is valid + * \param italic true if italic, otherwise false. + * \param languageTags a null character terminated comma separated IETF BCP47 compliant language + * tags. + * \param text a UTF-16 encoded text buffer to be rendered. + * \param textLength a length of the given text buffer. + * \param runLengthOut if not null, the font run length will be filled. + * \return a font to be used for given text and params. You need to release the returned font by + * ASystemFont_close when it is no longer needed. + */ +ASystemFont* _Nonnull ASystemFont_matchFamilyStyleCharacter( + const char* _Nonnull familyName, + uint16_t weight, + bool italic, + const char* _Nonnull languageTags, + const uint16_t* _Nonnull text, + uint32_t textLength, + uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29); + +/** + * Return an absolute path to the current font file. + * + * Here is a list of font formats returned by this method: + * <ul> + * <li>OpenType</li> + * <li>OpenType Font Collection</li> + * <li>TrueType</li> + * <li>TrueType Collection</li> + * </ul> + * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc. + * + * The font file returned is guaranteed to be opend with O_RDONLY. + * Note that the returned pointer is valid until ASystemFont_close() is called for the given font. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a string of the font file path. + */ +const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a weight value associated with the current font. + * + * The weight values are positive and less than or equal to 1000. + * Here are pairs of the common names and their values. + * <p> + * <table> + * <thead> + * <tr> + * <th align="center">Value</th> + * <th align="center">Name</th> + * <th align="center">NDK Definition</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td align="center">100</td> + * <td align="center">Thin</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_THIN}</td> + * </tr> + * <tr> + * <td align="center">200</td> + * <td align="center">Extra Light (Ultra Light)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">300</td> + * <td align="center">Light</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">400</td> + * <td align="center">Normal (Regular)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_NORMAL}</td> + * </tr> + * <tr> + * <td align="center">500</td> + * <td align="center">Medium</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_MEDIUM}</td> + * </tr> + * <tr> + * <td align="center">600</td> + * <td align="center">Semi Bold (Demi Bold)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_SEMI_BOLD}</td> + * </tr> + * <tr> + * <td align="center">700</td> + * <td align="center">Bold</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BOLD}</td> + * </tr> + * <tr> + * <td align="center">800</td> + * <td align="center">Extra Bold (Ultra Bold)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_BOLD}</td> + * </tr> + * <tr> + * <td align="center">900</td> + * <td align="center">Black (Heavy)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td> + * </tr> + * </tbody> + * </p> + * Note that the weight value may fall in between above values, e.g. 250 weight. + * + * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. + */ +uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return true if the current font is italic, otherwise returns false. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return true if italic, otherwise false. + */ +bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a IETF BCP47 compliant language tag associated with the current font. + * + * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)") + * + * Note that the returned pointer is valid until ASystemFont_close() is called. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a IETF BCP47 compliant langauge tag or nullptr if not available. + */ +const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a font collection index value associated with the current font. + * + * In case the target font file is a font collection (e.g. .ttc or .otc), this + * returns a non-negative value as an font offset in the collection. This + * always returns 0 if the target font file is a regular font. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a font collection index. + */ +size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a count of font variation settings associated with the current font + * + * The font variation settings are provided as multiple tag-values pairs. + * + * For example, bold italic font may have following font variation settings: + * 'wght' 700, 'slnt' -12 + * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag + * and ASystemFont_getAxisValue will return following values. + * <code> + * ASystemFont* font = ASystemFontIterator_next(ite); + * + * // Returns the number of axes + * ASystemFont_getAxisCount(font); // Returns 2 + * + * // Returns the tag-value pair for the first axis. + * ASystemFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874) + * ASystemFont_getAxisValue(font, 0); // Returns 700.0 + * + * // Returns the tag-value pair for the second axis. + * ASystemFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74) + * ASystemFont_getAxisValue(font, 1); // Returns -12.0 + * </code> + * + * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a number of font variation settings. + */ +size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + + +/** + * Return an OpenType axis tag associated with the current font. + * + * See ASystemFont_getAxisCount for more details. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \param an index to the font variation settings. Passing value larger than or + * equal to {@link ASystemFont_getAxisCount} is not allowed. + * \return an OpenType axis tag value for the given font variation setting. + */ +uint32_t ASystemFont_getAxisTag(const ASystemFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +/** + * Return an OpenType axis value associated with the current font. + * + * See ASystemFont_getAxisCount for more details. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \param an index to the font variation settings. Passing value larger than or + * equal to {@link ASYstemFont_getAxisCount} is not allwed. + * \return a float value for the given font variation setting. + */ +float ASystemFont_getAxisValue(const ASystemFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_SYSTEM_FONTS_H diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 86da4d38e8..0de3bb7a08 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -17,11 +17,37 @@ #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H #define _LIBINPUT_DISPLAY_VIEWPORT_H +#include <android-base/stringprintf.h> #include <ui/DisplayInfo.h> #include <input/Input.h> +using android::base::StringPrintf; + namespace android { +/** + * Describes the different type of viewports supported by input flinger. + * Keep in sync with values in InputManagerService.java. + */ +enum class ViewportType : int32_t { + VIEWPORT_INTERNAL = 1, + VIEWPORT_EXTERNAL = 2, + VIEWPORT_VIRTUAL = 3, +}; + +static const char* viewportTypeToString(ViewportType type) { + switch(type) { + case ViewportType::VIEWPORT_INTERNAL: + return "INTERNAL"; + case ViewportType::VIEWPORT_EXTERNAL: + return "EXTERNAL"; + case ViewportType::VIEWPORT_VIRTUAL: + return "VIRTUAL"; + default: + return "UNKNOWN"; + } +} + /* * Describes how coordinates are mapped on a physical display. * See com.android.server.display.DisplayViewport. @@ -39,13 +65,14 @@ struct DisplayViewport { int32_t physicalBottom; int32_t deviceWidth; int32_t deviceHeight; - String8 uniqueId; + std::string uniqueId; + ViewportType type; DisplayViewport() : displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), - deviceWidth(0), deviceHeight(0) { + deviceWidth(0), deviceHeight(0), uniqueId(), type(ViewportType::VIEWPORT_INTERNAL) { } bool operator==(const DisplayViewport& other) const { @@ -61,7 +88,8 @@ struct DisplayViewport { && physicalBottom == other.physicalBottom && deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight - && uniqueId == other.uniqueId; + && uniqueId == other.uniqueId + && type == other.type; } bool operator!=(const DisplayViewport& other) const { @@ -86,17 +114,22 @@ struct DisplayViewport { deviceWidth = width; deviceHeight = height; uniqueId.clear(); + type = ViewportType::VIEWPORT_INTERNAL; } -}; -/** - * Describes the different type of viewports supported by input flinger. - * Keep in sync with values in InputManagerService.java. - */ -enum class ViewportType : int32_t { - VIEWPORT_INTERNAL = 1, - VIEWPORT_EXTERNAL = 2, - VIEWPORT_VIRTUAL = 3, + std::string toString() const { + return StringPrintf("Viewport %s: displayId=%d, orientation=%d, " + "logicalFrame=[%d, %d, %d, %d], " + "physicalFrame=[%d, %d, %d, %d], " + "deviceSize=[%d, %d]", + viewportTypeToString(type), + displayId, orientation, + logicalLeft, logicalTop, + logicalRight, logicalBottom, + physicalLeft, physicalTop, + physicalRight, physicalBottom, + deviceWidth, deviceHeight); + } }; } // namespace android diff --git a/include/input/Input.h b/include/input/Input.h index cfcafabebf..819a89f37c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -25,7 +25,6 @@ #include <utils/BitSet.h> #include <utils/KeyedVector.h> #include <utils/RefBase.h> -#include <utils/String8.h> #include <utils/Timers.h> #include <utils/Vector.h> #include <stdint.h> @@ -302,12 +301,18 @@ public: inline void setSource(int32_t source) { mSource = source; } + inline int32_t getDisplayId() const { return mDisplayId; } + + inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } + + protected: - void initialize(int32_t deviceId, int32_t source); + void initialize(int32_t deviceId, int32_t source, int32_t displayId); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; + int32_t mDisplayId; }; /* @@ -339,10 +344,11 @@ public: static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - + void initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -556,6 +562,7 @@ public: void initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, diff --git a/services/inputflinger/InputApplication.h b/include/input/InputApplication.h index 724fc2c4f4..9b365b9f0d 100644 --- a/services/inputflinger/InputApplication.h +++ b/include/input/InputApplication.h @@ -17,6 +17,8 @@ #ifndef _UI_INPUT_APPLICATION_H #define _UI_INPUT_APPLICATION_H +#include <string> + #include <input/Input.h> #include <utils/RefBase.h> #include <utils/Timers.h> diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 1ea69d352d..34d164c280 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -31,9 +31,9 @@ struct InputDeviceIdentifier { } // Information provided by the kernel. - String8 name; - String8 location; - String8 uniqueId; + std::string name; + std::string location; + std::string uniqueId; uint16_t bus; uint16_t vendor; uint16_t product; @@ -45,7 +45,7 @@ struct InputDeviceIdentifier { // It is hashed from whatever kernel provided information is available. // Ideally, the way this value is computed should not change between Android releases // because that would invalidate persistent settings that rely on it. - String8 descriptor; + std::string descriptor; // A value added to uniquely identify a device in the absence of a unique id. This // is intended to be a minimum way to distinguish from other active devices and may @@ -73,16 +73,16 @@ public: }; void initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal, + const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } inline int32_t getGeneration() const { return mGeneration; } inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; } - inline const String8& getAlias() const { return mAlias; } - inline const String8& getDisplayName() const { - return mAlias.isEmpty() ? mIdentifier.name : mAlias; + inline const std::string& getAlias() const { return mAlias; } + inline const std::string& getDisplayName() const { + return mAlias.empty() ? mIdentifier.name : mAlias; } inline bool isExternal() const { return mIsExternal; } inline bool hasMic() const { return mHasMic; } @@ -121,7 +121,7 @@ private: int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; - String8 mAlias; + std::string mAlias; bool mIsExternal; bool mHasMic; uint32_t mSources; @@ -149,7 +149,7 @@ enum InputDeviceConfigurationFileType { * * Returns an empty string if not found. */ -extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type); @@ -162,8 +162,8 @@ extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( * * Returns an empty string if not found. */ -extern String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type); +extern std::string getInputDeviceConfigurationFilePathByName( + const std::string& name, InputDeviceConfigurationFileType type); } // namespace android diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 4b33a96adf..6d072a3ea5 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -326,7 +326,7 @@ static const InputEventLabel KEYCODES[] = { DEFINE_KEYCODE(ALL_APPS), DEFINE_KEYCODE(REFRESH), - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel AXES[] = { @@ -375,7 +375,7 @@ static const InputEventLabel AXES[] = { // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel LEDS[] = { @@ -396,7 +396,7 @@ static const InputEventLabel LEDS[] = { DEFINE_LED(CONTROLLER_4), // NOTE: If you add new LEDs here, you must also add them to Input.h - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel FLAGS[] = { @@ -404,7 +404,7 @@ static const InputEventLabel FLAGS[] = { DEFINE_FLAG(FUNCTION), DEFINE_FLAG(GESTURE), - { NULL, 0 } + { nullptr, 0 } }; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { @@ -424,7 +424,7 @@ static const char* lookupLabelByValue(int value, const InputEventLabel* list) { } list++; } - return NULL; + return nullptr; } static inline int32_t getKeyCodeByLabel(const char* label) { @@ -435,7 +435,7 @@ static inline const char* getLabelByKeyCode(int32_t keyCode) { if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) { return KEYCODES[keyCode].literal; } - return NULL; + return nullptr; } static inline uint32_t getKeyFlagByLabel(const char* label) { diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 1ea2c2cc07..4782c9b237 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -27,6 +27,8 @@ * The InputConsumer is used by the application to receive events from the input dispatcher. */ +#include <string> + #include <input/Input.h> #include <utils/Errors.h> #include <utils/Timers.h> @@ -35,6 +37,7 @@ #include <utils/BitSet.h> namespace android { +class Parcel; /* * Intermediate representation used to send input events and related signals. @@ -141,6 +144,7 @@ protected: virtual ~InputChannel(); public: + InputChannel() = default; InputChannel(const std::string& name, int fd); /* Creates a pair of input channels. @@ -181,9 +185,14 @@ public: /* Returns a new object that has a duplicate of this channel's fd. */ sp<InputChannel> dup() const; + status_t write(Parcel& out) const; + status_t read(const Parcel& from); + private: + void setFd(int fd); + std::string mName; - int mFd; + int mFd = -1; }; /* @@ -212,6 +221,7 @@ public: uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -305,7 +315,7 @@ public: * Other errors probably indicate that the channel is broken. */ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -460,10 +470,9 @@ private: Vector<SeqChain> mSeqChains; status_t consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); status_t consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, - int32_t* displayId); + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); void updateTouchState(InputMessage& msg); void resampleTouchState(nsecs_t frameTime, MotionEvent* event, diff --git a/services/inputflinger/InputWindow.h b/include/input/InputWindow.h index 5a48375910..7c284ddaf6 100644 --- a/services/inputflinger/InputWindow.h +++ b/include/input/InputWindow.h @@ -27,7 +27,7 @@ #include "InputApplication.h" namespace android { - +class Parcel; /* * Describes the properties of a window that can receive input. @@ -151,6 +151,9 @@ struct InputWindowInfo { bool supportsSplitTouch() const; bool overlaps(const InputWindowInfo* other) const; + + status_t write(Parcel& output) const; + static InputWindowInfo read(const Parcel& from); }; @@ -168,9 +171,7 @@ public: return mInfo; } - inline sp<InputChannel> getInputChannel() const { - return mInfo ? mInfo->inputChannel : NULL; - } + sp<InputChannel> getInputChannel() const; inline std::string getName() const { return mInfo ? mInfo->name : "<invalid>"; diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 33d2757ec8..9f4559f53c 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -27,7 +27,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> -#include <utils/String8.h> #include <utils/Unicode.h> #include <utils/RefBase.h> @@ -75,10 +74,10 @@ public: }; /* Loads a key character map from a file. */ - static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap); + static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap); /* Loads a key character map from its string contents. */ - static status_t loadContents(const String8& filename, + static status_t loadContents(const std::string& filename, const char* contents, Format format, sp<KeyCharacterMap>* outMap); /* Combines a base key character map and an overlay. */ @@ -221,7 +220,7 @@ private: status_t parseKey(); status_t parseKeyProperty(); status_t finishKey(Key* key); - status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseModifier(const std::string& token, int32_t* outMetaState); status_t parseCharacterLiteral(char16_t* outCharacter); }; diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 1e8de7173b..73815fe8b4 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -62,7 +62,7 @@ struct AxisInfo { */ class KeyLayoutMap : public RefBase { public: - static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap); + static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index d4903e98df..8b66f693cc 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -21,7 +21,6 @@ #include <input/InputDevice.h> #include <input/InputEventLabels.h> #include <utils/Errors.h> -#include <utils/String8.h> #include <utils/PropertyMap.h> namespace android { @@ -43,10 +42,10 @@ class KeyCharacterMap; */ class KeyMap { public: - String8 keyLayoutFile; + std::string keyLayoutFile; sp<KeyLayoutMap> keyLayoutMap; - String8 keyCharacterMapFile; + std::string keyCharacterMapFile; sp<KeyCharacterMap> keyCharacterMap; KeyMap(); @@ -56,11 +55,11 @@ public: const PropertyMap* deviceConfiguration); inline bool haveKeyLayout() const { - return !keyLayoutFile.isEmpty(); + return !keyLayoutFile.empty(); } inline bool haveKeyCharacterMap() const { - return !keyCharacterMapFile.isEmpty(); + return !keyCharacterMapFile.empty(); } inline bool isComplete() const { @@ -68,12 +67,12 @@ public: } private: - bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); - status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); + status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name); - String8 getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type); + const std::string& name); + std::string getPath(const InputDeviceIdentifier& deviceIdentifier, + const std::string& name, InputDeviceConfigurationFileType type); }; /** diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index ffa1614b55..727865a933 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -63,7 +63,7 @@ public: // Creates a velocity tracker using the specified strategy. // If strategy is NULL, uses the default strategy for the platform. - VelocityTracker(const char* strategy = NULL); + VelocityTracker(const char* strategy = nullptr); ~VelocityTracker(); diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h index e245ead682..24e0e0ed9e 100644 --- a/include/input/VirtualKeyMap.h +++ b/include/input/VirtualKeyMap.h @@ -23,7 +23,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> -#include <utils/String8.h> #include <utils/Unicode.h> namespace android { @@ -50,7 +49,7 @@ class VirtualKeyMap { public: ~VirtualKeyMap(); - static status_t load(const String8& filename, VirtualKeyMap** outMap); + static status_t load(const std::string& filename, VirtualKeyMap** outMap); inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { return mVirtualKeys; diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index da10687476..aedf6b0d18 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -107,6 +107,7 @@ cc_library_shared { "-Wall", "-Wextra", "-Werror", + "-Wzero-as-null-pointer-constant", ], product_variables: { binder32bit: { diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 307bc28c0e..caf2318281 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -86,7 +86,7 @@ public: virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; private: friend class IMemory; @@ -113,7 +113,7 @@ private: mutable void* mBase; mutable size_t mSize; mutable uint32_t mFlags; - mutable uint32_t mOffset; + mutable off_t mOffset; mutable bool mRealHeap; mutable Mutex mLock; }; @@ -189,13 +189,16 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp<IBinder> heap = reply.readStrongBinder(); - ssize_t o = reply.readInt32(); - size_t s = reply.readInt32(); if (heap != nullptr) { mHeap = interface_cast<IMemoryHeap>(heap); if (mHeap != nullptr) { + const int64_t offset64 = reply.readInt64(); + const uint64_t size64 = reply.readUint64(); + const ssize_t o = (ssize_t)offset64; + const size_t s = (size_t)size64; size_t heapSize = mHeap->getSize(); - if (s <= heapSize + if (s == size64 && o == offset64 // ILP32 bounds check + && s <= heapSize && o >= 0 && (static_cast<size_t>(o) <= heapSize - s)) { mOffset = o; @@ -236,8 +239,8 @@ status_t BnMemory::onTransact( ssize_t offset; size_t size; reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) ); - reply->writeInt32(offset); - reply->writeInt32(size); + reply->writeInt64(offset); + reply->writeUint64(size); return NO_ERROR; } break; default: @@ -316,18 +319,23 @@ void BpMemoryHeap::assertReallyMapped() const data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); - ssize_t size = reply.readInt32(); - uint32_t flags = reply.readInt32(); - uint32_t offset = reply.readInt32(); - - ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)", - IInterface::asBinder(this).get(), - parcel_fd, size, err, strerror(-err)); + const uint64_t size64 = reply.readUint64(); + const int64_t offset64 = reply.readInt64(); + const uint32_t flags = reply.readUint32(); + const size_t size = (size_t)size64; + const off_t offset = (off_t)offset64; + if (err != NO_ERROR || // failed transaction + size != size64 || offset != offset64) { // ILP32 size check + ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)", + IInterface::asBinder(this).get(), + parcel_fd, size, err, strerror(-err)); + return; + } Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0); - ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)", + ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; @@ -337,7 +345,7 @@ void BpMemoryHeap::assertReallyMapped() const mRealHeap = true; mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset); if (mBase == MAP_FAILED) { - ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)", + ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)", IInterface::asBinder(this).get(), size, fd, strerror(errno)); close(fd); } else { @@ -371,7 +379,7 @@ uint32_t BpMemoryHeap::getFlags() const { return mFlags; } -uint32_t BpMemoryHeap::getOffset() const { +off_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } @@ -394,9 +402,9 @@ status_t BnMemoryHeap::onTransact( case HEAP_ID: { CHECK_INTERFACE(IMemoryHeap, data, reply); reply->writeFileDescriptor(getHeapID()); - reply->writeInt32(getSize()); - reply->writeInt32(getFlags()); - reply->writeInt32(getOffset()); + reply->writeUint64(getSize()); + reply->writeInt64(getOffset()); + reply->writeUint32(getFlags()); return NO_ERROR; } break; default: diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index f052bcb982..7a477249dd 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -109,6 +109,10 @@ static const char *kCommandStrings[] = { "BC_DEAD_BINDER_DONE" }; +// The work source represents the UID of the process we should attribute the transaction to. +// We use -1 to specify that the work source was not set using #setWorkSource. +static const int kUnsetWorkSource = -1; + static const char* getReturnString(uint32_t cmd) { size_t idx = cmd & 0xff; @@ -383,6 +387,25 @@ int32_t IPCThreadState::getStrictModePolicy() const return mStrictModePolicy; } +uid_t IPCThreadState::setWorkSource(uid_t uid) +{ + uid_t returnValue = mWorkSource; + mWorkSource = uid; + return returnValue; +} + +uid_t IPCThreadState::getWorkSource() const +{ + return mWorkSource; +} + +uid_t IPCThreadState::clearWorkSource() +{ + uid_t returnValue = mWorkSource; + mWorkSource = kUnsetWorkSource; + return returnValue; +} + void IPCThreadState::setLastTransactionBinderFlags(int32_t flags) { mLastTransactionBinderFlags = flags; @@ -736,6 +759,7 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mWorkSource(kUnsetWorkSource), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 9850ad9624..4c300b47c6 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -76,7 +76,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) } } -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset) +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), mDevice(nullptr), mNeedUnmap(false), mOffset(0) { @@ -85,7 +85,7 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t off mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset); } -status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device) { if (mFD != -1) { return INVALID_OPERATION; @@ -98,13 +98,20 @@ status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const cha return NO_ERROR; } -status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) +status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset) { if (size == 0) { // try to figure out the size automatically struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; + if (fstat(fd, &sb) == 0) { + size = (size_t)sb.st_size; + // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits. + if ((off_t)size != sb.st_size) { + ALOGE("%s: size of file %lld cannot fit in memory", + __func__, (long long)sb.st_size); + return INVALID_OPERATION; + } + } // if it didn't work, let mmap() fail. } @@ -112,12 +119,12 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) void* base = (uint8_t*)mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { - ALOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); + ALOGE("mmap(fd=%d, size=%zu) failed (%s)", + fd, size, strerror(errno)); close(fd); return -errno; } - //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { @@ -140,7 +147,7 @@ void MemoryHeapBase::dispose() int fd = android_atomic_or(-1, &mFD); if (fd >= 0) { if (mNeedUnmap) { - //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize); munmap(mBase, mSize); } mBase = nullptr; @@ -169,7 +176,7 @@ const char* MemoryHeapBase::getDevice() const { return mDevice; } -uint32_t MemoryHeapBase::getOffset() const { +off_t MemoryHeapBase::getOffset() const { return mOffset; } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index b3ae09b405..bc1a71cac8 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -74,7 +74,7 @@ static size_t pad_size(size_t s) { } // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER -#define STRICT_MODE_PENALTY_GATHER (0x40 << 16) +#define STRICT_MODE_PENALTY_GATHER (1 << 31) // XXX This can be made public if we want to provide // support for typed data. @@ -601,6 +601,7 @@ status_t Parcel::writeInterfaceToken(const String16& interface) { writeInt32(IPCThreadState::self()->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); + writeInt32(IPCThreadState::self()->getWorkSource()); // currently the interface identification token is just its name as a string return writeString16(interface); } @@ -613,6 +614,7 @@ bool Parcel::checkInterface(IBinder* binder) const bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { + // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { threadState = IPCThreadState::self(); @@ -627,6 +629,10 @@ bool Parcel::enforceInterface(const String16& interface, } else { threadState->setStrictModePolicy(strictPolicy); } + // WorkSource. + int32_t workSource = readInt32(); + threadState->setWorkSource(workSource); + // Interface descriptor. const String16 str(readString16()); if (str == interface) { return true; @@ -2307,6 +2313,15 @@ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const int fd = readFileDescriptor(); if (fd == int(BAD_TYPE)) return BAD_VALUE; + if (!ashmem_valid(fd)) { + ALOGE("invalid fd"); + return BAD_VALUE; + } + int size = ashmem_get_size_region(fd); + if (size < 0 || size_t(size) < len) { + ALOGE("request size %zu does not match fd size %d", len, size); + return BAD_VALUE; + } void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) return NO_MEMORY; diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index c5b57c7edf..37237dfd48 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -95,6 +95,20 @@ public: OP_USE_FINGERPRINT = 55, OP_BODY_SENSORS = 56, OP_AUDIO_ACCESSIBILITY_VOLUME = 64, + OP_READ_PHONE_NUMBERS = 65, + OP_REQUEST_INSTALL_PACKAGES = 66, + OP_PICTURE_IN_PICTURE = 67, + OP_INSTANT_APP_START_FOREGROUND = 68, + OP_ANSWER_PHONE_CALLS = 69, + OP_RUN_ANY_IN_BACKGROUND = 70, + OP_CHANGE_WIFI_STATE = 71, + OP_REQUEST_DELETE_PACKAGES = 72, + OP_BIND_ACCESSIBILITY_SERVICE = 73, + OP_ACCEPT_HANDOVER = 74, + OP_MANAGE_IPSEC_TUNNELS = 75, + OP_START_FOREGROUND = 76, + OP_BLUETOOTH_SCAN = 77, + OP_USE_BIOMETRIC = 78, }; AppOpsManager(); diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index db9f53a797..071946f50c 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -43,7 +43,7 @@ public: virtual void* getBase() const = 0; virtual size_t getSize() const = 0; virtual uint32_t getFlags() const = 0; - virtual uint32_t getOffset() const = 0; + virtual off_t getOffset() const = 0; // these are there just for backward source compatibility int32_t heapID() const { return getHeapID(); } diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 40b51adb06..5888e14381 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -47,6 +47,13 @@ public: void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; + // See Binder#setThreadWorkSource in Binder.java. + uid_t setWorkSource(uid_t uid); + // See Binder#getThreadWorkSource in Binder.java. + uid_t getWorkSource() const; + // See Binder#clearThreadWorkSource in Binder.java. + uid_t clearWorkSource(); + void setLastTransactionBinderFlags(int32_t flags); int32_t getLastTransactionBinderFlags() const; @@ -155,6 +162,9 @@ private: status_t mLastError; pid_t mCallingPid; uid_t mCallingUid; + // The UID of the process who is responsible for this transaction. + // This is used for resource attribution. + int32_t mWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; IPCThreadStateBase *mIPCThreadStateBase; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index e5d8ea67de..aeea1a2676 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -88,7 +88,7 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) const sp<IServiceManager> sm = defaultServiceManager(); if (sm != nullptr) { *outService = interface_cast<INTERFACE>(sm->getService(name)); - if ((*outService) != NULL) return NO_ERROR; + if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; } diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 5e0a382a7e..2f5039dcc9 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -42,7 +42,7 @@ public: * maps the memory referenced by fd. but DOESN'T take ownership * of the filedescriptor (it makes a copy with dup() */ - MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0); + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0); /* * maps memory from the given device @@ -64,7 +64,7 @@ public: virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; const char* getDevice() const; @@ -82,11 +82,11 @@ public: protected: MemoryHeapBase(); // init() takes ownership of fd - status_t init(int fd, void *base, int size, + status_t init(int fd, void *base, size_t size, int flags = 0, const char* device = nullptr); private: - status_t mapfd(int fd, size_t size, uint32_t offset = 0); + status_t mapfd(int fd, size_t size, off_t offset = 0); int mFD; size_t mSize; @@ -94,7 +94,7 @@ private: uint32_t mFlags; const char* mDevice; bool mNeedUnmap; - uint32_t mOffset; + off_t mOffset; }; // --------------------------------------------------------------------------- diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 73c2eba1d5..7bcfffd8d4 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -71,6 +71,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, + BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -938,6 +939,43 @@ TEST_F(BinderLibTest, OnewayQueueing) EXPECT_EQ(NO_ERROR, ret); } +TEST_F(BinderLibTest, WorkSourceUnsetByDefault) +{ + status_t ret; + Parcel data, reply; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSet) +{ + status_t ret; + Parcel data, reply; + uid_t previousWorkSource = IPCThreadState::self()->setWorkSource(100); + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(-1, previousWorkSource); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setWorkSource(100); + uid_t previousWorkSource = IPCThreadState::self()->clearWorkSource(); + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(100, previousWorkSource); + EXPECT_EQ(NO_ERROR, ret); +} + class BinderLibTestService : public BBinder { public: @@ -1236,6 +1274,11 @@ class BinderLibTestService : public BBinder } return NO_ERROR; } + case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: { + data.enforceInterface(binderLibTestServiceName); + reply->writeInt32(IPCThreadState::self()->getWorkSource()); + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 8b2f842a44..0d224d8e45 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -46,6 +46,7 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", + "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 4da30e9980..bab87acb05 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -23,6 +23,7 @@ cc_library_shared { shared_libs: [ "liblog", + "libcutils", ], export_include_dirs: ["include"], diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index a8ef7a051d..97b4828e01 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -18,9 +18,12 @@ #define LOG_TAG "GraphicsEnv" #include <graphicsenv/GraphicsEnv.h> +#include <sys/prctl.h> + #include <mutex> #include <android/dlext.h> +#include <cutils/properties.h> #include <log/log.h> // TODO(b/37049319) Get this from a header once one exists @@ -46,6 +49,14 @@ namespace android { return env; } +int GraphicsEnv::getCanLoadSystemLibraries() { + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + // Return an integer value since this crosses library boundaries + return 1; + } + return 0; +} + void GraphicsEnv::setDriverPath(const std::string path) { if (!mDriverPath.empty()) { ALOGV("ignoring attempt to change driver path from '%s' to '%s'", @@ -56,6 +67,44 @@ void GraphicsEnv::setDriverPath(const std::string path) { mDriverPath = path; } +void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, + const std::string appPref, bool developerOptIn, + const int rulesFd, const long rulesOffset, + const long rulesLength) { + if (!mAnglePath.empty()) { + ALOGV("ignoring attempt to change ANGLE path from '%s' to '%s'", mAnglePath.c_str(), + path.c_str()); + } else { + ALOGV("setting ANGLE path to '%s'", path.c_str()); + mAnglePath = path; + } + + if (!mAngleAppName.empty()) { + ALOGV("ignoring attempt to change ANGLE app name from '%s' to '%s'", mAngleAppName.c_str(), + appName.c_str()); + } else { + ALOGV("setting ANGLE app name to '%s'", appName.c_str()); + mAngleAppName = appName; + } + + if (!mAngleAppPref.empty()) { + ALOGV("ignoring attempt to change ANGLE application opt-in from '%s' to '%s'", + mAngleAppPref.c_str(), appPref.c_str()); + } else { + ALOGV("setting ANGLE application opt-in to '%s'", appPref.c_str()); + mAngleAppPref = appPref; + } + + mAngleDeveloperOptIn = developerOptIn; + + ALOGV("setting ANGLE rules file descriptor to '%i'", rulesFd); + mAngleRulesFd = rulesFd; + ALOGV("setting ANGLE rules offset to '%li'", rulesOffset); + mAngleRulesOffset = rulesOffset; + ALOGV("setting ANGLE rules length to '%li'", rulesLength); + mAngleRulesLength = rulesLength; +} + void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; @@ -70,18 +119,52 @@ NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } -const std::string GraphicsEnv::getLayerPaths(){ +const char* GraphicsEnv::getAngleAppName() { + if (mAngleAppName.empty()) return nullptr; + return mAngleAppName.c_str(); +} + +bool GraphicsEnv::getAngleDeveloperOptIn() { + return mAngleDeveloperOptIn; +} + +const char* GraphicsEnv::getAngleAppPref() { + if (mAngleAppPref.empty()) return nullptr; + return mAngleAppPref.c_str(); +} + +int GraphicsEnv::getAngleRulesFd() { + return mAngleRulesFd; +} + +long GraphicsEnv::getAngleRulesOffset() { + return mAngleRulesOffset; +} + +long GraphicsEnv::getAngleRulesLength() { + return mAngleRulesLength; +} + +const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } -const std::string GraphicsEnv::getDebugLayers() { +const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } +const std::string& GraphicsEnv::getDebugLayersGLES() { + return mDebugLayersGLES; +} + void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } +void GraphicsEnv::setDebugLayersGLES(const std::string layers) { + mDebugLayersGLES = layers; +} + android_namespace_t* GraphicsEnv::getDriverNamespace() { static std::once_flag once; std::call_once(once, [this]() { @@ -92,7 +175,7 @@ android_namespace_t* GraphicsEnv::getDriverNamespace() { auto sphalNamespace = android_get_exported_namespace("sphal"); if (!sphalNamespace) return; mDriverNamespace = android_create_namespace("gfx driver", - nullptr, // ld_library_path + mDriverPath.c_str(), // ld_library_path mDriverPath.c_str(), // default_library_path ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED, @@ -102,8 +185,22 @@ android_namespace_t* GraphicsEnv::getDriverNamespace() { return mDriverNamespace; } -} // namespace android +android_namespace_t* GraphicsEnv::getAngleNamespace() { + static std::once_flag once; + std::call_once(once, [this]() { + if (mAnglePath.empty()) return; + + mAngleNamespace = android_create_namespace("ANGLE", + nullptr, // ld_library_path + mAnglePath.c_str(), // default_library_path + ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); + if (!mAngleNamespace) ALOGD("Could not create ANGLE namespace from default"); + }); -extern "C" android_namespace_t* android_getDriverNamespace() { - return android::GraphicsEnv::getInstance().getDriverNamespace(); + return mAngleNamespace; } + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 17e8f6ba47..528c260653 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -29,6 +29,8 @@ class GraphicsEnv { public: static GraphicsEnv& getInstance(); + int getCanLoadSystemLibraries(); + // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (drivers must be stored uncompressed and page aligned); such elements @@ -37,35 +39,50 @@ public: void setDriverPath(const std::string path); android_namespace_t* getDriverNamespace(); + // Set a search path for loading ANGLE libraries. The path is a list of + // directories separated by ':'. A directory can be contained in a zip file + // (libraries must be stored uncompressed and page aligned); such elements + // in the search path must have a '!' after the zip filename, e.g. + // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a + void setAngleInfo(const std::string path, const std::string appName, const std::string appPref, + bool devOptIn, const int rulesFd, const long rulesOffset, + const long rulesLength); + android_namespace_t* getAngleNamespace(); + const char* getAngleAppName(); + const char* getAngleAppPref(); + bool getAngleDeveloperOptIn(); + int getAngleRulesFd(); + long getAngleRulesOffset(); + long getAngleRulesLength(); + void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths); NativeLoaderNamespace* getAppNamespace(); - const std::string getLayerPaths(); + + const std::string& getLayerPaths(); void setDebugLayers(const std::string layers); - const std::string getDebugLayers(); + void setDebugLayersGLES(const std::string layers); + const std::string& getDebugLayers(); + const std::string& getDebugLayersGLES(); private: GraphicsEnv() = default; std::string mDriverPath; + std::string mAnglePath; + std::string mAngleAppName; + std::string mAngleAppPref; + bool mAngleDeveloperOptIn; + int mAngleRulesFd; + long mAngleRulesOffset; + long mAngleRulesLength; std::string mDebugLayers; + std::string mDebugLayersGLES; std::string mLayerPaths; android_namespace_t* mDriverNamespace = nullptr; + android_namespace_t* mAngleNamespace = nullptr; NativeLoaderNamespace* mAppNamespace = nullptr; }; } // namespace android -/* FIXME - * Export an un-mangled function that just does - * return android::GraphicsEnv::getInstance().getDriverNamespace(); - * This allows libEGL to get the function pointer via dlsym, since it can't - * directly link against libgui. In a future release, we'll fix this so that - * libgui does not depend on graphics API libraries, and libEGL can link - * against it. The current dependencies from libgui -> libEGL are: - * - the GLConsumer class, which should be moved to its own library - * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and - * will be removed soon. - */ -extern "C" android_namespace_t* android_getDriverNamespace(); - #endif // ANDROID_UI_GRAPHICS_ENV_H diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b29c1d5157..98264ac35f 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -31,6 +31,7 @@ cc_library_shared { "-Werror", ], cppflags: [ + "-std=c++1z", "-Weverything", // The static constructors and destructors in this library have not been noted to @@ -58,7 +59,7 @@ cc_library_shared { "-Wno-weak-vtables", // Allow four-character integer literals - "-Wno-four-char-constants", + "-Wno-four-char-constants", // Allow documentation warnings "-Wno-documentation", @@ -116,14 +117,15 @@ cc_library_shared { "SyncFeatures.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", - "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" + "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", ], shared_libs: [ "android.hardware.graphics.common@1.1", "libsync", "libbinder", - "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. + "libbufferhub", + "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. "libpdx_default_transport", "libcutils", "libEGL", @@ -149,6 +151,7 @@ cc_library_shared { "BufferHubProducer.cpp", ], exclude_shared_libs: [ + "libbufferhub", "libbufferhubqueue", "libpdx_default_transport", ], diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp index ae5cca2d20..ed773e003f 100644 --- a/libs/gui/BufferHubProducer.cpp +++ b/libs/gui/BufferHubProducer.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <log/log.h> #include <system/window.h> +#include <ui/BufferHubBuffer.h> namespace android { @@ -224,23 +225,172 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u return ret; } -status_t BufferHubProducer::detachBuffer(int /* slot */) { - ALOGE("BufferHubProducer::detachBuffer not implemented."); - return INVALID_OPERATION; +status_t BufferHubProducer::detachBuffer(int slot) { + ALOGV("detachBuffer: slot=%d", slot); + std::unique_lock<std::mutex> lock(mutex_); + + return DetachBufferLocked(static_cast<size_t>(slot)); } -status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */, - sp<Fence>* /* out_fence */) { - ALOGE("BufferHubProducer::detachNextBuffer not implemented."); +status_t BufferHubProducer::DetachBufferLocked(size_t slot) { + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + if (slot >= static_cast<size_t>(max_buffer_count_)) { + ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if (!buffers_[slot].mRequestBufferCalled) { + ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot); + return BAD_VALUE; + } + std::shared_ptr<BufferProducer> buffer_producer = queue_->GetBuffer(slot); + if (buffer_producer == nullptr || buffer_producer->buffer() == nullptr) { + ALOGE("detachBuffer: Invalid BufferProducer at slot %zu.", slot); + return BAD_VALUE; + } + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + if (graphic_buffer == nullptr) { + ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); + return BAD_VALUE; + } + + // Remove the BufferProducer from the ProducerQueue. + status_t error = RemoveBuffer(slot); + if (error != NO_ERROR) { + ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error); + return error; + } + + // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject + // the handle into the GraphicBuffer object at the requested slot. + auto status_or_handle = buffer_producer->Detach(); + if (!status_or_handle.ok()) { + ALOGE("detachBuffer: Failed to detach from a BufferProducer at slot %zu, error=%d.", slot, + status_or_handle.error()); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } -status_t BufferHubProducer::attachBuffer(int* /* out_slot */, - const sp<GraphicBuffer>& /* buffer */) { - // With this BufferHub backed implementation, we assume (for now) all buffers - // are allocated and owned by the BufferHub. Thus the attempt of transfering - // ownership of a buffer to the buffer queue is intentionally unsupported. - LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported."); +status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) { + ALOGV("detachNextBuffer."); + + if (out_buffer == nullptr || out_fence == nullptr) { + ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer, + out_fence); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachNextBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in + // sequence, except for two things: + // + // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the + // function just returns whatever BufferProducer is available from the ProducerQueue and no + // buffer allocation or re-allocation will happen. + // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return + // an error instead. + size_t slot = 0; + LocalHandle fence; + + // First, dequeue a BufferProducer from the ProducerQueue with no timeout. Report error + // immediately if ProducerQueue::Dequeue() fails. + auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence); + if (!status_or_buffer.ok()) { + ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error()); + return NO_MEMORY; + } + + std::shared_ptr<BufferProducer> buffer_producer = status_or_buffer.take(); + if (buffer_producer == nullptr) { + ALOGE("detachNextBuffer: Dequeued buffer is null."); + return NO_MEMORY; + } + + // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to + // be available for producer's use. It's either in free state (if the buffer has never been used + // before) or in queued state (if the buffer has been dequeued and queued back to + // BufferHubQueue). + if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) { + ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } + if (buffers_[slot].mBufferProducer == nullptr) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu is null.", slot); + return BAD_VALUE; + } + if (buffers_[slot].mBufferProducer->id() != buffer_producer->id()) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu has mismatched id, actual: " + "%d, expected: %d.", + slot, buffers_[slot].mBufferProducer->id(), buffer_producer->id()); + return BAD_VALUE; + } + + ALOGV("detachNextBuffer: slot=%zu", slot); + buffers_[slot].mBufferState.freeQueued(); + buffers_[slot].mBufferState.dequeue(); + + // Second, request the buffer. + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + buffers_[slot].mGraphicBuffer = buffer_producer->buffer()->buffer(); + + // Finally, detach the buffer and then return. + status_t error = DetachBufferLocked(slot); + if (error == NO_ERROR) { + *out_fence = new Fence(fence.Release()); + *out_buffer = graphic_buffer; + } + return error; +} + +status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) { + // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only + // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer. + ALOGV("queueBuffer: buffer=%p", buffer.get()); + + if (out_slot == nullptr) { + ALOGE("attachBuffer: out_slot cannot be NULL."); + return BAD_VALUE; + } + if (buffer == nullptr) { + ALOGE("attachBuffer: invalid GraphicBuffer."); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("attachBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + // Before attaching the buffer, caller is supposed to call + // IGraphicBufferProducer::setGenerationNumber to inform the + // BufferHubProducer the next generation number. + if (buffer->getGenerationNumber() != generation_number_) { + ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.", + buffer->getGenerationNumber(), generation_number_); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } @@ -654,26 +804,28 @@ status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint status_t BufferHubProducer::RemoveBuffer(size_t slot) { auto status = queue_->RemoveBuffer(slot); if (!status) { - ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s", - status.GetErrorMessage().c_str()); + ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.", + slot, status.GetErrorMessage().c_str()); return INVALID_OPERATION; } // Reset in memory objects related the the buffer. buffers_[slot].mBufferProducer = nullptr; - buffers_[slot].mGraphicBuffer = nullptr; buffers_[slot].mBufferState.detachProducer(); + buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; return NO_ERROR; } status_t BufferHubProducer::FreeAllBuffers() { for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { // Reset in memory objects related the the buffer. - buffers_[slot].mGraphicBuffer = nullptr; - buffers_[slot].mBufferState.reset(); - buffers_[slot].mRequestBufferCalled = false; buffers_[slot].mBufferProducer = nullptr; + buffers_[slot].mBufferState.reset(); buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; } auto status = queue_->FreeAllBuffers(); diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index f50379b3ed..5beba02e63 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -39,8 +39,8 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { } BufferItem::BufferItem() : - mGraphicBuffer(NULL), - mFence(NULL), + mGraphicBuffer(nullptr), + mFence(nullptr), mCrop(Rect::INVALID_RECT), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), @@ -91,11 +91,11 @@ size_t BufferItem::getPodSize() const { size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { size += mGraphicBuffer->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } - if (mFence != 0) { + if (mFence != nullptr) { size += mFence->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } @@ -107,10 +107,10 @@ size_t BufferItem::getFlattenedSize() const { size_t BufferItem::getFdCount() const { size_t count = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { count += mGraphicBuffer->getFdCount(); } - if (mFence != 0) { + if (mFence != nullptr) { count += mFence->getFdCount(); } return count; @@ -137,13 +137,13 @@ status_t BufferItem::flatten( FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); flags = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 1; } - if (mFence != 0) { + if (mFence != nullptr) { status_t err = mFence->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 89bc0c4c2d..f50bc203e8 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -107,7 +107,7 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, void BufferItemConsumer::freeBufferLocked(int slotIndex) { sp<BufferFreedListener> listener = mBufferFreedListener.promote(); - if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) { + if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) { // Fire callback if we have a listener registered and the buffer being freed is valid. BI_LOGV("actually calling onBufferFreed"); listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer); diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index a8da1347cb..5fb3f0b80f 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -38,7 +38,7 @@ BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onDisconnect() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onDisconnect(); } } @@ -46,7 +46,7 @@ void BufferQueue::ProxyConsumerListener::onDisconnect() { void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameAvailable(item); } } @@ -54,21 +54,21 @@ void BufferQueue::ProxyConsumerListener::onFrameAvailable( void BufferQueue::ProxyConsumerListener::onFrameReplaced( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameReplaced(item); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } } void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } } @@ -85,21 +85,21 @@ void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<BufferQueueCore> core(new BufferQueueCore()); - LOG_ALWAYS_FATAL_IF(core == NULL, + LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); - LOG_ALWAYS_FATAL_IF(producer == NULL, + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); - LOG_ALWAYS_FATAL_IF(consumer == NULL, + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; @@ -109,8 +109,8 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, #ifndef NO_BUFFERHUB void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; @@ -118,16 +118,16 @@ void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, dvr::ProducerQueueConfigBuilder configBuilder; std::shared_ptr<dvr::ProducerQueue> producerQueue = dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{}); - LOG_ALWAYS_FATAL_IF(producerQueue == NULL, "BufferQueue: failed to create ProducerQueue."); + LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue."); std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue(); - LOG_ALWAYS_FATAL_IF(consumerQueue == NULL, "BufferQueue: failed to create ConsumerQueue."); + LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue."); producer = BufferHubProducer::Create(producerQueue); consumer = BufferHubConsumer::Create(consumerQueue); - LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer"); - LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer"); + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index d70e1422b0..3837c3e11a 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -255,7 +255,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { - outBuffer->mGraphicBuffer = NULL; + outBuffer->mGraphicBuffer = nullptr; } mCore->mQueue.erase(front); @@ -272,7 +272,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, VALIDATE_CONSISTENCY(); } - if (listener != NULL) { + if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } @@ -321,10 +321,10 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -413,7 +413,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, ATRACE_BUFFER_INDEX(slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == NULL) { + releaseFence == nullptr) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; @@ -465,7 +465,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBufferReleased(); } @@ -476,7 +476,7 @@ status_t BufferQueueConsumer::connect( const sp<IConsumerListener>& consumerListener, bool controlledByApp) { ATRACE_CALL(); - if (consumerListener == NULL) { + if (consumerListener == nullptr) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } @@ -504,13 +504,13 @@ status_t BufferQueueConsumer::disconnect() { Mutex::Autolock lock(mCore->mMutex); - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; - mCore->mConsumerListener = NULL; + mCore->mConsumerListener = nullptr; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; @@ -521,7 +521,7 @@ status_t BufferQueueConsumer::disconnect() { status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); - if (outSlotMask == NULL) { + if (outSlotMask == nullptr) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } @@ -673,7 +673,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( } } // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -772,7 +772,7 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul if (uid != shellUid) { #endif android_errorWriteWithInfoLog(0x534e4554, "27046057", - static_cast<int32_t>(uid), NULL, 0); + static_cast<int32_t>(uid), nullptr, 0); return PERMISSION_DENIED; } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index bb703da3dd..960b1948c2 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -349,7 +349,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", slot); usleep(PAUSE_TIME); @@ -371,7 +371,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", slot); usleep(PAUSE_TIME); @@ -394,7 +394,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL) { + if (mSlots[slot].mGraphicBuffer == nullptr) { BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); usleep(PAUSE_TIME); } @@ -418,7 +418,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) { + if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) { BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot); usleep(PAUSE_TIME); } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index c96a2dd6a3..5e250a4185 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -166,7 +166,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -221,7 +221,7 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; @@ -449,11 +449,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou mSlots[found].mBufferState.dequeue(); - if ((buffer == NULL) || + if ((buffer == nullptr) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; - mSlots[found].mGraphicBuffer = NULL; + mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; @@ -471,7 +471,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); - if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); @@ -612,7 +612,7 @@ status_t BufferQueueProducer::detachBuffer(int slot) { listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -623,10 +623,10 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { ATRACE_CALL(); - if (outBuffer == NULL) { + if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -670,7 +670,7 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -681,10 +681,10 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -766,7 +766,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); - if (acquireFence == NULL) { + if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } @@ -972,9 +972,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCallbackCondition.wait(mCallbackMutex); } - if (frameAvailableListener != NULL) { + if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); - } else if (frameReplacedListener != NULL) { + } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } @@ -1039,7 +1039,7 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; - } else if (fence == NULL) { + } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } @@ -1069,7 +1069,7 @@ int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); - if (outValue == NULL) { + if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } @@ -1145,12 +1145,12 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, return NO_INIT; } - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } - if (output == NULL) { + if (output == nullptr) { BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } @@ -1188,10 +1188,10 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; - if (listener != NULL) { + if (listener != nullptr) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies - if (IInterface::asBinder(listener)->remoteBinder() != NULL) { + if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( static_cast<IBinder::DeathRecipient*>(this)); if (status != NO_ERROR) { @@ -1268,7 +1268,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one - if (mCore->mLinkedToDeath != NULL) { + if (mCore->mLinkedToDeath != nullptr) { sp<IBinder> token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death @@ -1278,8 +1278,8 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; - mCore->mLinkedToDeath = NULL; - mCore->mConnectedProducerListener = NULL; + mCore->mLinkedToDeath = nullptr; + mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); @@ -1302,7 +1302,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } @@ -1318,7 +1318,7 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) listener = mCore->mConsumerListener; } // Autolock scope - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; @@ -1536,7 +1536,7 @@ void BufferQueueProducer::addAndGetFrameTimestamps( Mutex::Autolock lock(mCore->mMutex); listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index f9e292e199..abd9921fa9 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -96,7 +96,7 @@ void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mGraphicBuffer = nullptr; mSlots[slotIndex].mFence = Fence::NO_FENCE; mSlots[slotIndex].mFrameNumber = 0; } @@ -110,7 +110,7 @@ void ConsumerBase::onFrameAvailable(const BufferItem& item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(item); } @@ -125,7 +125,7 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameReplaced"); listener->onFrameReplaced(item); } @@ -352,8 +352,8 @@ status_t ConsumerBase::acquireBufferLocked(BufferItem *item, return err; } - if (item->mGraphicBuffer != NULL) { - if (mSlots[item->mSlot].mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { + if (mSlots[item->mSlot].mGraphicBuffer != nullptr) { freeBufferLocked(item->mSlot); } mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer; @@ -468,7 +468,7 @@ bool ConsumerBase::stillTracking(int slot, if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { return false; } - return (mSlots[slot].mGraphicBuffer != NULL && + return (mSlots[slot].mGraphicBuffer != nullptr && mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); } diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 1757ec1cd3..f5cf1c4d5a 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -34,9 +34,9 @@ namespace android { DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != NULL) { + if (sf != nullptr) { mEventConnection = sf->createDisplayEventConnection(vsyncSource); - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } @@ -47,13 +47,13 @@ DisplayEventReceiver::~DisplayEventReceiver() { } status_t DisplayEventReceiver::initCheck() const { - if (mDataChannel != NULL) + if (mDataChannel != nullptr) return NO_ERROR; return NO_INIT; } int DisplayEventReceiver::getFd() const { - if (mDataChannel == NULL) + if (mDataChannel == nullptr) return NO_INIT; return mDataChannel->getFd(); @@ -63,7 +63,7 @@ status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { if (int32_t(count) < 0) return BAD_VALUE; - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->setVsyncRate(count); return NO_ERROR; } @@ -71,7 +71,7 @@ status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { } status_t DisplayEventReceiver::requestNextVsync() { - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->requestNextVsync(); return NO_ERROR; } diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 885efec9b9..faf02f3b53 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -46,7 +46,6 @@ #include <utils/Trace.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" #define EGL_PROTECTED_CONTENT_EXT 0x32C0 @@ -82,26 +81,6 @@ static const mat4 mtxIdentity; Mutex GLConsumer::sStaticInitLock; sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; -static bool hasEglAndroidImageCropImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglAndroidImageCrop() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglAndroidImageCropImpl(); - return hasIt; -} - static bool hasEglProtectedContentImpl() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); @@ -122,10 +101,6 @@ static bool hasEglProtectedContent() { return hasIt; } -static bool isEglImageCroppable(const Rect& crop) { - return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); -} - GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -291,7 +266,7 @@ status_t GLConsumer::releaseTexImage() { return err; } - if (mReleasedTexImage == NULL) { + if (mReleasedTexImage == nullptr) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } @@ -321,7 +296,7 @@ status_t GLConsumer::releaseTexImage() { sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( @@ -357,7 +332,7 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, // If item->mGraphicBuffer is not null, this buffer has not been acquired // before, so any prior EglImage created is using a stale buffer. This // replaces any old EglImage with a new one (using the new buffer). - if (item->mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } @@ -406,7 +381,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // ConsumerBase. // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop); + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); @@ -430,8 +405,8 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, } GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", - mCurrentTexture, mCurrentTextureImage != NULL ? - mCurrentTextureImage->graphicBufferHandle() : 0, + mCurrentTexture, mCurrentTextureImage != nullptr ? + mCurrentTextureImage->graphicBufferHandle() : nullptr, slot, mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to @@ -491,13 +466,12 @@ status_t GLConsumer::bindTextureImageLocked() { glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && - mCurrentTextureImage == NULL) { + mCurrentTextureImage == nullptr) { GLC_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop); + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -511,9 +485,7 @@ status_t GLConsumer::bindTextureImageLocked() { // forcing the creation of a new image. if ((error = glGetError()) != GL_NO_ERROR) { glBindTexture(mTexTarget, mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop, - true); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); if (result != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -655,7 +627,7 @@ status_t GLConsumer::attachToContext(uint32_t tex) { mTexName = tex; mAttached = true; - if (mCurrentTextureImage != NULL) { + if (mCurrentTextureImage != nullptr) { // This may wait for a buffer a second time. This is likely required if // this is a different context, since otherwise the wait could be skipped // by bouncing through another context. For the same context the extra @@ -676,7 +648,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (SyncFeatures::getInstance().useNativeFenceSync()) { EGLSyncKHR sync = eglCreateSyncKHR(dpy, - EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); @@ -720,7 +692,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { // Create a fence for the outstanding accesses in the current // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); if (fence == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); @@ -752,11 +724,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) { bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute && mCurrentTextureImage==NULL) { + if (needsRecompute && mCurrentTextureImage==nullptr) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } - if (needsRecompute && mCurrentTextureImage != NULL) { + if (needsRecompute && mCurrentTextureImage != nullptr) { computeCurrentTransformMatrixLocked(); } } @@ -769,8 +741,7 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { GLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureImage is NULL"); } - computeTransformMatrix(mCurrentTransformMatrix, buf, - isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, mFilteringEnabled); } @@ -863,10 +834,10 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], xform = crop * xform; } - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so GLConsumer must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. + // GLConsumer uses the GL convention where (0, 0) is the bottom-left + // corner and (1, 1) is the top-right corner. Add an additional vertical + // flip after all other transforms to map from GL convention to buffer + // queue memory layout, where (0, 0) is the top-left corner. xform = mtxFlipV * xform; memcpy(outTransform, xform.asArray(), sizeof(xform)); @@ -938,7 +909,7 @@ sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const { } return (mCurrentTextureImage == nullptr) ? - NULL : mCurrentTextureImage->graphicBuffer(); + nullptr : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { @@ -1063,8 +1034,7 @@ void GLConsumer::dumpLocked(String8& result, const char* prefix) const GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mCropRect(Rect::EMPTY_RECT) { + mEglDisplay(EGL_NO_DISPLAY) { } GLConsumer::EglImage::~EglImage() { @@ -1077,13 +1047,11 @@ GLConsumer::EglImage::~EglImage() { } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, - const Rect& cropRect, bool forceCreation) { // If there's an image and it's no longer valid, destroy it. bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool displayInvalid = mEglDisplay != eglDisplay; - bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; - if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { + if (haveImage && (displayInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } @@ -1095,14 +1063,12 @@ status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, // If there's no image, create one. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = eglDisplay; - mCropRect = cropRect; - mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); + mEglImage = createImage(mEglDisplay, mGraphicBuffer); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; - mCropRect.makeInvalid(); const sp<GraphicBuffer>& buffer = mGraphicBuffer; ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), @@ -1119,38 +1085,19 @@ void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { } EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { + const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, - EGL_IMAGE_CROP_TOP_ANDROID, crop.top, - EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, - EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; - if (!crop.isValid()) { - // No crop rect to set, so leave the crop out of the attrib array. Make - // sure to propagate the protected content attrs if they are set. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } else if (!isEglImageCroppable(crop)) { - // The crop rect is not at the origin, so we can't set the crop on the - // EGLImage because that's not allowed by the EGL_ANDROID_image_crop - // extension. In the future we can add a layered extension that - // removes this restriction if there is hardware that can support it. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } - eglInitialize(dpy, 0, 0); + eglInitialize(dpy, nullptr, nullptr); EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0b3796056d..68a6b1fe38 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -190,10 +190,10 @@ public: virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - if (outBuffer == NULL) { + if (outBuffer == nullptr) { ALOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { ALOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -301,7 +301,7 @@ public: int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - if (listener != NULL) { + if (listener != nullptr) { data.writeInt32(1); data.writeStrongBinder(IInterface::asBinder(listener)); } else { @@ -738,8 +738,8 @@ status_t BnGraphicBufferProducer::onTransact( int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); - reply->writeInt32(buffer != 0); - if (buffer != 0) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(result); @@ -797,12 +797,12 @@ status_t BnGraphicBufferProducer::onTransact( int32_t result = detachNextBuffer(&buffer, &fence); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeInt32(buffer != NULL); - if (buffer != NULL) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } - reply->writeInt32(fence != NULL); - if (fence != NULL) { + reply->writeInt32(fence != nullptr); + if (fence != nullptr) { reply->write(*fence); } } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index d2d27e8239..e66c0e5dd6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -103,59 +103,64 @@ public: } virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); - data.writeInt32(minLayerZ); - data.writeInt32(maxLayerZ); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + + return result; } virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(layerHandleBinder); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeFloat(frameScale); data.writeBool(childrenOnly); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + return result; } virtual bool authenticateSurfaceTexture( @@ -332,50 +337,38 @@ public: return result; } - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { + virtual int getActiveConfig(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); + return reply.readInt32(); + } + + virtual status_t setActiveConfig(const sp<IBinder>& display, int id) + { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result); + ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result); + ALOGE("setActiveConfig failed to writeStrongBinder: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply); + result = data.writeInt32(id); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to transact: %d", result); + ALOGE("setActiveConfig failed to writeInt32: %d", result); return result; } - result = reply.readInt32(); - if (result == NO_ERROR) { - result = reply.read(*outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to read: %d", result); - return result; - } + result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to transact: %d", result); + return result; } - return result; - } - - virtual int getActiveConfig(const sp<IBinder>& display) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); - return reply.readInt32(); - } - - virtual status_t setActiveConfig(const sp<IBinder>& display, int id) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeInt32(id); - remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); return reply.readInt32(); } @@ -457,8 +450,16 @@ public: virtual status_t clearAnimationFrameStats() { Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to transact: %d", result); + return result; + } return reply.readInt32(); } @@ -563,6 +564,44 @@ public: outLayers->clear(); return reply.readParcelableVector(outLayers); } + + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + return error; + } + error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply); + if (error != NO_ERROR) { + return error; + } + error = static_cast<status_t>(reply.readInt32()); + if (error == NO_ERROR) { + *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + } + return error; + } + + virtual bool isColorManagementUsed() const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::IS_COLOR_MANAGEMET_USED, data, &reply); + int32_t result = 0; + status_t err = reply.readInt32(&result); + if (err != NO_ERROR) { + ALOGE("ISurfaceComposer::isColorManagementUsed: error " + "retrieving result: %s (%d)", + strerror(-err), -err); + return false; + } + return result != 0; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -634,18 +673,18 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); - int32_t minLayerZ = data.readInt32(); - int32_t maxLayerZ = data.readInt32(); bool useIdentityTransform = static_cast<bool>(data.readInt32()); int32_t rotation = data.readInt32(); - status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight, - minLayerZ, maxLayerZ, useIdentityTransform, + status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); reply->writeInt32(res); if (res == NO_ERROR) { @@ -656,14 +695,16 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> layerHandleBinder = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); float frameScale = data.readFloat(); bool childrenOnly = data.readBool(); - status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale, - childrenOnly); + status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace, + reqPixelFormat, sourceCrop, frameScale, childrenOnly); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); @@ -752,26 +793,6 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } - case GET_DISPLAY_VIEWPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - Rect outViewport; - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to readStrongBinder: %d", result); - return result; - } - result = getDisplayViewport(display, &outViewport); - result = reply->writeInt32(result); - if (result == NO_ERROR) { - result = reply->write(outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to write: %d", result); - return result; - } - } - return NO_ERROR; - } case GET_ACTIVE_CONFIG: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); @@ -906,6 +927,30 @@ status_t BnSurfaceComposer::onTransact( } return result; } + case GET_COMPOSITION_PREFERENCE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + ui::Dataspace defaultDataspace; + ui::PixelFormat defaultPixelFormat; + ui::Dataspace wideColorGamutDataspace; + ui::PixelFormat wideColorGamutPixelFormat; + status_t error = + getCompositionPreference(&defaultDataspace, &defaultPixelFormat, + &wideColorGamutDataspace, &wideColorGamutPixelFormat); + reply->writeInt32(error); + if (error == NO_ERROR) { + reply->writeInt32(static_cast<int32_t>(defaultDataspace)); + reply->writeInt32(static_cast<int32_t>(defaultPixelFormat)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace)); + } + return NO_ERROR; + } + case IS_COLOR_MANAGEMET_USED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int32_t result = isColorManagementUsed() ? 1 : 0; + reply->writeInt32(result); + return NO_ERROR; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index d3dc16d30e..ccde9e08e1 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -42,7 +42,6 @@ status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeInt32(mWidth)); RETURN_ON_ERROR(parcel->writeInt32(mHeight)); RETURN_ON_ERROR(parcel->write(mCrop)); - RETURN_ON_ERROR(parcel->write(mFinalCrop)); RETURN_ON_ERROR(parcel->writeFloat(mColor.r)); RETURN_ON_ERROR(parcel->writeFloat(mColor.g)); RETURN_ON_ERROR(parcel->writeFloat(mColor.b)); @@ -81,7 +80,6 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { RETURN_ON_ERROR(parcel->readInt32(&mWidth)); RETURN_ON_ERROR(parcel->readInt32(&mHeight)); RETURN_ON_ERROR(parcel->read(mCrop)); - RETURN_ON_ERROR(parcel->read(mFinalCrop)); mColor.r = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.g = parcel->readFloat(); @@ -121,8 +119,7 @@ std::string to_string(const LayerDebugInfo& info) { info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY), info.mWidth, info.mHeight); - result.appendFormat("crop=%s, finalCrop=%s, ", - to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str()); + result.appendFormat("crop=%s, ", to_string(info.mCrop).c_str()); result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 01acc2de20..2b0a46181b 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "LayerState" + #include <utils/Errors.h> #include <binder/Parcel.h> #include <gui/ISurfaceComposerClient.h> @@ -37,19 +39,47 @@ status_t layer_state_t::write(Parcel& output) const output.writeUint32(mask); *reinterpret_cast<layer_state_t::matrix22_t *>( output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop); - output.write(finalCrop); - output.writeStrongBinder(barrierHandle); + output.write(crop_legacy); + output.writeStrongBinder(barrierHandle_legacy); output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber); + output.writeUint64(frameNumber_legacy); output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp)); + output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); output.writeStrongBinder(relativeLayerHandle); output.writeStrongBinder(parentHandleForChild); output.writeFloat(color.r); output.writeFloat(color.g); output.writeFloat(color.b); output.write(transparentRegion); + output.writeUint32(transform); + output.writeBool(transformToDisplayInverse); + output.write(crop); + if (buffer) { + output.writeBool(true); + output.write(*buffer); + } else { + output.writeBool(false); + } + if (acquireFence) { + output.writeBool(true); + output.write(*acquireFence); + } else { + output.writeBool(false); + } + output.writeUint32(static_cast<uint32_t>(dataspace)); + output.write(hdrMetadata); + output.write(surfaceDamageRegion); + output.writeInt32(api); + if (sidebandStream) { + output.writeBool(true); + output.writeNativeHandle(sidebandStream->handle()); + } else { + output.writeBool(false); + } + + memcpy(output.writeInplace(16 * sizeof(float)), + colorTransform.asArray(), 16 * sizeof(float)); + return NO_ERROR; } @@ -72,20 +102,39 @@ status_t layer_state_t::read(const Parcel& input) } else { return BAD_VALUE; } - input.read(crop); - input.read(finalCrop); - barrierHandle = input.readStrongBinder(); + input.read(crop_legacy); + barrierHandle_legacy = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); - frameNumber = input.readUint64(); + frameNumber_legacy = input.readUint64(); overrideScalingMode = input.readInt32(); - barrierGbp = - interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); + barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); parentHandleForChild = input.readStrongBinder(); color.r = input.readFloat(); color.g = input.readFloat(); color.b = input.readFloat(); input.read(transparentRegion); + transform = input.readUint32(); + transformToDisplayInverse = input.readBool(); + input.read(crop); + buffer = new GraphicBuffer(); + if (input.readBool()) { + input.read(*buffer); + } + acquireFence = new Fence(); + if (input.readBool()) { + input.read(*acquireFence); + } + dataspace = static_cast<ui::Dataspace>(input.readUint32()); + input.read(hdrMetadata); + input.read(surfaceDamageRegion); + api = input.readInt32(); + if (input.readBool()) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + } + + colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); + return NO_ERROR; } @@ -194,19 +243,15 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eLayerStackChanged; layerStack = other.layerStack; } - if (other.what & eCropChanged) { - what |= eCropChanged; - crop = other.crop; - } - if (other.what & eDeferTransaction) { - what |= eDeferTransaction; - barrierHandle = other.barrierHandle; - barrierGbp = other.barrierGbp; - frameNumber = other.frameNumber; + if (other.what & eCropChanged_legacy) { + what |= eCropChanged_legacy; + crop_legacy = other.crop_legacy; } - if (other.what & eFinalCropChanged) { - what |= eFinalCropChanged; - finalCrop = other.finalCrop; + if (other.what & eDeferTransaction_legacy) { + what |= eDeferTransaction_legacy; + barrierHandle_legacy = other.barrierHandle_legacy; + barrierGbp_legacy = other.barrierGbp_legacy; + frameNumber_legacy = other.frameNumber_legacy; } if (other.what & eOverrideScalingModeChanged) { what |= eOverrideScalingModeChanged; @@ -234,6 +279,56 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eDestroySurface) { what |= eDestroySurface; } + if (other.what & eTransformChanged) { + what |= eTransformChanged; + transform = other.transform; + } + if (other.what & eTransformToDisplayInverseChanged) { + what |= eTransformToDisplayInverseChanged; + transformToDisplayInverse = other.transformToDisplayInverse; + } + if (other.what & eCropChanged) { + what |= eCropChanged; + crop = other.crop; + } + if (other.what & eBufferChanged) { + what |= eBufferChanged; + buffer = other.buffer; + } + if (other.what & eAcquireFenceChanged) { + what |= eAcquireFenceChanged; + acquireFence = other.acquireFence; + } + if (other.what & eDataspaceChanged) { + what |= eDataspaceChanged; + dataspace = other.dataspace; + } + if (other.what & eHdrMetadataChanged) { + what |= eHdrMetadataChanged; + hdrMetadata = other.hdrMetadata; + } + if (other.what & eSurfaceDamageRegionChanged) { + what |= eSurfaceDamageRegionChanged; + surfaceDamageRegion = other.surfaceDamageRegion; + } + if (other.what & eApiChanged) { + what |= eApiChanged; + api = other.api; + } + if (other.what & eSidebandStreamChanged) { + what |= eSidebandStreamChanged; + sidebandStream = other.sidebandStream; + } + if (other.what & eColorTransformChanged) { + what |= eColorTransformChanged; + colorTransform = other.colorTransform; + } + + if ((other.what & what) != other.what) { + ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " + "other.what=0x%X what=0x%X", + other.what, what); + } } }; // namespace android diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 52c906775e..2f8e104ea0 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -38,11 +38,11 @@ namespace android { status_t StreamSplitter::createSplitter( const sp<IGraphicBufferConsumer>& inputQueue, sp<StreamSplitter>* outSplitter) { - if (inputQueue == NULL) { + if (inputQueue == nullptr) { ALOGE("createSplitter: inputQueue must not be NULL"); return BAD_VALUE; } - if (outSplitter == NULL) { + if (outSplitter == nullptr) { ALOGE("createSplitter: outSplitter must not be NULL"); return BAD_VALUE; } @@ -74,7 +74,7 @@ StreamSplitter::~StreamSplitter() { status_t StreamSplitter::addOutput( const sp<IGraphicBufferProducer>& outputQueue) { - if (outputQueue == NULL) { + if (outputQueue == nullptr) { ALOGE("addOutput: outputQueue must not be NULL"); return BAD_VALUE; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 2de14c8846..b505c6fa23 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -156,7 +156,7 @@ status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); DisplayStatInfo stats; - status_t result = composerService()->getDisplayStats(NULL, &stats); + status_t result = composerService()->getDisplayStats(nullptr, &stats); if (result != NO_ERROR) { return result; } @@ -501,7 +501,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer); - if (gbuf != NULL) { + if (gbuf != nullptr) { *buffer = gbuf.get(); *fenceFd = -1; return OK; @@ -541,7 +541,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); // this should never happen - ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); @@ -619,7 +619,7 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } @@ -1268,7 +1268,7 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, ATRACE_CALL(); ALOGV("Surface::detachNextBuffer"); - if (outBuffer == NULL || outFence == NULL) { + if (outBuffer == nullptr || outFence == nullptr) { return BAD_VALUE; } @@ -1277,8 +1277,8 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, mRemovedBuffers.clear(); } - sp<GraphicBuffer> buffer(NULL); - sp<Fence> fence(NULL); + sp<GraphicBuffer> buffer(nullptr); + sp<Fence> fence(nullptr); status_t result = mGraphicBufferProducer->detachNextBuffer( &buffer, &fence); if (result != NO_ERROR) { @@ -1286,19 +1286,19 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, } *outBuffer = buffer; - if (fence != NULL && fence->isValid()) { + if (fence != nullptr && fence->isValid()) { *outFence = fence; } else { *outFence = Fence::NO_FENCE; } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { mRemovedBuffers.push_back(mSlots[i].buffer); } - mSlots[i].buffer = NULL; + mSlots[i].buffer = nullptr; } } @@ -1349,7 +1349,7 @@ int Surface::setCrop(Rect const* rect) ATRACE_CALL(); Rect realRect(Rect::EMPTY_RECT); - if (rect == NULL || rect->isEmpty()) { + if (rect == nullptr || rect->isEmpty()) { realRect.clear(); } else { realRect = *rect; @@ -1576,7 +1576,7 @@ Dataspace Surface::getBuffersDataSpace() { void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - mSlots[i].buffer = 0; + mSlots[i].buffer = nullptr; } } @@ -1616,12 +1616,12 @@ static status_t copyBlt( // src and dst with, height and format must be identical. no verification // is done here. status_t err; - uint8_t* src_bits = NULL; + uint8_t* src_bits = nullptr; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), reinterpret_cast<void**>(&src_bits)); ALOGE_IF(err, "error locking src buffer %s", strerror(-err)); - uint8_t* dst_bits = NULL; + uint8_t* dst_bits = nullptr; err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), reinterpret_cast<void**>(&dst_bits), *dstFenceFd); ALOGE_IF(err, "error locking dst buffer %s", strerror(-err)); @@ -1669,7 +1669,7 @@ static status_t copyBlt( status_t Surface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { - if (mLockedBuffer != 0) { + if (mLockedBuffer != nullptr) { ALOGE("Surface::lock failed, already locked"); return INVALID_OPERATION; } @@ -1701,7 +1701,7 @@ status_t Surface::lock( // figure out if we can copy the frontbuffer back const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - const bool canCopyBack = (frontBuffer != 0 && + const bool canCopyBack = (frontBuffer != nullptr && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); @@ -1763,7 +1763,7 @@ status_t Surface::lock( status_t Surface::unlockAndPost() { - if (mLockedBuffer == 0) { + if (mLockedBuffer == nullptr) { ALOGE("Surface::unlockAndPost failed, no locked buffer"); return INVALID_OPERATION; } @@ -1777,7 +1777,7 @@ status_t Surface::unlockAndPost() mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; - mLockedBuffer = 0; + mLockedBuffer = nullptr; return err; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f3c6fd2f87..e5a2454db3 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -60,7 +60,7 @@ void ComposerService::connectLocked() { while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } - assert(mComposerService != NULL); + assert(mComposerService != nullptr); // Create the death listener. class DeathObserver : public IBinder::DeathRecipient { @@ -81,9 +81,9 @@ void ComposerService::connectLocked() { /*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() { ComposerService& instance = ComposerService::getInstance(); Mutex::Autolock _l(instance.mLock); - if (instance.mComposerService == NULL) { + if (instance.mComposerService == nullptr) { ComposerService::getInstance().connectLocked(); - assert(instance.mComposerService != NULL); + assert(instance.mComposerService != nullptr); ALOGD("ComposerService reconnected"); } return instance.mComposerService; @@ -92,8 +92,8 @@ void ComposerService::connectLocked() { void ComposerService::composerServiceDied() { Mutex::Autolock _l(mLock); - mComposerService = NULL; - mDeathObserver = NULL; + mComposerService = nullptr; + mDeathObserver = nullptr; } // --------------------------------------------------------------------------- @@ -240,9 +240,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize( s->w = w; s->h = h; - // Resizing a surface makes the transaction synchronous. - mForceSynchronous = true; - return *this; } @@ -344,54 +341,45 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatri return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy( const sp<SurfaceControl>& sc, const Rect& crop) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eCropChanged; - s->crop = crop; - return *this; -} - -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - s->what |= layer_state_t::eFinalCropChanged; - s->finalCrop = crop; + s->what |= layer_state_t::eCropChanged_legacy; + s->crop_legacy = crop; return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierHandle = handle; - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierHandle_legacy = handle; + s->frameNumber_legacy = frameNumber; return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierGbp = barrierSurface->getIGraphicBufferProducer(); - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer(); + s->frameNumber_legacy = frameNumber; return *this; } @@ -434,6 +422,127 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform( + const sp<SurfaceControl>& sc, uint32_t transform) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformChanged; + s->transform = transform; + return *this; +} + +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformToDisplayInverseChanged; + s->transformToDisplayInverse = transformToDisplayInverse; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( + const sp<SurfaceControl>& sc, const Rect& crop) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eCropChanged; + s->crop = crop; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( + const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eBufferChanged; + s->buffer = buffer; + return *this; +} + +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; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( + const sp<SurfaceControl>& sc, ui::Dataspace dataspace) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDataspaceChanged; + s->dataspace = dataspace; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata( + const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eHdrMetadataChanged; + s->hdrMetadata = hdrMetadata; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfaceDamageRegion( + const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSurfaceDamageRegionChanged; + s->surfaceDamageRegion = surfaceDamageRegion; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApi( + const sp<SurfaceControl>& sc, int32_t api) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eApiChanged; + s->api = api; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSidebandStream( + const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSidebandStreamChanged; + s->sidebandStream = sidebandStream; + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren( const sp<SurfaceControl>& sc) { layer_state_t* s = getLayerState(sc); @@ -493,6 +602,18 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroyS return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( + const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eColorTransformChanged; + s->colorTransform = mat4(matrix, translation); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -571,12 +692,12 @@ SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& c void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != 0 && mStatus == NO_INIT) { + if (sf != nullptr && mStatus == NO_INIT) { auto rootProducer = mParent.promote(); sp<ISurfaceComposerClient> conn; conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) : sf->createConnection(); - if (conn != 0) { + if (conn != nullptr) { mClient = conn; mStatus = NO_ERROR; } @@ -606,7 +727,7 @@ void SurfaceComposerClient::dispose() { // this can be called more than once. sp<ISurfaceComposerClient> client; Mutex::Autolock _lm(mLock); - if (mClient != 0) { + if (mClient != nullptr) { client = mClient; // hold ref while lock is held mClient.clear(); } @@ -718,10 +839,6 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, return NO_ERROR; } -status_t SurfaceComposerClient::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - return ComposerService::getComposerService()->getDisplayViewport(display, outViewport); -} - int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveConfig(display); } @@ -749,6 +866,14 @@ void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token, ComposerService::getComposerService()->setPowerMode(token, mode); } +status_t SurfaceComposerClient::getCompositionPreference( + ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) { + return ComposerService::getComposerService() + ->getCompositionPreference(defaultDataspace, defaultPixelFormat, + wideColorGamutDataspace, wideColorGamutPixelFormat); +} + status_t SurfaceComposerClient::clearAnimationFrameStats() { return ComposerService::getComposerService()->clearAnimationFrameStats(); } @@ -765,14 +890,14 @@ status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display, // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight, minLayerZ, - maxLayerZ, useIdentityTransform, + if (s == nullptr) return NO_INIT; + status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, + reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); if (ret != NO_ERROR) { return ret; @@ -780,21 +905,25 @@ status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, return ret; } -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - false /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, false /* childrenOnly */); return ret; } -status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - true /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, true /* childrenOnly */); return ret; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 5eafbb3555..3a6dfdada5 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -86,7 +86,7 @@ void SurfaceControl::clear() } void SurfaceControl::disconnect() { - if (mGraphicBufferProducer != NULL) { + if (mGraphicBufferProducer != nullptr) { mGraphicBufferProducer->disconnect( BufferQueueCore::CURRENTLY_CONNECTED_API); } @@ -95,28 +95,28 @@ void SurfaceControl::disconnect() { bool SurfaceControl::isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { - if (lhs == 0 || rhs == 0) + if (lhs == nullptr || rhs == nullptr) return false; return lhs->mHandle == rhs->mHandle; } status_t SurfaceControl::clearLayerFrameStats() const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->clearLayerFrameStats(mHandle); } status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->getLayerFrameStats(mHandle, outStats); } status_t SurfaceControl::validate() const { - if (mHandle==0 || mClient==0) { + if (mHandle==nullptr || mClient==nullptr) { ALOGE("invalid handle (%p) or client (%p)", mHandle.get(), mClient.get()); return NO_INIT; @@ -128,7 +128,7 @@ status_t SurfaceControl::writeSurfaceToParcel( const sp<SurfaceControl>& control, Parcel* parcel) { sp<IGraphicBufferProducer> bp; - if (control != NULL) { + if (control != nullptr) { bp = control->mGraphicBufferProducer; } return parcel->writeStrongBinder(IInterface::asBinder(bp)); @@ -146,7 +146,7 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() const sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); - if (mSurfaceData == 0) { + if (mSurfaceData == nullptr) { return generateSurfaceLocked(); } return mSurfaceData; diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index afa15c5cda..fcae05c8ad 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -41,7 +41,7 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), // This can only be called after EGL has been initialized; otherwise the // check below will abort. const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed"); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index 3b89291dc8..8af1a67f12 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -100,7 +100,12 @@ inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) { */ // convert: Return<Status> -> status_t inline status_t toStatusT(Return<Status> const& t) { - return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR; + if (t.isOk()) { + return static_cast<status_t>(static_cast<Status>(t)); + } else if (t.isDeadObject()) { + return DEAD_OBJECT; + } + return UNKNOWN_ERROR; } /** @@ -111,7 +116,7 @@ inline status_t toStatusT(Return<Status> const& t) { */ // convert: Return<void> -> status_t inline status_t toStatusT(Return<void> const& t) { - return t.isOk() ? OK : UNKNOWN_ERROR; + return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); } /** diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h index 23c9909826..f7af19b20e 100644 --- a/libs/gui/include/gui/BufferHubProducer.h +++ b/libs/gui/include/gui/BufferHubProducer.h @@ -165,6 +165,10 @@ private: // buffers are acquired by the consumer, we can't . status_t FreeAllBuffers(); + // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been + // locked already. + status_t DetachBufferLocked(size_t slot); + // Concreate implementation backed by BufferHubBuffer. std::shared_ptr<dvr::ProducerQueue> queue_; diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index d375611e5b..806fbe8aa0 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -70,7 +70,7 @@ class CpuConsumer : public ConsumerBase uint32_t chromaStep; LockedBuffer() : - data(NULL), + data(nullptr), width(0), height(0), format(PIXEL_FORMAT_NONE), @@ -82,8 +82,8 @@ class CpuConsumer : public ConsumerBase dataSpace(HAL_DATASPACE_UNKNOWN), frameNumber(0), flexFormat(PIXEL_FORMAT_NONE), - dataCb(NULL), - dataCr(NULL), + dataCb(nullptr), + dataCr(nullptr), chromaStride(0), chromaStep(0) {} diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 71ed3bf239..46a99e124c 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -140,7 +140,8 @@ public: // Scale the crop down horizontally or vertically such that it has the // same aspect ratio as the buffer does. - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, + uint32_t bufferHeight); // getTimestamp retrieves the timestamp associated with the texture image // set by the most recent call to updateTexImage. @@ -305,7 +306,6 @@ private: // createIfNeeded creates an EGLImage if required (we haven't created // one yet, or the EGLDisplay or crop-rect has changed). status_t createIfNeeded(EGLDisplay display, - const Rect& cropRect, bool forceCreate = false); // This calls glEGLImageTargetTexture2DOES to bind the image to the @@ -314,7 +314,7 @@ private: const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } const native_handle* graphicBufferHandle() { - return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle; + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; } private: @@ -324,7 +324,7 @@ private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop); + const sp<GraphicBuffer>& graphicBuffer); // Disallow copying EglImage(const EglImage& rhs); diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 887654e05b..8ff8d81cf6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -345,7 +345,7 @@ public: *outScalingMode = scalingMode; *outTransform = transform; *outFence = fence; - if (outStickyTransform != NULL) { + if (outStickyTransform != nullptr) { *outStickyTransform = stickyTransform; } if (outGetFrameTimestamps) { diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 99a3a75502..9316ae69bc 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -157,9 +157,6 @@ public: virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) = 0; - /* returns display viewport information of the given display */ - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) = 0; - /* indicates which of the configurations returned by getDisplayInfo is * currently active */ virtual int getActiveConfig(const sp<IBinder>& display) = 0; @@ -174,21 +171,86 @@ public: virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode) = 0; - /* Capture the specified screen. requires READ_FRAME_BUFFER permission - * This function will fail if there is a secure window on screen. + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. */ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, Rotation rotation = eRotateNone) = 0; + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen + * into an sRGB buffer with RGBA_8888 pixel format. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. + */ + virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + bool useIdentityTransform, Rotation rotation = eRotateNone) { + return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation); + } /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. */ virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale = 1.0, bool childrenOnly = false) = 0; + /** + * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, + * potentially ignoring the root node. + */ + status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const Rect& sourceCrop, float frameScale = 1.0, + bool childrenOnly = false) { + return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly); + } + /* Clears the frame statistics for animations. * * Requires the ACCESS_SURFACE_FLINGER permission. @@ -217,18 +279,32 @@ public: * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; + + virtual bool isColorManagementUsed() const = 0; + + /* Gets the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const = 0; }; // ---------------------------------------------------------------------------- class BnSurfaceComposer: public BnInterface<ISurfaceComposer> { public: - enum { + enum ISurfaceComposerTag { // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, - UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC + CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, DESTROY_DISPLAY, @@ -239,7 +315,7 @@ public: GET_DISPLAY_CONFIGS, GET_ACTIVE_CONFIG, SET_ACTIVE_CONFIG, - CONNECT_DISPLAY, + CONNECT_DISPLAY_UNUSED, // unused, fails permissions check CAPTURE_SCREEN, CAPTURE_LAYERS, CLEAR_ANIMATION_FRAME_STATS, @@ -254,7 +330,8 @@ public: INJECT_VSYNC, GET_LAYER_DEBUG_INFO, CREATE_SCOPED_CONNECTION, - GET_DISPLAY_VIEWPORT + GET_COMPOSITION_PREFERENCE, + IS_COLOR_MANAGEMET_USED, }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 8dfc99a4b7..82b01b8cf0 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -40,8 +40,10 @@ public: eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, - eFXSurfaceNormal = 0x00000000, + eFXSurfaceBufferQueue = 0x00000000, eFXSurfaceColor = 0x00020000, + eFXSurfaceBufferState = 0x00040000, + eFXSurfaceContainer = 0x00080000, eFXSurfaceMask = 0x000F0000, }; diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 92bd8c5b28..66a7b4dc06 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -52,7 +52,6 @@ public: int32_t mWidth = -1; int32_t mHeight = -1; Rect mCrop = Rect::INVALID_RECT; - Rect mFinalCrop = Rect::INVALID_RECT; half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf); uint32_t mFlags = 0; PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 788962e490..e06e2b14b9 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -22,10 +22,12 @@ #include <utils/Errors.h> -#include <ui/Region.h> -#include <ui/Rect.h> #include <gui/IGraphicBufferProducer.h> +#include <math/mat4.h> #include <math/vec3.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> namespace android { @@ -36,113 +38,146 @@ class ISurfaceComposerClient; * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { - - enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSecure = 0x80, // SECURE }; enum { - ePositionChanged = 0x00000001, - eLayerChanged = 0x00000002, - eSizeChanged = 0x00000004, - eAlphaChanged = 0x00000008, - eMatrixChanged = 0x00000010, - eTransparentRegionChanged = 0x00000020, - eFlagsChanged = 0x00000040, - eLayerStackChanged = 0x00000080, - eCropChanged = 0x00000100, - eDeferTransaction = 0x00000200, - eFinalCropChanged = 0x00000400, - eOverrideScalingModeChanged = 0x00000800, - eGeometryAppliesWithResize = 0x00001000, - eReparentChildren = 0x00002000, - eDetachChildren = 0x00004000, - eRelativeLayerChanged = 0x00008000, - eReparent = 0x00010000, - eColorChanged = 0x00020000, - eDestroySurface = 0x00040000 + ePositionChanged = 0x00000001, + eLayerChanged = 0x00000002, + eSizeChanged = 0x00000004, + eAlphaChanged = 0x00000008, + eMatrixChanged = 0x00000010, + eTransparentRegionChanged = 0x00000020, + eFlagsChanged = 0x00000040, + eLayerStackChanged = 0x00000080, + eCropChanged_legacy = 0x00000100, + eDeferTransaction_legacy = 0x00000200, + eOverrideScalingModeChanged = 0x00000400, + eGeometryAppliesWithResize = 0x00000800, + eReparentChildren = 0x00001000, + eDetachChildren = 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, }; layer_state_t() - : what(0), - x(0), y(0), z(0), w(0), h(0), layerStack(0), - alpha(0), flags(0), mask(0), - reserved(0), crop(Rect::INVALID_RECT), - finalCrop(Rect::INVALID_RECT), frameNumber(0), - overrideScalingMode(-1) - { + : what(0), + x(0), + y(0), + z(0), + w(0), + h(0), + layerStack(0), + alpha(0), + flags(0), + mask(0), + reserved(0), + crop_legacy(Rect::INVALID_RECT), + frameNumber_legacy(0), + overrideScalingMode(-1), + transform(0), + transformToDisplayInverse(false), + crop(Rect::INVALID_RECT), + dataspace(ui::Dataspace::UNKNOWN), + surfaceDamageRegion(), + api(-1), + colorTransform(mat4()) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; + hdrMetadata.validTypes = 0; } void merge(const layer_state_t& other); - status_t write(Parcel& output) const; - status_t read(const Parcel& input); - - struct matrix22_t { - float dsdx{0}; - float dtdx{0}; - float dtdy{0}; - float dsdy{0}; - }; - sp<IBinder> surface; - uint32_t what; - float x; - float y; - int32_t z; - uint32_t w; - uint32_t h; - uint32_t layerStack; - float alpha; - uint8_t flags; - uint8_t mask; - uint8_t reserved; - matrix22_t matrix; - Rect crop; - Rect finalCrop; - sp<IBinder> barrierHandle; - sp<IBinder> reparentHandle; - uint64_t frameNumber; - int32_t overrideScalingMode; - - sp<IGraphicBufferProducer> barrierGbp; - - sp<IBinder> relativeLayerHandle; - - sp<IBinder> parentHandleForChild; - - half3 color; - - // non POD must be last. see write/read - Region transparentRegion; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); + + struct matrix22_t { + float dsdx{0}; + float dtdx{0}; + float dtdy{0}; + float dsdy{0}; + }; + sp<IBinder> surface; + uint32_t what; + float x; + float y; + int32_t z; + uint32_t w; + uint32_t h; + uint32_t layerStack; + float alpha; + uint8_t flags; + uint8_t mask; + uint8_t reserved; + matrix22_t matrix; + Rect crop_legacy; + sp<IBinder> barrierHandle_legacy; + sp<IBinder> reparentHandle; + uint64_t frameNumber_legacy; + int32_t overrideScalingMode; + + sp<IGraphicBufferProducer> barrierGbp_legacy; + + sp<IBinder> relativeLayerHandle; + + sp<IBinder> parentHandleForChild; + + half3 color; + + // non POD must be last. see write/read + Region transparentRegion; + + uint32_t transform; + bool transformToDisplayInverse; + Rect crop; + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + sp<NativeHandle> sidebandStream; + mat4 colorTransform; }; struct ComposerState { sp<ISurfaceComposerClient> client; layer_state_t state; - status_t write(Parcel& output) const; - status_t read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; struct DisplayState { - enum { - eOrientationDefault = 0, - eOrientation90 = 1, - eOrientation180 = 2, - eOrientation270 = 3, - eOrientationUnchanged = 4, - eOrientationSwapMask = 0x01 + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + eOrientationUnchanged = 4, + eOrientationSwapMask = 0x01 }; enum { - eSurfaceChanged = 0x01, - eLayerStackChanged = 0x02, - eDisplayProjectionChanged = 0x04, - eDisplaySizeChanged = 0x08 + eSurfaceChanged = 0x01, + eLayerStackChanged = 0x02, + eDisplayProjectionChanged = 0x04, + eDisplaySizeChanged = 0x08 }; DisplayState(); @@ -152,29 +187,40 @@ struct DisplayState { sp<IBinder> token; sp<IGraphicBufferProducer> surface; uint32_t layerStack; + + // These states define how layers are projected onto the physical display. + // + // Layers are first clipped to `viewport'. They are then translated and + // scaled from `viewport' to `frame'. Finally, they are rotated according + // to `orientation', `width', and `height'. + // + // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, + // 10, 420, 210), and the size of the display is WxH. When orientation is + // 0, layers will be scaled by a factor of 2 and translated by (20, 10). + // When orientation is 1, layers will be additionally rotated by 90 + // degrees around the origin clockwise and translated by (W, 0). uint32_t orientation; Rect viewport; Rect frame; + uint32_t width, height; + status_t write(Parcel& output) const; status_t read(const Parcel& input); }; -static inline -int compare_type(const ComposerState& lhs, const ComposerState& rhs) { +static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { if (lhs.client < rhs.client) return -1; if (lhs.client > rhs.client) return 1; - if (lhs.state.surface < rhs.state.surface) return -1; - if (lhs.state.surface > rhs.state.surface) return 1; + if (lhs.state.surface < rhs.state.surface) return -1; + if (lhs.state.surface > rhs.state.surface) return 1; return 0; } -static inline -int compare_type(const DisplayState& lhs, const DisplayState& rhs) { +static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) { return compare_type(lhs.token, rhs.token); } }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H - diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 9aeafae198..32ee595d45 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -80,7 +80,7 @@ public: /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp<Surface>& surface) { - return surface != NULL && surface->getIGraphicBufferProducer() != NULL; + return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index ad8a8b09d0..8ccee05ba4 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -69,7 +69,7 @@ public: // callback when the composer is dies status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient, - void* cookie = NULL, uint32_t flags = 0); + void* cookie = nullptr, uint32_t flags = 0); // Get a list of supported configurations for a given display static status_t getDisplayConfigs(const sp<IBinder>& display, @@ -79,9 +79,6 @@ public: static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info); - // Get the display viewport for the given display - static status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport); - // Get the index of the current active configuration (relative to the list // returned by getDisplayInfo) static int getActiveConfig(const sp<IBinder>& display); @@ -104,6 +101,16 @@ public: /* Triggers screen on/off or low power mode and waits for it to complete */ static void setDisplayPowerMode(const sp<IBinder>& display, int mode); + /* Returns the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + */ + static status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat); + // ------------------------------------------------------------------------ // surface creation / destruction @@ -203,22 +210,20 @@ public: float alpha); Transaction& setMatrix(const sp<SurfaceControl>& sc, float dsdx, float dtdx, float dtdy, float dsdy); - Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); - Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber. If the Layer identified // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, - uint64_t frameNumber); - // A variant of deferTransactionUntil which identifies the Layer we wait for by + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, uint64_t frameNumber); + // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by // Surface instead of Handle. Useful for clients which may not have the // SurfaceControl for some of their Surfaces. Otherwise behaves identically. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, - uint64_t frameNumber); + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. Transaction& reparentChildren(const sp<SurfaceControl>& sc, const sp<IBinder>& newParentHandle); @@ -231,6 +236,20 @@ public: Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); + Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform); + Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse); + Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer); + Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence); + Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); + Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); + Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, + const Region& surfaceDamageRegion); + Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api); + Transaction& setSidebandStream(const sp<SurfaceControl>& sc, + const sp<NativeHandle>& sidebandStream); + // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. // The child SurfaceControls will not throw exceptions or return errors, @@ -256,6 +275,10 @@ public: Transaction& destroySurface(const sp<SurfaceControl>& sc); + // Set a color transform matrix on the given layer on the built-in display. + Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, + const vec3& translation); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -312,13 +335,16 @@ class ScreenshotClient { public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale, - sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer); + static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer); }; diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index bd987dd638..ccb30fa8e7 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -48,11 +48,11 @@ public: void writeToParcel(Parcel* parcel); static bool isValid(const sp<SurfaceControl>& surface) { - return (surface != 0) && surface->isValid(); + return (surface != nullptr) && surface->isValid(); } bool isValid() { - return mHandle!=0 && mClient!=0; + return mHandle!=nullptr && mClient!=nullptr; } static bool isSameSurface( diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 01e90e0eb8..7ecadf836c 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -44,15 +44,19 @@ cc_test { "libgui", "libhidlbase", "libhidltransport", + "libinput", "libui", "libutils", "libnativewindow" ], } -// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// This test has a main method, and requires a separate binary to be built. +// To add move tests like this, just add additional cc_test statements, +// as opposed to adding more source files to this one. cc_test { - name: "libgui_separate_binary_test", + name: "SurfaceParcelable_test", test_suites: ["device-tests"], clang: true, @@ -61,7 +65,6 @@ cc_test { "-Werror", ], - test_per_src: true, srcs: [ "SurfaceParcelable_test.cpp", ], diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 9a208593ab..119e888edb 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -61,7 +61,7 @@ protected: } void GetMinUndequeuedBufferCount(int* bufferCount) { - ASSERT_TRUE(bufferCount != NULL); + ASSERT_TRUE(bufferCount != nullptr); ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); ASSERT_GE(*bufferCount, 0); @@ -82,7 +82,7 @@ protected: sp<Fence> fence; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, - &scalingMode, &transform, &fence, NULL); + &scalingMode, &transform, &fence, nullptr); ASSERT_EQ(timestamp, item.mTimestamp); ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp); ASSERT_EQ(dataSpace, item.mDataSpace); @@ -128,17 +128,17 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { sp<IBinder> binderProducer = serviceManager->getService(PRODUCER_NAME); mProducer = interface_cast<IGraphicBufferProducer>(binderProducer); - EXPECT_TRUE(mProducer != NULL); + EXPECT_TRUE(mProducer != nullptr); sp<IBinder> binderConsumer = serviceManager->getService(CONSUMER_NAME); mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer); - EXPECT_TRUE(mConsumer != NULL); + EXPECT_TRUE(mConsumer != nullptr); sp<DummyConsumer> dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, - mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp<Fence> fence; @@ -353,8 +353,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { ASSERT_EQ(OK, buffer->unlock()); int newSlot; - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, @@ -412,8 +412,8 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { int newSlot; sp<GraphicBuffer> safeToClobberBuffer; - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY, diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 36be7d9368..00e32d9124 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -484,12 +484,12 @@ void produceOneFrame(const sp<ANativeWindow>& anw, err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); ASSERT_NO_ERROR(err, "dequeueBuffer error: "); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); *stride = buf->getStride(); - uint8_t* img = NULL; + uint8_t* img = nullptr; ALOGVV("Lock buffer from %p for write", anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); @@ -554,7 +554,7 @@ TEST_P(CpuConsumerTest, FromCpuSingle) { err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -595,7 +595,7 @@ TEST_P(CpuConsumerTest, FromCpuManyInQueue) { err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -637,7 +637,7 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { err = mCC->lockNextBuffer(&b[i]); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b[i].data != NULL); + ASSERT_TRUE(b[i].data != nullptr); EXPECT_EQ(params.width, b[i].width); EXPECT_EQ(params.height, b[i].height); EXPECT_EQ(params.format, b[i].format); @@ -660,7 +660,7 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { err = mCC->lockNextBuffer(&bTooMuch); ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); - ASSERT_TRUE(bTooMuch.data != NULL); + ASSERT_TRUE(bTooMuch.data != nullptr); EXPECT_EQ(params.width, bTooMuch.width); EXPECT_EQ(params.height, bTooMuch.height); EXPECT_EQ(params.format, bTooMuch.format); diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index ccd674fcb8..b60995a624 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -93,11 +93,11 @@ void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index a91552f7fe..a1405fcb11 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -50,7 +50,7 @@ void GLTest::SetUp() { ASSERT_EQ(EGL_SUCCESS, eglGetError()); char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); - if (displaySecsEnv != NULL) { + if (displaySecsEnv != nullptr) { mDisplaySecs = atoi(displaySecsEnv); if (mDisplaySecs < 0) { mDisplaySecs = 0; @@ -67,7 +67,7 @@ void GLTest::SetUp() { String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -117,7 +117,7 @@ void GLTest::TearDown() { sleep(mDisplaySecs); } - if (mComposerClient != NULL) { + if (mComposerClient != nullptr) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { @@ -171,7 +171,7 @@ EGLint GLTest::getSurfaceHeight() { EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, sp<ANativeWindow>& window) const { - return eglCreateWindowSurface(display, config, window.get(), NULL); + return eglCreateWindowSurface(display, config, window.get(), nullptr); } ::testing::AssertionResult GLTest::checkPixel(int x, int y, @@ -256,7 +256,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, GLuint shader = glCreateShader(shaderType); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (shader) { - glShaderSource(shader, 1, &pSource, NULL); + glShaderSource(shader, 1, &pSource, nullptr); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glCompileShader(shader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); @@ -270,7 +270,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { - glGetShaderInfoLog(shader, infoLen, NULL, buf); + glGetShaderInfoLog(shader, infoLen, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -278,7 +278,7 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, } else { char* buf = (char*) malloc(0x1000); if (buf) { - glGetShaderInfoLog(shader, 0x1000, NULL, buf); + glGetShaderInfoLog(shader, 0x1000, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -322,7 +322,7 @@ void GLTest::createProgram(const char* pVertexSource, if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { - glGetProgramInfoLog(program, bufLength, NULL, buf); + glGetProgramInfoLog(program, bufLength, nullptr, buf); printf("Program link log:\n%s\n", buf); free(buf); FAIL(); diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h index f0d27a8a34..f290b3c68d 100644 --- a/libs/gui/tests/GLTest.h +++ b/libs/gui/tests/GLTest.h @@ -39,7 +39,7 @@ protected: mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mGlConfig(NULL) { + mGlConfig(nullptr) { } virtual void SetUp(); diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index a35cf11174..aef7aed52c 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -228,9 +228,9 @@ protected: void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, sp<GraphicBuffer> *buffer) { - ASSERT_TRUE(slot != NULL); - ASSERT_TRUE(fence != NULL); - ASSERT_TRUE(buffer != NULL); + ASSERT_TRUE(slot != nullptr); + ASSERT_TRUE(fence != nullptr); + ASSERT_TRUE(buffer != nullptr); ASSERT_NO_FATAL_FAILURE(ConnectProducer()); @@ -263,7 +263,7 @@ TEST_P(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, - /*output*/NULL)); + /*output*/nullptr)); // Invalid API returns bad value EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, @@ -359,7 +359,7 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP // Value was NULL - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr)); ASSERT_OK(mConsumer->consumerDisconnect()); @@ -465,7 +465,7 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // Fence was NULL { - sp<Fence> nullFence = NULL; + sp<Fence> nullFence = nullptr; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); @@ -695,10 +695,7 @@ TEST_P(IGraphicBufferProducerTest, sp<Fence> fence; sp<GraphicBuffer> buffer; - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); - } + ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); } TEST_P(IGraphicBufferProducerTest, @@ -735,10 +732,7 @@ TEST_P(IGraphicBufferProducerTest, ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); - } + ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); } TEST_P(IGraphicBufferProducerTest, @@ -778,18 +772,29 @@ TEST_P(IGraphicBufferProducerTest, sp<GraphicBuffer> buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_OK(mProducer->detachBuffer(slot)); - } + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/69981968): Implement BufferHubProducer::attachBuffer - ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); - } + ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); +} + +TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { + int slot = -1; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + + setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); + + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); + + EXPECT_OK(mProducer->attachBuffer(&slot, buffer)); + EXPECT_OK(buffer->initCheck()); } #if USE_BUFFER_HUB_AS_BUFFER_QUEUE diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 3a25ac59ca..7d3d4aa412 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -47,7 +47,7 @@ protected: GLTest::TearDown(); } virtual EGLint const* getContextAttribs() { - return NULL; + return nullptr; } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { @@ -105,7 +105,7 @@ TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { glClear(GL_COLOR_BUFFER_BIT); for (int i=0 ; i<8 ; i++) { - mSurface->lock(&buffer, NULL); + mSurface->lock(&buffer, nullptr); memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); mSurface->unlockAndPost(); diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index d5b2f004ed..65e09f2540 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -29,7 +29,6 @@ #include <utils/Thread.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" namespace android { @@ -39,7 +38,7 @@ protected: mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mEglConfig(NULL) { + mEglConfig(nullptr) { } virtual void SetUp() { @@ -82,7 +81,7 @@ protected: ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -127,7 +126,7 @@ protected: TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer()); - ASSERT_TRUE(ist != NULL); + ASSERT_TRUE(ist != nullptr); } TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { @@ -155,7 +154,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EGLConfig myConfig = {0}; + EGLConfig myConfig = {nullptr}; EGLint numConfigs = 0; EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -172,7 +171,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(), - NULL); + nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -185,7 +184,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) { - EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL); + EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -638,18 +637,6 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { - // Query to see if the image crop extension exists - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle; - android_native_buffer_t* buf[3]; float mtx[16] = {}; android_native_rect_t crop; @@ -669,17 +656,15 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); - // If the egl image crop extension is not present, this accounts for the - // .5 texel shrink for each edge that's included in the transform matrix - // to avoid texturing outside the crop region. Otherwise the crop is not - // included in the transform matrix. - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]); + // This accounts for the .5 texel shrink for each edge that's included in + // the transform matrix to avoid texturing outside the crop region. + EXPECT_EQ(0.5f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); - EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]); + EXPECT_EQ(-0.5f, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); @@ -688,8 +673,8 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); - EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]); - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]); + EXPECT_EQ(0.0625f, mtx[12]); + EXPECT_EQ(0.5625f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } @@ -753,7 +738,7 @@ protected: ASSERT_EQ(EGL_SUCCESS, eglGetError()); mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, - 0); + nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -765,7 +750,7 @@ protected: GLConsumer::TEXTURE_EXTERNAL, true, false)); sp<Surface> stc(new Surface(producer)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, - static_cast<ANativeWindow*>(stc.get()), NULL); + static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]); } diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h index 7f1ae84c48..70f988de11 100644 --- a/libs/gui/tests/SurfaceTextureFBO.h +++ b/libs/gui/tests/SurfaceTextureFBO.h @@ -34,7 +34,7 @@ protected: glGenTextures(1, &mFboTex); glBindTexture(GL_TEXTURE_2D, mFboTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), - getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index 0134273a07..f34561f668 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -39,12 +39,12 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with green - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, 0, 255); @@ -63,7 +63,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); buf = GraphicBuffer::from(anb); diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h index 2ce20eb2b1..03975b1261 100644 --- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h @@ -158,7 +158,7 @@ protected: } virtual void TearDown() { - if (mProducerThread != NULL) { + if (mProducerThread != nullptr) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); @@ -167,7 +167,7 @@ protected: } void runProducerThread(const sp<ProducerThread> producerThread) { - ASSERT_TRUE(mProducerThread == NULL); + ASSERT_TRUE(mProducerThread == nullptr); mProducerThread = producerThread; producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, mProducerEglContext); diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h index 5d43a48898..3a87c12cf6 100644 --- a/libs/gui/tests/SurfaceTextureGLToGL.h +++ b/libs/gui/tests/SurfaceTextureGLToGL.h @@ -38,7 +38,7 @@ protected: void SetUpWindowAndContext() { mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + mANW.get(), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 56392867ea..e2b4f3d035 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -40,12 +40,12 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -90,12 +90,12 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -155,11 +155,11 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); @@ -234,7 +234,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } @@ -248,7 +248,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; int yuvTexStrideU = yuvTexStrideV; - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); // Gray out all the test pixels first, so we're more likely to @@ -457,7 +457,7 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -641,7 +641,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -654,7 +654,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 6e196bfac8..a3e9249ac2 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -72,7 +72,7 @@ protected: mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -81,7 +81,7 @@ protected: .apply()); mSurface = mSurfaceControl->getSurface(); - ASSERT_TRUE(mSurface != NULL); + ASSERT_TRUE(mSurface != nullptr); } virtual void TearDown() { @@ -134,8 +134,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp<IBinder> display(sf->getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -145,7 +146,7 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); - ANativeWindowBuffer* buf = 0; + ANativeWindowBuffer* buf = nullptr; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { @@ -165,8 +166,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -581,9 +583,6 @@ public: Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; } status_t getDisplayStats(const sp<IBinder>& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } - status_t getDisplayViewport(const sp<IBinder>& /*display*/, Rect* /*outViewport*/) override { - return NO_ERROR; - } int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/) override { @@ -599,15 +598,19 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, - sp<GraphicBuffer>* /*outBuffer*/, - Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, - bool /*useIdentityTransform*/, - Rotation /*rotation*/) override { return NO_ERROR; } + status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, + uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + bool /*useIdentityTransform*/, Rotation /*rotation*/) override { + return NO_ERROR; + } virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/, - sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, + const Rect& /*sourceCrop*/, float /*frameScale*/, + bool /*childrenOnly*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -625,6 +628,14 @@ public: status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { return NO_ERROR; } + status_t getCompositionPreference( + ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/, + ui::Dataspace* /*outWideColorGamutDataspace*/, + ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override { + return NO_ERROR; + } + + virtual bool isColorManagementUsed() const { return false; } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index a2d6a8a798..72558a6d91 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -47,11 +47,14 @@ cc_library { "InputTransport.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", + "InputApplication.cpp", + "InputWindow.cpp" ], shared_libs: [ "libutils", "libbinder", + "libui" ], sanitize: { diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a6246636a3..8a15e2f40f 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -31,14 +31,16 @@ namespace android { // --- InputEvent --- -void InputEvent::initialize(int32_t deviceId, int32_t source) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; + mDisplayId = displayId; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; + mDisplayId = from.mDisplayId; } // --- KeyEvent --- @@ -54,6 +56,7 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { void KeyEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -62,7 +65,7 @@ void KeyEvent::initialize( int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -215,6 +218,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { void MotionEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, @@ -230,7 +234,7 @@ void MotionEvent::initialize( size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -250,7 +254,7 @@ void MotionEvent::initialize( } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; @@ -431,6 +435,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); + mDisplayId = parcel->readInt32(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -480,6 +485,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); + parcel->writeInt32(mDisplayId); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); diff --git a/services/inputflinger/InputApplication.cpp b/libs/input/InputApplication.cpp index 9e90631840..a0d1668029 100644 --- a/services/inputflinger/InputApplication.cpp +++ b/libs/input/InputApplication.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "InputApplication" -#include "InputApplication.h" +#include <input/InputApplication.h> #include <android/log.h> @@ -25,7 +25,7 @@ namespace android { // --- InputApplicationHandle --- InputApplicationHandle::InputApplicationHandle() : - mInfo(NULL) { + mInfo(nullptr) { } InputApplicationHandle::~InputApplicationHandle() { @@ -35,7 +35,7 @@ InputApplicationHandle::~InputApplicationHandle() { void InputApplicationHandle::releaseInfo() { if (mInfo) { delete mInfo; - mInfo = NULL; + mInfo = nullptr; } } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4287abeb7d..778c4539fa 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -20,9 +20,12 @@ #include <unistd.h> #include <ctype.h> +#include <android-base/stringprintf.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> +using android::base::StringPrintf; + namespace android { static const char* CONFIGURATION_FILE_DIR[] = { @@ -41,8 +44,8 @@ static bool isValidNameChar(char ch) { return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); } -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { +static void appendInputDeviceConfigurationFileRelativePath(std::string& path, + const std::string& name, InputDeviceConfigurationFileType type) { path.append(CONFIGURATION_FILE_DIR[type]); for (size_t i = 0; i < name.length(); i++) { char ch = name[i]; @@ -54,28 +57,28 @@ static void appendInputDeviceConfigurationFileRelativePath(String8& path, path.append(CONFIGURATION_FILE_EXTENSION[type]); } -String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - String8 versionPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x_Version_%04x", + std::string versionPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", deviceIdentifier.vendor, deviceIdentifier.product, deviceIdentifier.version), - type)); - if (!versionPath.isEmpty()) { + type); + if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - String8 productPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x", + std::string productPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x", deviceIdentifier.vendor, deviceIdentifier.product), - type)); - if (!productPath.isEmpty()) { + type); + if (!productPath.empty()) { return productPath; } } @@ -84,22 +87,25 @@ String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); } -String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type) { +std::string getInputDeviceConfigurationFilePathByName( + const std::string& name, InputDeviceConfigurationFileType type) { // Search system repository. - String8 path; + std::string path; // Treblized input device config files will be located /odm/usr or /vendor/usr. const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; for (size_t i = 0; i < size(rootsForPartition); i++) { - path.setTo(rootsForPartition[i]); - path.append("/usr/"); + if (rootsForPartition[i] == nullptr) { + continue; + } + path = rootsForPartition[i]; + path += "/usr/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system provided input device configuration file: path='%s'", - path.string()); + path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -109,13 +115,17 @@ String8 getInputDeviceConfigurationFilePathByName( // Search user repository. // TODO Should only look here if not in safe mode. - path.setTo(getenv("ANDROID_DATA")); - path.append("/system/devices/"); + path = ""; + char *androidData = getenv("ANDROID_DATA"); + if (androidData != nullptr) { + path += androidData; + } + path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); + ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -125,16 +135,16 @@ String8 getInputDeviceConfigurationFilePathByName( // Not found. #if DEBUG_PROBE ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.string(), type); + name.c_str(), type); #endif - return String8(); + return ""; } // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { - initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false, false); + initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : @@ -150,7 +160,7 @@ InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal, + const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic) { mId = id; mGeneration = generation; @@ -175,7 +185,7 @@ const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( return ⦥ } } - return NULL; + return nullptr; } void InputDeviceInfo::addSource(uint32_t source) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index aa0bf17ca3..32444f9092 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -30,6 +30,7 @@ #include <cutils/properties.h> #include <log/log.h> +#include <binder/Parcel.h> #include <input/InputTransport.h> namespace android { @@ -100,15 +101,13 @@ size_t InputMessage::size() const { // --- InputChannel --- InputChannel::InputChannel(const std::string& name, int fd) : - mName(name), mFd(fd) { + mName(name) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.c_str(), errno); + setFd(fd); } InputChannel::~InputChannel() { @@ -120,6 +119,18 @@ InputChannel::~InputChannel() { ::close(mFd); } +void InputChannel::setFd(int fd) { + if (mFd > 0) { + ::close(mFd); + } + mFd = fd; + if (mFd > 0) { + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " + "non-blocking. errno=%d", mName.c_str(), errno); + } +} + status_t InputChannel::openInputChannelPair(const std::string& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; @@ -226,9 +237,34 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { sp<InputChannel> InputChannel::dup() const { int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : NULL; + return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; +} + + +status_t InputChannel::write(Parcel& out) const { + status_t s = out.writeString8(String8(getName().c_str())); + + if (s != OK) { + return s; + } + + s = out.writeDupFileDescriptor(getFd()); + + return s; } +status_t InputChannel::read(const Parcel& from) { + mName = from.readString8(); + + int rawFd = from.readFileDescriptor(); + setFd(::dup(rawFd)); + + if (mFd < 0) { + return BAD_VALUE; + } + + return OK; +} // --- InputPublisher --- @@ -243,6 +279,7 @@ status_t InputPublisher::publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -270,6 +307,7 @@ status_t InputPublisher::publishKeyEvent( msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; + msg.body.key.displayId = displayId; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -303,13 +341,15 @@ status_t InputPublisher::publishMotionEvent( const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, - deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); + deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, + buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, + pointerCount); #endif if (!seq) { @@ -384,7 +424,7 @@ InputConsumer::~InputConsumer() { bool InputConsumer::isTouchResamplingEnabled() { char value[PROPERTY_VALUE_MAX]; - int length = property_get("ro.input.noresample", value, NULL); + int length = property_get("ro.input.noresample", value, nullptr); if (length > 0) { if (!strcmp("1", value)) { return false; @@ -398,16 +438,14 @@ bool InputConsumer::isTouchResamplingEnabled() { } status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, - int32_t* displayId) { + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; - *outEvent = NULL; - *displayId = -1; // Invalid display. + *outEvent = nullptr; // Fetch the next input message. // Loop until an event can be returned or no additional events are received. @@ -422,7 +460,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId); + result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", @@ -466,7 +504,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, - batch, batch.samples.size(), outSeq, outEvent, displayId); + batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(batchIndex); if (result) { return result; @@ -500,7 +538,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; - *displayId = mMsg.body.motion.displayId; + #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().c_str(), *outSeq); @@ -518,14 +556,13 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, } status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; Batch& batch = mBatches.editItemAt(i); if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(i); return result; } @@ -539,11 +576,11 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, continue; } - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; if (batch.samples.isEmpty()) { mBatches.removeAt(i); - next = NULL; + next = nullptr; } else { next = &batch.samples.itemAt(0); } @@ -557,7 +594,7 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, } status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; @@ -572,7 +609,6 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, mSeqChains.push(seqChain); addSample(motionEvent, &msg); } else { - *displayId = msg.body.motion.displayId; initializeMotionEvent(motionEvent, &msg); } chain = msg.body.motion.seq; @@ -928,6 +964,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) event->initialize( msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, @@ -950,6 +987,7 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage event->initialize( msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, diff --git a/services/inputflinger/InputWindow.cpp b/libs/input/InputWindow.cpp index 3ae7972779..f94faba99b 100644 --- a/services/inputflinger/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -17,7 +17,9 @@ #define LOG_TAG "InputWindow" #define LOG_NDEBUG 0 -#include "InputWindow.h" +#include <binder/Parcel.h> +#include <input/InputWindow.h> +#include <input/InputTransport.h> #include <log/log.h> @@ -62,11 +64,81 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { && frameTop < other->frameBottom && frameBottom > other->frameTop; } +status_t InputWindowInfo::write(Parcel& output) const { + if (inputChannel == nullptr) { + output.writeInt32(0); + return OK; + } + output.writeInt32(1); + status_t s = inputChannel->write(output); + if (s != OK) return s; + + output.writeString8(String8(name.c_str())); + output.writeInt32(layoutParamsFlags); + output.writeInt32(layoutParamsType); + output.writeInt64(dispatchingTimeout); + output.writeInt32(frameLeft); + output.writeInt32(frameTop); + output.writeInt32(frameRight); + output.writeInt32(frameBottom); + output.writeFloat(scaleFactor); + output.writeBool(visible); + output.writeBool(canReceiveKeys); + output.writeBool(hasFocus); + output.writeBool(hasWallpaper); + output.writeBool(paused); + output.writeInt32(layer); + output.writeInt32(ownerPid); + output.writeInt32(ownerUid); + output.writeInt32(inputFeatures); + output.writeInt32(displayId); + output.write(touchableRegion); + + return OK; +} + +InputWindowInfo InputWindowInfo::read(const Parcel& from) { + InputWindowInfo ret; + + if (from.readInt32() == 0) { + return ret; + + } + sp<InputChannel> inputChannel = new InputChannel(); + status_t s = inputChannel->read(from); + if (s != OK) { + return ret; + } + + ret.inputChannel = inputChannel; + ret.name = from.readString8().c_str(); + ret.layoutParamsFlags = from.readInt32(); + ret.layoutParamsType = from.readInt32(); + ret.dispatchingTimeout = from.readInt64(); + ret.frameLeft = from.readInt32(); + ret.frameTop = from.readInt32(); + ret.frameRight = from.readInt32(); + ret.frameBottom = from.readInt32(); + ret.scaleFactor = from.readFloat(); + ret.visible = from.readBool(); + ret.canReceiveKeys = from.readBool(); + ret.hasFocus = from.readBool(); + ret.hasWallpaper = from.readBool(); + ret.paused = from.readBool(); + ret.layer = from.readInt32(); + ret.ownerPid = from.readInt32(); + ret.ownerUid = from.readInt32(); + ret.inputFeatures = from.readInt32(); + ret.displayId = from.readInt32(); + from.read(ret.touchableRegion); + + return ret; +} // --- InputWindowHandle --- InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) : - inputApplicationHandle(inputApplicationHandle), mInfo(NULL) { + inputApplicationHandle(inputApplicationHandle), mInfo(nullptr) { } InputWindowHandle::~InputWindowHandle() { @@ -76,8 +148,12 @@ InputWindowHandle::~InputWindowHandle() { void InputWindowHandle::releaseInfo() { if (mInfo) { delete mInfo; - mInfo = NULL; + mInfo = nullptr; } } +sp<InputChannel> InputWindowHandle::getInputChannel() const { + return mInfo ? mInfo->inputChannel : nullptr; +} + } // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index cba1111606..e189d20e28 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -106,14 +106,14 @@ KeyCharacterMap::~KeyCharacterMap() { } } -status_t KeyCharacterMap::load(const String8& filename, +status_t KeyCharacterMap::load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.string()); + ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); } else { status = load(tokenizer, format, outMap); delete tokenizer; @@ -121,12 +121,12 @@ status_t KeyCharacterMap::load(const String8& filename, return status; } -status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, +status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); + status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); } else { @@ -164,10 +164,10 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, const sp<KeyCharacterMap>& overlay) { - if (overlay == NULL) { + if (overlay == nullptr) { return base; } - if (base == NULL) { + if (base == nullptr) { return overlay; } @@ -468,7 +468,7 @@ bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMeta // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. - const Behavior* found = NULL; + const Behavior* found = nullptr; for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { if (behavior->character == ch) { found = behavior; @@ -487,7 +487,7 @@ void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, 0, time, time); } @@ -605,11 +605,11 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { map->mType = parcel->readInt32(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } if (numKeys > MAX_KEYS) { ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); - return NULL; + return nullptr; } for (size_t i = 0; i < numKeys; i++) { @@ -617,7 +617,7 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { char16_t label = parcel->readInt32(); char16_t number = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Key* key = new Key(); @@ -625,14 +625,14 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { key->number = number; map->mKeys.add(keyCode, key); - Behavior* lastBehavior = NULL; + Behavior* lastBehavior = nullptr; while (parcel->readInt32()) { int32_t metaState = parcel->readInt32(); char16_t character = parcel->readInt32(); int32_t fallbackKeyCode = parcel->readInt32(); int32_t replacementKeyCode = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Behavior* behavior = new Behavior(); @@ -649,7 +649,7 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { } if (parcel->errorCheck()) { - return NULL; + return nullptr; } } return map; @@ -666,7 +666,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(keyCode); parcel->writeInt32(key->label); parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != NULL; + for (const Behavior* behavior = key->firstBehavior; behavior != nullptr; behavior = behavior->next) { parcel->writeInt32(1); parcel->writeInt32(behavior->metaState); @@ -683,12 +683,12 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { // --- KeyCharacterMap::Key --- KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(NULL) { + label(0), number(0), firstBehavior(nullptr) { } KeyCharacterMap::Key::Key(const Key& other) : label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { + firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) { } KeyCharacterMap::Key::~Key() { @@ -704,11 +704,11 @@ KeyCharacterMap::Key::~Key() { // --- KeyCharacterMap::Behavior --- KeyCharacterMap::Behavior::Behavior() : - next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { + next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { } KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : NULL), + next(other.next ? new Behavior(*other.next) : nullptr), metaState(other.metaState), character(other.character), fallbackKeyCode(other.fallbackKeyCode), replacementKeyCode(other.replacementKeyCode) { @@ -944,7 +944,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { properties.add(Property(PROPERTY_NUMBER)); } else { int32_t metaState; - status_t status = parseModifier(token, &metaState); + status_t status = parseModifier(token.string(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", mTokenizer->getLocation().string(), token.string()); @@ -1137,7 +1137,7 @@ status_t KeyCharacterMap::Parser::finishKey(Key* key) { return NO_ERROR; } -status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { +status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; return NO_ERROR; @@ -1145,7 +1145,7 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o int32_t combinedMeta = 0; - const char* str = token.string(); + const char* str = token.c_str(); const char* start = str; for (const char* cur = str; ; cur++) { char ch = *cur; @@ -1164,7 +1164,7 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o } if (combinedMeta & metaState) { ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().string(), token.c_str()); return BAD_VALUE; } diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 2b2f13e4c7..88cb0dbdb4 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -49,13 +49,13 @@ KeyLayoutMap::KeyLayoutMap() { KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { +status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key layout map file %s.", status, filename.string()); + ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); } else { sp<KeyLayoutMap> map = new KeyLayoutMap(); if (!map.get()) { @@ -117,7 +117,7 @@ const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCod return &mKeysByScanCode.valueAt(index); } } - return NULL; + return nullptr; } status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 11842ee7ff..0c22bfefed 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -45,22 +45,22 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, String8 keyLayoutName; if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), keyLayoutName)) { - status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { - status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } @@ -70,30 +70,30 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, } // Try searching by device identifier. - if (probeKeyMap(deviceIdenfifier, String8::empty())) { + if (probeKeyMap(deviceIdenfifier, "")) { return OK; } // Fall back on the Generic key map. // TODO Apply some additional heuristics here to figure out what kind of // generic key map to use (US English, etc.) for typical external keyboards. - if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + if (probeKeyMap(deviceIdenfifier, "Generic")) { return OK; } // Try the Virtual key map as a last resort. - if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + if (probeKeyMap(deviceIdenfifier, "Virtual")) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", - deviceIdenfifier.name.string()); + deviceIdenfifier.name.c_str()); return NAME_NOT_FOUND; } bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& keyMapName) { + const std::string& keyMapName) { if (!haveKeyLayout()) { loadKeyLayout(deviceIdentifier, keyMapName); } @@ -104,10 +104,10 @@ bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, } status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, + const std::string& name) { + std::string path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); - if (path.isEmpty()) { + if (path.empty()) { return NAME_NOT_FOUND; } @@ -116,15 +116,15 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, return status; } - keyLayoutFile.setTo(path); + keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); - if (path.isEmpty()) { + const std::string& name) { + std::string path = getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); + if (path.empty()) { return NAME_NOT_FOUND; } @@ -134,13 +134,13 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return status; } - keyCharacterMapFile.setTo(path); + keyCharacterMapFile = path; return OK; } -String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type) { - return name.isEmpty() +std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const std::string& name, InputDeviceConfigurationFileType type) { + return name.empty() ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) : getInputDeviceConfigurationFilePathByName(name, type); } @@ -174,7 +174,7 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, } } - return strstr(deviceIdentifier.name.string(), "-keypad"); + return strstr(deviceIdentifier.name.c_str(), "-keypad"); } static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index f72e49b664..1bbd82b3b3 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -117,7 +117,7 @@ VelocityTracker::VelocityTracker(const char* strategy) : // Allow the default strategy to be overridden using a system property for debugging. if (!strategy) { - int length = property_get("debug.velocitytracker.strategy", value, NULL); + int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); if (length > 0) { strategy = value; } else { @@ -141,7 +141,7 @@ VelocityTracker::~VelocityTracker() { bool VelocityTracker::configureStrategy(const char* strategy) { mStrategy = createStrategy(strategy); - return mStrategy != NULL; + return mStrategy != nullptr; } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { @@ -206,7 +206,7 @@ VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { // time to adjust to changes in direction. return new LegacyVelocityTrackerStrategy(); } - return NULL; + return nullptr; } void VelocityTracker::clear() { diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 28ea7177cf..3ec53bf5a0 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -46,13 +46,13 @@ VirtualKeyMap::VirtualKeyMap() { VirtualKeyMap::~VirtualKeyMap() { } -status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { - *outMap = NULL; +status_t VirtualKeyMap::load(const std::string& filename, VirtualKeyMap** outMap) { + *outMap = nullptr; Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); + ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); } else { VirtualKeyMap* map = new VirtualKeyMap(); if (!map) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index f06119f3f7..fdd945e90e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "VelocityTracker_test.cpp", + "InputWindow_test.cpp" ], cflags: [ "-Wall", @@ -34,4 +35,12 @@ cc_library_static { "-Wall", "-Werror", ], + shared_libs: [ + "libinput", + "libcutils", + "libutils", + "libbinder", + "libui", + "libbase", + ] } diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index fd3b7c8ee4..99f83ba7db 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -22,6 +22,9 @@ namespace android { +// Default display id. +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; + class BaseTest : public testing::Test { protected: virtual void SetUp() { } @@ -178,13 +181,14 @@ TEST_F(KeyEventTest, Properties) { // Initialize and get properties. const nsecs_t ARBITRARY_DOWN_TIME = 1; const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource()); + ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -197,6 +201,11 @@ TEST_F(KeyEventTest, Properties) { // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + + // Set display id. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); } @@ -248,7 +257,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, @@ -301,6 +310,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); + ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); @@ -434,6 +444,11 @@ TEST_F(MotionEventTest, Properties) { event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + // Set displayId. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); + // Set action. event.setAction(AMOTION_EVENT_ACTION_CANCEL); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); @@ -557,7 +572,7 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, + event.initialize(0, 0, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index c5322414fd..0788c89b2a 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -46,12 +46,12 @@ protected: virtual void TearDown() { if (mPublisher) { delete mPublisher; - mPublisher = NULL; + mPublisher = nullptr; } if (mConsumer) { delete mConsumer; - mConsumer = NULL; + mConsumer = nullptr; } serverChannel.clear(); @@ -70,32 +70,31 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_KEYBOARD; - const int32_t action = AKEY_EVENT_ACTION_DOWN; - const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; - const int32_t keyCode = AKEYCODE_ENTER; - const int32_t scanCode = 13; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t repeatCount = 1; - const nsecs_t downTime = 3; - const nsecs_t eventTime = 4; - - status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; + constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + constexpr int32_t keyCode = AKEYCODE_ENTER; + constexpr int32_t scanCode = 13; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t repeatCount = 1; + constexpr nsecs_t downTime = 3; + constexpr nsecs_t eventTime = 4; + + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; InputEvent* event; - int32_t displayId; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) << "consumer should have returned a key event"; @@ -104,6 +103,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(displayId, keyEvent->getDisplayId()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -131,23 +131,23 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; - int32_t displayId = 0; - const int32_t action = AMOTION_EVENT_ACTION_MOVE; - const int32_t actionButton = 0; - const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - const float xOffset = -10; - const float yOffset = -20; - const float xPrecision = 0.25; - const float yPrecision = 0.5; - const nsecs_t downTime = 3; - const size_t pointerCount = 3; - const nsecs_t eventTime = 4; + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; + constexpr int32_t actionButton = 0; + constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + constexpr float xOffset = -10; + constexpr float yOffset = -20; + constexpr float xPrecision = 0.25; + constexpr float yPrecision = 0.5; + constexpr nsecs_t downTime = 3; + constexpr size_t pointerCount = 3; + constexpr nsecs_t eventTime = 4; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { @@ -176,12 +176,11 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << "consumer should have returned a motion event"; @@ -190,6 +189,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); + EXPECT_EQ(displayId, motionEvent->getDisplayId()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp new file mode 100644 index 0000000000..39ad26e8c1 --- /dev/null +++ b/libs/input/tests/InputWindow_test.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/Parcel.h> + +#include <input/InputWindow.h> +#include <input/InputTransport.h> + +namespace android { +namespace test { + +TEST(InputWindowInfo, ParcellingWithoutChannel) { + InputWindowInfo i; + i.inputChannel = nullptr; + + Parcel p; + ASSERT_EQ(OK, i.write(p)); + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_TRUE(i2.inputChannel == nullptr); +} + +TEST(InputWindowInfo, Parcelling) { + sp<InputChannel> channel, junkChannel; + status_t result = InputChannel::openInputChannelPair("name", channel, junkChannel); + ASSERT_EQ(OK, result) << "openInputChannelPair should have returned valid channels"; + + InputWindowInfo i; + i.inputChannel = channel; + i.name = "Foobar"; + i.layoutParamsFlags = 7; + i.layoutParamsType = 39; + i.dispatchingTimeout = 12; + i.frameLeft = 93; + i.frameTop = 34; + i.frameRight = 16; + i.frameBottom = 19; + i.scaleFactor = 0.3; + i.visible = false; + i.canReceiveKeys = false; + i.hasFocus = false; + i.hasWallpaper = false; + i.paused = false; + i.layer = 7; + i.ownerPid = 19; + i.ownerUid = 24; + i.inputFeatures = 29; + i.displayId = 34; + + Parcel p; + i.write(p); + + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_EQ(i.inputChannel->getName(), i2.inputChannel->getName()); + ASSERT_EQ(i.name, i2.name); + ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); + ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); + ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); + ASSERT_EQ(i.frameLeft, i2.frameLeft); + ASSERT_EQ(i.frameTop, i2.frameTop); + ASSERT_EQ(i.frameRight, i2.frameRight); + ASSERT_EQ(i.frameBottom, i2.frameBottom); + ASSERT_EQ(i.scaleFactor, i2.scaleFactor); + ASSERT_EQ(i.visible, i2.visible); + ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); + ASSERT_EQ(i.hasFocus, i2.hasFocus); + ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); + ASSERT_EQ(i.paused, i2.paused); + ASSERT_EQ(i.layer, i2.layer); + ASSERT_EQ(i.ownerPid, i2.ownerPid); + ASSERT_EQ(i.ownerUid, i2.ownerUid); + ASSERT_EQ(i.inputFeatures, i2.inputFeatures); + ASSERT_EQ(i.displayId, i2.displayId); +} + +} // namespace test +} // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 9da2e2a315..af97c34055 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -27,6 +27,8 @@ using android::base::StringPrintf; namespace android { +constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id + constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests // velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV @@ -96,7 +98,7 @@ MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSample // First sample added separately with initialize coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y); - event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords); for (size_t i = 1; i < numSamples; i++) { diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 5fbb3b2f87..647fe867ac 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -13,13 +13,20 @@ // limitations under the License. ndk_headers { - name: "libnativewindow_headers", + name: "libnativewindow_ndk_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } +// TODO(b/118715870): cleanup header files +cc_library_headers { + name: "libnativewindow_headers", + export_include_dirs: ["include"], + vendor_available: false, +} + ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", @@ -70,6 +77,7 @@ cc_library { header_libs: [ "libnativebase_headers", + "libnativewindow_headers", ], // headers we include in our public headers diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp new file mode 100644 index 0000000000..674659c63f --- /dev/null +++ b/libs/renderengine/Android.bp @@ -0,0 +1,80 @@ +// TODO(b/112585051) Add to VNDK once moved to libs/ +cc_defaults { + name: "renderengine_defaults", + cflags: [ + "-DLOG_TAG=\"RenderEngine\"", + "-Wall", + "-Werror", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", + ], + cppflags: ["-std=c++1z"], +} + +cc_defaults { + name: "librenderengine_defaults", + defaults: ["renderengine_defaults"], + cflags: [ + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + shared_libs: [ + "libcutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libui", + "libutils", + ], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + +filegroup { + name: "librenderengine_sources", + srcs: [ + "Description.cpp", + "Mesh.cpp", + "RenderEngine.cpp", + "Texture.cpp", + ], +} + +filegroup { + name: "librenderengine_gl_sources", + srcs: [ + "gl/GLES20RenderEngine.cpp", + "gl/GLExtensions.cpp", + "gl/GLFramebuffer.cpp", + "gl/GLImage.cpp", + "gl/GLSurface.cpp", + "gl/Program.cpp", + "gl/ProgramCache.cpp", + ], +} + +cc_library_static { + name: "librenderengine", + defaults: ["librenderengine_defaults"], + double_loadable: true, + + clang: true, + cflags: [ + "-fvisibility=hidden", + "-Werror=format", + ], + cppflags: [ + "-fwhole-program-vtables", // requires ThinLTO + ], + srcs: [ + ":librenderengine_sources", + ":librenderengine_gl_sources", + ], + lto: { + thin: true, + }, +} diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp new file mode 100644 index 0000000000..b9cea1071f --- /dev/null +++ b/libs/renderengine/Description.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <renderengine/private/Description.h> + +#include <stdint.h> + +#include <utils/TypeHelpers.h> + +namespace android { +namespace renderengine { + +Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) { + ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK); + switch (transfer) { + case ui::Dataspace::TRANSFER_ST2084: + return Description::TransferFunction::ST2084; + case ui::Dataspace::TRANSFER_HLG: + return Description::TransferFunction::HLG; + case ui::Dataspace::TRANSFER_LINEAR: + return Description::TransferFunction::LINEAR; + default: + return Description::TransferFunction::SRGB; + } +} + +bool Description::hasInputTransformMatrix() const { + const mat4 identity; + return inputTransformMatrix != identity; +} + +bool Description::hasOutputTransformMatrix() const { + const mat4 identity; + return outputTransformMatrix != identity; +} + +bool Description::hasColorMatrix() const { + const mat4 identity; + return colorMatrix != identity; +} + +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Mesh.cpp b/libs/renderengine/Mesh.cpp index 6a62b1d7dd..6a40c6c680 100644 --- a/services/surfaceflinger/RenderEngine/Mesh.cpp +++ b/libs/renderengine/Mesh.cpp @@ -14,11 +14,12 @@ * limitations under the License. */ -#include "Mesh.h" +#include <renderengine/Mesh.h> #include <utils/Log.h> namespace android { +namespace renderengine { Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize) : mVertexCount(vertexCount), @@ -26,7 +27,7 @@ Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t te mTexCoordsSize(texCoordSize), mPrimitive(primitive) { if (vertexCount == 0) { - mVertices = new float[1]; + mVertices.resize(1); mVertices[0] = 0.0f; mStride = 0; return; @@ -39,7 +40,7 @@ Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t te // will be equal to stride as long as stride * vertexCount doesn't overflow. if ((stride < vertexSize) || (remainder != stride)) { ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize); - mVertices = new float[1]; + mVertices.resize(1); mVertices[0] = 0.0f; mVertexCount = 0; mVertexSize = 0; @@ -48,30 +49,26 @@ Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t te return; } - mVertices = new float[stride * vertexCount]; + mVertices.resize(stride * vertexCount); mStride = stride; } -Mesh::~Mesh() { - delete[] mVertices; -} - Mesh::Primitive Mesh::getPrimitive() const { return mPrimitive; } float const* Mesh::getPositions() const { - return mVertices; + return mVertices.data(); } float* Mesh::getPositions() { - return mVertices; + return mVertices.data(); } float const* Mesh::getTexCoords() const { - return mVertices + mVertexSize; + return mVertices.data() + mVertexSize; } float* Mesh::getTexCoords() { - return mVertices + mVertexSize; + return mVertices.data() + mVertexSize; } size_t Mesh::getVertexCount() const { @@ -94,4 +91,5 @@ size_t Mesh::getStride() const { return mStride; } -} /* namespace android */ +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp new file mode 100644 index 0000000000..8be1c3c85b --- /dev/null +++ b/libs/renderengine/RenderEngine.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <renderengine/RenderEngine.h> + +#include <cutils/properties.h> +#include <log/log.h> +#include <private/gui/SyncFeatures.h> +#include "gl/GLES20RenderEngine.h" + +namespace android { +namespace renderengine { + +std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) { + char prop[PROPERTY_VALUE_MAX]; + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + if (strcmp(prop, "gles") == 0) { + ALOGD("RenderEngine GLES Backend"); + return renderengine::gl::GLES20RenderEngine::create(hwcFormat, featureFlags); + } + ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); + return renderengine::gl::GLES20RenderEngine::create(hwcFormat, featureFlags); +} + +RenderEngine::~RenderEngine() = default; + +namespace impl { + +RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {} + +RenderEngine::~RenderEngine() = default; + +bool RenderEngine::useNativeFenceSync() const { + return SyncFeatures::getInstance().useNativeFenceSync(); +} + +bool RenderEngine::useWaitSync() const { + return SyncFeatures::getInstance().useWaitSync(); +} + +} // namespace impl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING new file mode 100644 index 0000000000..995dba1422 --- /dev/null +++ b/libs/renderengine/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "librenderengine_test" + } + ] +} diff --git a/services/surfaceflinger/RenderEngine/Texture.cpp b/libs/renderengine/Texture.cpp index 351430fa06..154cde80b9 100644 --- a/services/surfaceflinger/RenderEngine/Texture.cpp +++ b/libs/renderengine/Texture.cpp @@ -14,11 +14,10 @@ * limitations under the License. */ -#include <string.h> - -#include "Texture.h" +#include <renderengine/Texture.h> namespace android { +namespace renderengine { Texture::Texture() : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {} @@ -74,4 +73,5 @@ size_t Texture::getHeight() const { return mHeight; } -} /* namespace android */ +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp new file mode 100644 index 0000000000..026b15149a --- /dev/null +++ b/libs/renderengine/gl/GLES20RenderEngine.cpp @@ -0,0 +1,952 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GLES20RenderEngine.h" + +#include <math.h> +#include <fstream> +#include <sstream> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <cutils/compiler.h> +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> +#include <renderengine/private/Description.h> +#include <ui/ColorSpace.h> +#include <ui/DebugUtils.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/Trace.h> +#include "GLExtensions.h" +#include "GLFramebuffer.h" +#include "GLImage.h" +#include "GLSurface.h" +#include "Program.h" +#include "ProgramCache.h" + +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + +bool checkGlError(const char* op, int lineNumber) { + bool errorFound = false; + GLint error = glGetError(); + while (error != GL_NO_ERROR) { + errorFound = true; + error = glGetError(); + ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); + } + return errorFound; +} + +static constexpr bool outputDebugPPMs = false; + +void writePPM(const char* basename, GLuint width, GLuint height) { + ALOGV("writePPM #%s: %d x %d", basename, width, height); + + std::vector<GLubyte> pixels(width * height * 4); + std::vector<GLubyte> outBuffer(width * height * 3); + + // TODO(courtneygo): We can now have float formats, need + // to remove this code or update to support. + // Make returned pixels fit in uint32_t, one byte per component + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + if (checkGlError(__FUNCTION__, __LINE__)) { + return; + } + + std::string filename(basename); + filename.append(".ppm"); + std::ofstream file(filename.c_str(), std::ios::binary); + if (!file.is_open()) { + ALOGE("Unable to open file: %s", filename.c_str()); + ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " + "surfaceflinger to write debug images"); + return; + } + + file << "P6\n"; + file << width << "\n"; + file << height << "\n"; + file << 255 << "\n"; + + auto ptr = reinterpret_cast<char*>(pixels.data()); + auto outPtr = reinterpret_cast<char*>(outBuffer.data()); + for (int y = height - 1; y >= 0; y--) { + char* data = ptr + y * width * sizeof(uint32_t); + + for (GLuint x = 0; x < width; x++) { + // Only copy R, G and B components + outPtr[0] = data[0]; + outPtr[1] = data[1]; + outPtr[2] = data[2]; + data += sizeof(uint32_t); + outPtr += 3; + } + } + file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); +} + +namespace android { +namespace renderengine { +namespace gl { + +using ui::Dataspace; + +static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, + EGLint wanted, EGLConfig* outConfig) { + EGLint numConfigs = -1, n = 0; + eglGetConfigs(dpy, nullptr, 0, &numConfigs); + std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); + eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); + configs.resize(n); + + if (!configs.empty()) { + if (attribute != EGL_NONE) { + for (EGLConfig config : configs) { + EGLint value = 0; + eglGetConfigAttrib(dpy, config, attribute, &value); + if (wanted == value) { + *outConfig = config; + return NO_ERROR; + } + } + } else { + // just pick the first one + *outConfig = configs[0]; + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +class EGLAttributeVector { + struct Attribute; + class Adder; + friend class Adder; + KeyedVector<Attribute, EGLint> mList; + struct Attribute { + Attribute() : v(0){}; + explicit Attribute(EGLint v) : v(v) {} + EGLint v; + bool operator<(const Attribute& other) const { + // this places EGL_NONE at the end + EGLint lhs(v); + EGLint rhs(other.v); + if (lhs == EGL_NONE) lhs = 0x7FFFFFFF; + if (rhs == EGL_NONE) rhs = 0x7FFFFFFF; + return lhs < rhs; + } + }; + class Adder { + friend class EGLAttributeVector; + EGLAttributeVector& v; + EGLint attribute; + Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {} + + public: + void operator=(EGLint value) { + if (attribute != EGL_NONE) { + v.mList.add(Attribute(attribute), value); + } + } + operator EGLint() const { return v.mList[attribute]; } + }; + +public: + EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); } + void remove(EGLint attribute) { + if (attribute != EGL_NONE) { + mList.removeItem(Attribute(attribute)); + } + } + Adder operator[](EGLint attribute) { return Adder(*this, attribute); } + EGLint operator[](EGLint attribute) const { return mList[attribute]; } + // cast-operator to (EGLint const*) + operator EGLint const*() const { return &mList.keyAt(0).v; } +}; + +static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, + EGLConfig* config) { + // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if + // it is to be used with WIFI displays + status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; + + EGLAttributeVector attribs; + if (renderableType) { + attribs[EGL_RENDERABLE_TYPE] = renderableType; + attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; + attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; + attribs[EGL_RED_SIZE] = 8; + attribs[EGL_GREEN_SIZE] = 8; + attribs[EGL_BLUE_SIZE] = 8; + attribs[EGL_ALPHA_SIZE] = 8; + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = format; + } + + err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + + return err; +} + +std::unique_ptr<GLES20RenderEngine> GLES20RenderEngine::create(int hwcFormat, + uint32_t featureFlags) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + GLExtensions& extensions = GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), + eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG) { + renderableType = EGL_OPENGL_ES2_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(6); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + bool useContextPriority = extensions.hasContextPriority() && + (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT); + if (useContextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data()); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + // now figure out what version of GL did we actually get + // NOTE: a dummy surface is not needed if KHR_create_context is supported + + EGLConfig dummyConfig = config; + if (dummyConfig == EGL_NO_CONFIG) { + dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE}; + EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs); + LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); + EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + GlesVersion version = parseGlesVersion(extensions.getVersion()); + + // initialize the renderer while GL is current + + std::unique_ptr<GLES20RenderEngine> engine; + switch (version) { + case GLES_VERSION_1_0: + case GLES_VERSION_1_1: + LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); + break; + case GLES_VERSION_2_0: + case GLES_VERSION_3_0: + engine = std::make_unique<GLES20RenderEngine>(featureFlags); + break; + } + engine->setEGLHandles(display, config, ctxt); + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtensions()); + ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, dummy); + + return engine; +} + +EGLConfig GLES20RenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { + status_t err; + EGLConfig config; + + // First try to get an ES2 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); + if (err != NO_ERROR) { + // If ES2 fails, try ES1 + err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config); + if (err != NO_ERROR) { + // still didn't work, probably because we're on the emulator... + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(display, format, 0, &config); + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + } + } + + if (logConfig) { + // print some debugging info + EGLint r, g, b, a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } + + return config; +} + +GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags) + : renderengine::impl::RenderEngine(featureFlags), + mEGLDisplay(EGL_NO_DISPLAY), + mEGLConfig(nullptr), + mEGLContext(EGL_NO_CONTEXT), + mVpWidth(0), + mVpHeight(0), + mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) { + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + const uint16_t protTexData[] = {0}; + glGenTextures(1, &mProtectedTexName); + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); + + // mColorBlindnessCorrection = M; + + if (mUseColorManagement) { + ColorSpace srgb(ColorSpace::sRGB()); + ColorSpace displayP3(ColorSpace::DisplayP3()); + ColorSpace bt2020(ColorSpace::BT2020()); + + // Compute sRGB to Display P3 transform matrix. + // NOTE: For now, we are limiting output wide color space support to + // Display-P3 only. + mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform()); + + // Compute Display P3 to sRGB transform matrix. + mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform()); + + // no chromatic adaptation needed since all color spaces use D65 for their white points. + mSrgbToXyz = mat4(srgb.getRGBtoXYZ()); + mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ()); + mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ()); + mXyzToSrgb = mat4(srgb.getXYZtoRGB()); + mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); + mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); + } +} + +GLES20RenderEngine::~GLES20RenderEngine() { + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEGLDisplay); +} + +std::unique_ptr<Framebuffer> GLES20RenderEngine::createFramebuffer() { + return std::make_unique<GLFramebuffer>(*this); +} + +std::unique_ptr<Surface> GLES20RenderEngine::createSurface() { + return std::make_unique<GLSurface>(*this); +} + +std::unique_ptr<Image> GLES20RenderEngine::createImage() { + return std::make_unique<GLImage>(*this); +} + +void GLES20RenderEngine::primeCache() const { + ProgramCache::getInstance().primeCache(mFeatureFlags & USE_COLOR_MANAGEMENT); +} + +bool GLES20RenderEngine::isCurrent() const { + return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); +} + +bool GLES20RenderEngine::setCurrentSurface(const Surface& surface) { + // Surface is an abstract interface. GLES20RenderEngine only ever + // creates GLSurface's, so it is safe to just cast to the actual + // type. + bool success = true; + const GLSurface& glSurface = static_cast<const GLSurface&>(surface); + EGLSurface eglSurface = glSurface.getEGLSurface(); + if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) { + success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE; + if (success && glSurface.getAsync()) { + eglSwapInterval(mEGLDisplay, 0); + } + if (success) { + mSurfaceHeight = glSurface.getHeight(); + } + } + + return success; +} + +void GLES20RenderEngine::resetCurrentSurface() { + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + mSurfaceHeight = 0; +} + +base::unique_fd GLES20RenderEngine::flush() { + if (!GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); + } + + // native fence fd will not be populated until flush() is done. + glFlush(); + + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); + } + + return fenceFd; +} + +bool GLES20RenderEngine::finish() { + if (!GLExtensions::getInstance().hasFenceSync()) { + ALOGW("no synchronization support"); + return false; + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL fence sync: %#x", eglGetError()); + return false; + } + + EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + 2000000000 /*2 sec*/); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (result != EGL_CONDITION_SATISFIED_KHR) { + if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGW("fence wait timed out"); + } else { + ALOGW("error waiting on EGL fence: %#x", error); + } + return false; + } + + return true; +} + +bool GLES20RenderEngine::waitFence(base::unique_fd fenceFd) { + if (!GLExtensions::getInstance().hasNativeFenceSync() || + !GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // fenceFd is now owned by EGLSync + (void)fenceFd.release(); + + // XXX: The spec draft is inconsistent as to whether this should return an + // EGLint or void. Ignore the return value for now, as it's not strictly + // needed. + eglWaitSyncKHR(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +void GLES20RenderEngine::clearWithColor(float red, float green, float blue, float alpha) { + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLES20RenderEngine::fillRegionWithColor(const Region& region, float red, float green, + float blue, float alpha) { + size_t c; + Rect const* r = region.getArray(&c); + Mesh mesh(Mesh::TRIANGLES, c * 6, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + for (size_t i = 0; i < c; i++, r++) { + position[i * 6 + 0].x = r->left; + position[i * 6 + 0].y = r->top; + position[i * 6 + 1].x = r->left; + position[i * 6 + 1].y = r->bottom; + position[i * 6 + 2].x = r->right; + position[i * 6 + 2].y = r->bottom; + position[i * 6 + 3].x = r->left; + position[i * 6 + 3].y = r->top; + position[i * 6 + 4].x = r->right; + position[i * 6 + 4].y = r->bottom; + position[i * 6 + 5].x = r->right; + position[i * 6 + 5].y = r->top; + } + setupFillWithColor(red, green, blue, alpha); + drawMesh(mesh); +} + +void GLES20RenderEngine::setScissor(const Rect& region) { + // Invert y-coordinate to map to GL-space. + int32_t canvasHeight = mRenderToFbo ? mFboHeight : mSurfaceHeight; + int32_t glBottom = canvasHeight - region.bottom; + + glScissor(region.left, glBottom, region.getWidth(), region.getHeight()); + glEnable(GL_SCISSOR_TEST); +} + +void GLES20RenderEngine::disableScissor() { + glDisable(GL_SCISSOR_TEST); +} + +void GLES20RenderEngine::genTextures(size_t count, uint32_t* names) { + glGenTextures(count, names); +} + +void GLES20RenderEngine::deleteTextures(size_t count, uint32_t const* names) { + glDeleteTextures(count, names); +} + +void GLES20RenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { + const GLImage& glImage = static_cast<const GLImage&>(image); + const GLenum target = GL_TEXTURE_EXTERNAL_OES; + + glBindTexture(target, texName); + if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { + glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage())); + } +} + +status_t GLES20RenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer); + EGLImageKHR eglImage = glFramebuffer->getEGLImage(); + uint32_t textureName = glFramebuffer->getTextureName(); + uint32_t framebufferName = glFramebuffer->getFramebufferName(); + + // Bind the texture and turn our EGLImage into a texture + glBindTexture(GL_TEXTURE_2D, textureName); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); + + // Bind the Framebuffer to render into + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); + + mRenderToFbo = true; + mFboHeight = glFramebuffer->getBufferHeight(); + + uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", + glStatus); + + return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; +} + +void GLES20RenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { + mRenderToFbo = false; + mFboHeight = 0; + + // back to main framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // Workaround for b/77935566 to force the EGL driver to release the + // screenshot buffer + setScissor(Rect::EMPTY_RECT); + clearWithColor(0.0, 0.0, 0.0, 0.0); + disableScissor(); +} + +void GLES20RenderEngine::checkErrors() const { + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) break; + ALOGE("GL error 0x%04x", int(error)); + } while (true); +} + +status_t GLES20RenderEngine::drawLayers(const DisplaySettings& /*settings*/, + const std::vector<LayerSettings>& /*layers*/, + ANativeWindowBuffer* const /*buffer*/, + base::unique_fd* /*displayFence*/) const { + return NO_ERROR; +} + +void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) { + int32_t l = sourceCrop.left; + int32_t r = sourceCrop.right; + int32_t b = sourceCrop.bottom; + int32_t t = sourceCrop.top; + if (mRenderToFbo) { + std::swap(t, b); + } + mat4 m = mat4::ortho(l, r, b, t, 0, 1); + + // Apply custom rotation to the projection. + float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; + switch (rotation) { + case ui::Transform::ROT_0: + break; + case ui::Transform::ROT_90: + m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_180: + m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_270: + m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; + break; + default: + break; + } + + glViewport(0, 0, vpw, vph); + mState.projectionMatrix = m; + mVpWidth = vpw; + mVpHeight = vph; +} + +void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, + bool disableTexture, const half4& color) { + mState.isPremultipliedAlpha = premultipliedAlpha; + mState.isOpaque = opaque; + mState.color = color; + + if (disableTexture) { + mState.textureEnabled = false; + } + + if (color.a < 1.0f || !opaque) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLES20RenderEngine::setSourceY410BT2020(bool enable) { + mState.isY410BT2020 = enable; +} + +void GLES20RenderEngine::setSourceDataSpace(Dataspace source) { + mDataSpace = source; +} + +void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) { + mOutputDataSpace = dataspace; +} + +void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) { + mState.displayMaxLuminance = maxLuminance; +} + +void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLES20RenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + Texture texture(Texture::TEXTURE_2D, mProtectedTexName); + texture.setDimensions(1, 1); // FIXME: we should get that from somewhere + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLES20RenderEngine::setColorTransform(const mat4& colorTransform) { + mState.colorMatrix = colorTransform; +} + +void GLES20RenderEngine::disableTexturing() { + mState.textureEnabled = false; +} + +void GLES20RenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + +void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) { + mState.isPremultipliedAlpha = true; + mState.isOpaque = false; + mState.color = half4(r, g, b, a); + mState.textureEnabled = false; + glDisable(GL_BLEND); +} + +void GLES20RenderEngine::drawMesh(const Mesh& mesh) { + ATRACE_CALL(); + if (mesh.getTexCoordsSize()) { + glEnableVertexAttribArray(Program::texCoords); + glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getTexCoords()); + } + + glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getPositions()); + + // By default, DISPLAY_P3 is the only supported wide color output. However, + // when HDR content is present, hardware composer may be able to handle + // BT2020 data space, in that case, the output data space is set to be + // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need + // to respect this and convert non-HDR content to HDR format. + if (mUseColorManagement) { + Description managedState = mState; + Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); + Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + Dataspace outputStandard = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK); + Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + bool needsXYZConversion = needsXYZTransformMatrix(); + + if (needsXYZConversion) { + // The supported input color spaces are standard RGB, Display P3 and BT2020. + switch (inputStandard) { + case Dataspace::STANDARD_DCI_P3: + managedState.inputTransformMatrix = mDisplayP3ToXyz; + break; + case Dataspace::STANDARD_BT2020: + managedState.inputTransformMatrix = mBt2020ToXyz; + break; + default: + managedState.inputTransformMatrix = mSrgbToXyz; + break; + } + + // The supported output color spaces are BT2020, Display P3 and standard RGB. + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + managedState.outputTransformMatrix = mXyzToBt2020; + break; + case Dataspace::STANDARD_DCI_P3: + managedState.outputTransformMatrix = mXyzToDisplayP3; + break; + default: + managedState.outputTransformMatrix = mXyzToSrgb; + break; + } + } else if (inputStandard != outputStandard) { + // At this point, the input data space and output data space could be both + // HDR data spaces, but they match each other, we do nothing in this case. + // In addition to the case above, the input data space could be + // - scRGB linear + // - scRGB non-linear + // - sRGB + // - Display P3 + // The output data spaces could be + // - sRGB + // - Display P3 + if (outputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mDisplayP3ToSrgb; + } else if (outputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mSrgbToDisplayP3; + } + } + + // we need to convert the RGB value to linear space and convert it back when: + // - there is a color matrix that is not an identity matrix, or + // - there is an output transform matrix that is not an identity matrix, or + // - the input transfer function doesn't match the output transfer function. + if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || + inputTransfer != outputTransfer) { + managedState.inputTransferFunction = + Description::dataSpaceToTransferFunction(inputTransfer); + managedState.outputTransferFunction = + Description::dataSpaceToTransferFunction(outputTransfer); + } + + ProgramCache::getInstance().useProgram(managedState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (outputDebugPPMs) { + static uint64_t managedColorFrameCount = 0; + std::ostringstream out; + out << "/data/texture_out" << managedColorFrameCount++; + writePPM(out.str().c_str(), mVpWidth, mVpHeight); + } + } else { + ProgramCache::getInstance().useProgram(mState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + } + + if (mesh.getTexCoordsSize()) { + glDisableVertexAttribArray(Program::texCoords); + } +} + +size_t GLES20RenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLES20RenderEngine::getMaxViewportDims() const { + return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLES20RenderEngine::dump(String8& result) { + const GLExtensions& extensions = GLExtensions::getInstance(); + + result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion()); + result.appendFormat("%s\n", extensions.getEGLExtensions()); + + result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), + extensions.getVersion()); + result.appendFormat("%s\n", extensions.getExtensions()); + + result.appendFormat("RenderEngine program cache size: %zu\n", + ProgramCache::getInstance().getSize()); + + result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n", + dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), + dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); +} + +GLES20RenderEngine::GlesVersion GLES20RenderEngine::parseGlesVersion(const char* str) { + int major, minor; + if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { + if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { + ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); + return GLES_VERSION_1_0; + } + } + + if (major == 1 && minor == 0) return GLES_VERSION_1_0; + if (major == 1 && minor >= 1) return GLES_VERSION_1_1; + if (major == 2 && minor >= 0) return GLES_VERSION_2_0; + if (major == 3 && minor >= 0) return GLES_VERSION_3_0; + + ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); + return GLES_VERSION_1_0; +} + +bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { + const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); + const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); + return standard == Dataspace::STANDARD_BT2020 && + (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); +} + +// For convenience, we want to convert the input color space to XYZ color space first, +// and then convert from XYZ color space to output color space when +// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or +// HDR content will be tone-mapped to SDR; Or, +// - there are HDR PQ and HLG contents presented at the same time, where we want to convert +// HLG content to PQ content. +// In either case above, we need to operate the Y value in XYZ color space. Thus, when either +// input data space or output data space is HDR data space, and the input transfer function +// doesn't match the output transfer function, we would enable an intermediate transfrom to +// XYZ color space. +bool GLES20RenderEngine::needsXYZTransformMatrix() const { + const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); + const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); + const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + const Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + + return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; +} + +void GLES20RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) { + mEGLDisplay = display; + mEGLConfig = config; + mEGLContext = ctxt; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h new file mode 100644 index 0000000000..6ea85236da --- /dev/null +++ b/libs/renderengine/gl/GLES20RenderEngine.h @@ -0,0 +1,163 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_GLES20RENDERENGINE_H_ +#define SF_GLES20RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/private/Description.h> + +#define EGL_NO_CONFIG ((EGLConfig)0) + +namespace android { + +class String8; + +namespace renderengine { + +class Mesh; +class Texture; + +namespace gl { + +class GLImage; +class GLSurface; + +class GLES20RenderEngine : public impl::RenderEngine { +public: + static std::unique_ptr<GLES20RenderEngine> create(int hwcFormat, uint32_t featureFlags); + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); + + GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag + ~GLES20RenderEngine() override; + + std::unique_ptr<Framebuffer> createFramebuffer() override; + std::unique_ptr<Surface> createSurface() override; + std::unique_ptr<Image> createImage() override; + + void primeCache() const override; + bool isCurrent() const override; + bool setCurrentSurface(const Surface& surface) override; + void resetCurrentSurface() override; + base::unique_fd flush() override; + bool finish() override; + bool waitFence(base::unique_fd fenceFd) override; + void clearWithColor(float red, float green, float blue, float alpha) override; + void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) override; + void setScissor(const Rect& region) override; + void disableScissor() override; + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void bindExternalTextureImage(uint32_t texName, const Image& image) override; + status_t bindFrameBuffer(Framebuffer* framebuffer) override; + void unbindFrameBuffer(Framebuffer* framebuffer) override; + void checkErrors() const override; + + status_t drawLayers(const DisplaySettings& settings, const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* const buffer, + base::unique_fd* displayFence) const override; + + // internal to RenderEngine + EGLDisplay getEGLDisplay() const { return mEGLDisplay; } + EGLConfig getEGLConfig() const { return mEGLConfig; } + +protected: + void dump(String8& result) override; + void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) override; + void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color) override; + void setupLayerTexturing(const Texture& texture) override; + void setupLayerBlackedOut() override; + void setupFillWithColor(float r, float g, float b, float a) override; + void setColorTransform(const mat4& colorTransform) override; + void disableTexturing() override; + void disableBlending() override; + + // HDR and color management related functions and state + void setSourceY410BT2020(bool enable) override; + void setSourceDataSpace(ui::Dataspace source) override; + void setOutputDataSpace(ui::Dataspace dataspace) override; + void setDisplayMaxLuminance(const float maxLuminance) override; + + // drawing + void drawMesh(const Mesh& mesh) override; + + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + +private: + enum GlesVersion { + GLES_VERSION_1_0 = 0x10000, + GLES_VERSION_1_1 = 0x10001, + GLES_VERSION_2_0 = 0x20000, + GLES_VERSION_3_0 = 0x30000, + }; + + static GlesVersion parseGlesVersion(const char* str); + + // A data space is considered HDR data space if it has BT2020 color space + // with PQ or HLG transfer function. + bool isHdrDataSpace(const ui::Dataspace dataSpace) const; + bool needsXYZTransformMatrix() const; + void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt); + + EGLDisplay mEGLDisplay; + EGLConfig mEGLConfig; + EGLContext mEGLContext; + GLuint mProtectedTexName; + GLint mMaxViewportDims[2]; + GLint mMaxTextureSize; + GLuint mVpWidth; + GLuint mVpHeight; + Description mState; + + mat4 mSrgbToDisplayP3; + mat4 mDisplayP3ToSrgb; + mat4 mSrgbToXyz; + mat4 mBt2020ToXyz; + mat4 mDisplayP3ToXyz; + mat4 mXyzToSrgb; + mat4 mXyzToDisplayP3; + mat4 mXyzToBt2020; + + bool mRenderToFbo = false; + int32_t mSurfaceHeight = 0; + int32_t mFboHeight = 0; + + // Current dataspace of layer being rendered + ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; + + // Current output dataspace of the render engine + ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; + + // Whether device supports color management, currently color management + // supports sRGB, DisplayP3 color spaces. + const bool mUseColorManagement = false; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_GLES20RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp index dc09a376bb..784693bdfa 100644 --- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp +++ b/libs/renderengine/gl/GLExtensions.cpp @@ -14,33 +14,45 @@ * limitations under the License. */ +#include "GLExtensions.h" + +#include <string> +#include <unordered_set> + #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include "GLExtensions.h" +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions) namespace android { -// --------------------------------------------------------------------------- - -ANDROID_SINGLETON_STATIC_INSTANCE(GLExtensions) +namespace renderengine { +namespace gl { + +namespace { + +class ExtensionSet { +public: + ExtensionSet(const char* extensions) { + char const* curr = extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + size_t len = head ? head - curr : strlen(curr); + if (len > 0) { + mExtensions.emplace(curr, len); + } + curr = head + 1; + } while (head); + } -SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) { - SortedVector<String8> list; + bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; } - char const* curr = extensions; - char const* head = curr; - do { - head = strchr(curr, ' '); - String8 s(curr, head ? head - curr : strlen(curr)); - if (s.length()) { - list.add(s); - } - curr = head + 1; - } while (head); +private: + std::unordered_set<std::string> mExtensions; +}; - return list; -} +} // anonymous namespace void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, GLubyte const* extensions) { @@ -48,12 +60,6 @@ void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* rende mRenderer = (char const*)renderer; mVersion = (char const*)version; mExtensions = (char const*)extensions; - mExtensionList = parseExtensionString(mExtensions); -} - -bool GLExtensions::hasExtension(char const* extension) const { - const String8 s(extension); - return mExtensionList.indexOf(s) >= 0; } char const* GLExtensions::getVendor() const { @@ -75,7 +81,8 @@ char const* GLExtensions::getExtensions() const { void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { mEGLVersion = eglVersion; mEGLExtensions = eglExtensions; - mEGLExtensionList = parseExtensionString(mEGLExtensions); + + ExtensionSet extensionSet(eglExtensions); // EGL_ANDROIDX_no_config_context is an experimental extension with no // written specification. It will be replaced by something more formal. @@ -85,28 +92,24 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt // // EGL_KHR_no_config_context is official extension to allow creating a // context that works with any surface of a display. - if (hasEGLExtension("EGL_ANDROIDX_no_config_context") || - hasEGLExtension("EGL_KHR_no_config_context")) { + if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") || + extensionSet.hasExtension("EGL_KHR_no_config_context")) { mHasNoConfigContext = true; } - if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) { + if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) { mHasNativeFenceSync = true; } - if (hasEGLExtension("EGL_KHR_fence_sync")) { + if (extensionSet.hasExtension("EGL_KHR_fence_sync")) { mHasFenceSync = true; } - if (hasEGLExtension("EGL_KHR_wait_sync")) { + if (extensionSet.hasExtension("EGL_KHR_wait_sync")) { mHasWaitSync = true; } - - if (hasEGLExtension("EGL_ANDROID_image_crop")) { - mHasImageCrop = true; - } - if (hasEGLExtension("EGL_EXT_protected_content")) { + if (extensionSet.hasExtension("EGL_EXT_protected_content")) { mHasProtectedContent = true; } - if (hasEGLExtension("EGL_IMG_context_priority")) { + if (extensionSet.hasExtension("EGL_IMG_context_priority")) { mHasContextPriority = true; } } @@ -119,10 +122,6 @@ char const* GLExtensions::getEGLExtensions() const { return mEGLExtensions.string(); } -bool GLExtensions::hasEGLExtension(char const* extension) const { - const String8 s(extension); - return mEGLExtensionList.indexOf(s) >= 0; -} - -// --------------------------------------------------------------------------- -}; // namespace android +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h index 0d8c10b0ac..382c23a583 100644 --- a/services/surfaceflinger/RenderEngine/GLExtensions.h +++ b/libs/renderengine/gl/GLExtensions.h @@ -20,17 +20,16 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Singleton.h> -#include <utils/SortedVector.h> -#include <utils/String8.h> - #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES/gl.h> #include <GLES/glext.h> +#include <utils/Singleton.h> +#include <utils/String8.h> namespace android { -// --------------------------------------------------------------------------- +namespace renderengine { +namespace gl { class GLExtensions : public Singleton<GLExtensions> { friend class Singleton<GLExtensions>; @@ -39,7 +38,6 @@ class GLExtensions : public Singleton<GLExtensions> { bool mHasNativeFenceSync = false; bool mHasFenceSync = false; bool mHasWaitSync = false; - bool mHasImageCrop = false; bool mHasProtectedContent = false; bool mHasContextPriority = false; @@ -47,13 +45,9 @@ class GLExtensions : public Singleton<GLExtensions> { String8 mRenderer; String8 mVersion; String8 mExtensions; - SortedVector<String8> mExtensionList; String8 mEGLVersion; String8 mEGLExtensions; - SortedVector<String8> mEGLExtensionList; - - static SortedVector<String8> parseExtensionString(char const* extensions); GLExtensions(const GLExtensions&); GLExtensions& operator=(const GLExtensions&); @@ -66,7 +60,6 @@ public: bool hasNativeFenceSync() const { return mHasNativeFenceSync; } bool hasFenceSync() const { return mHasFenceSync; } bool hasWaitSync() const { return mHasWaitSync; } - bool hasImageCrop() const { return mHasImageCrop; } bool hasProtectedContent() const { return mHasProtectedContent; } bool hasContextPriority() const { return mHasContextPriority; } @@ -76,15 +69,14 @@ public: char const* getRenderer() const; char const* getVersion() const; char const* getExtensions() const; - bool hasExtension(char const* extension) const; void initWithEGLStrings(char const* eglVersion, char const* eglExtensions); char const* getEGLVersion() const; char const* getEGLExtensions() const; - bool hasEGLExtension(char const* extension) const; }; -// --------------------------------------------------------------------------- -}; // namespace android +} // namespace gl +} // namespace renderengine +} // namespace android #endif // ANDROID_SF_GLEXTENSION_H diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp new file mode 100644 index 0000000000..2bd4e7f6c3 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -0,0 +1,64 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLFramebuffer.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <nativebase/nativebase.h> +#include "GLES20RenderEngine.h" + +namespace android { +namespace renderengine { +namespace gl { + +GLFramebuffer::GLFramebuffer(const GLES20RenderEngine& engine) + : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { + glGenTextures(1, &mTextureName); + glGenFramebuffers(1, &mFramebufferName); +} + +GLFramebuffer::~GLFramebuffer() { + glDeleteFramebuffers(1, &mFramebufferName); + glDeleteTextures(1, &mTextureName); + eglDestroyImageKHR(mEGLDisplay, mEGLImage); +} + +bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) { + if (mEGLImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mEGLDisplay, mEGLImage); + mEGLImage = EGL_NO_IMAGE_KHR; + mBufferWidth = 0; + mBufferHeight = 0; + } + + if (nativeBuffer) { + mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + nativeBuffer, nullptr); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + return false; + } + mBufferWidth = nativeBuffer->width; + mBufferHeight = nativeBuffer->height; + } + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h new file mode 100644 index 0000000000..90c6f4acee --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <renderengine/Framebuffer.h> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { +namespace gl { + +class GLES20RenderEngine; + +class GLFramebuffer : public renderengine::Framebuffer { +public: + explicit GLFramebuffer(const GLES20RenderEngine& engine); + ~GLFramebuffer() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } + uint32_t getTextureName() const { return mTextureName; } + uint32_t getFramebufferName() const { return mFramebufferName; } + int32_t getBufferHeight() const { return mBufferHeight; } + int32_t getBufferWidth() const { return mBufferWidth; } + +private: + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage; + uint32_t mTextureName, mFramebufferName; + + int32_t mBufferHeight = 0; + int32_t mBufferWidth = 0; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Image.cpp b/libs/renderengine/gl/GLImage.cpp index 0d06422a41..5a92093276 100644 --- a/services/surfaceflinger/RenderEngine/Image.cpp +++ b/libs/renderengine/gl/GLImage.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,30 +14,19 @@ * limitations under the License. */ -#include "Image.h" +#include "GLImage.h" #include <vector> #include <log/log.h> - +#include "GLES20RenderEngine.h" #include "GLExtensions.h" -#include "RenderEngine.h" namespace android { -namespace RE { - -Image::~Image() = default; - -namespace impl { - -Image::Image(const RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} - -Image::~Image() { - setNativeWindowBuffer(nullptr, false, 0, 0); -} +namespace renderengine { +namespace gl { -static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidth, - int32_t cropHeight) { +static std::vector<EGLint> buildAttributeList(bool isProtected) { std::vector<EGLint> attrs; attrs.reserve(16); @@ -49,24 +38,18 @@ static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidt attrs.push_back(EGL_TRUE); } - if (cropWidth > 0 && cropHeight > 0) { - attrs.push_back(EGL_IMAGE_CROP_LEFT_ANDROID); - attrs.push_back(0); - attrs.push_back(EGL_IMAGE_CROP_TOP_ANDROID); - attrs.push_back(0); - attrs.push_back(EGL_IMAGE_CROP_RIGHT_ANDROID); - attrs.push_back(cropWidth); - attrs.push_back(EGL_IMAGE_CROP_BOTTOM_ANDROID); - attrs.push_back(cropHeight); - } - attrs.push_back(EGL_NONE); return attrs; } -bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth, - int32_t cropHeight) { +GLImage::GLImage(const GLES20RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} + +GLImage::~GLImage() { + setNativeWindowBuffer(nullptr, false); +} + +bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) { if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { ALOGE("failed to destroy image: %#x", eglGetError()); @@ -75,7 +58,7 @@ bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, } if (buffer) { - std::vector<EGLint> attrs = buildAttributeList(isProtected, cropWidth, cropHeight); + std::vector<EGLint> attrs = buildAttributeList(isProtected); mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, static_cast<EGLClientBuffer>(buffer), attrs.data()); if (mEGLImage == EGL_NO_IMAGE_KHR) { @@ -87,6 +70,6 @@ bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, return true; } -} // namespace impl -} // namespace RE +} // namespace gl +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Image.h b/libs/renderengine/gl/GLImage.h index 1ae7e09cba..0e451f86f5 100644 --- a/services/surfaceflinger/RenderEngine/Image.h +++ b/libs/renderengine/gl/GLImage.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,43 +20,33 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <android-base/macros.h> +#include <renderengine/Image.h> struct ANativeWindowBuffer; namespace android { -namespace RE { +namespace renderengine { +namespace gl { -class Image { -public: - virtual ~Image() = 0; - virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, - int32_t cropWidth, int32_t cropHeight) = 0; -}; - -namespace impl { +class GLES20RenderEngine; -class RenderEngine; - -class Image : public RE::Image { +class GLImage : public renderengine::Image { public: - explicit Image(const RenderEngine& engine); - ~Image() override; + explicit GLImage(const GLES20RenderEngine& engine); + ~GLImage() override; - Image(const Image&) = delete; - Image& operator=(const Image&) = delete; + bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; - bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth, - int32_t cropHeight) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } private: - // methods internal to RenderEngine - friend class RenderEngine; - EGLSurface getEGLImage() const { return mEGLImage; } - EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; + + DISALLOW_COPY_AND_ASSIGN(GLImage); }; -} // namespace impl -} // namespace RE +} // namespace gl +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Surface.cpp b/libs/renderengine/gl/GLSurface.cpp index 0d20f1fd87..2d694e9124 100644 --- a/services/surfaceflinger/RenderEngine/Surface.cpp +++ b/libs/renderengine/gl/GLSurface.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,44 +14,47 @@ * limitations under the License. */ -#include "Surface.h" - -#include "RenderEngine.h" +#include "GLSurface.h" +#include <android/native_window.h> #include <log/log.h> +#include <ui/PixelFormat.h> +#include "GLES20RenderEngine.h" namespace android { -namespace RE { - -Surface::~Surface() = default; - -namespace impl { +namespace renderengine { +namespace gl { -Surface::Surface(const RenderEngine& engine) +GLSurface::GLSurface(const GLES20RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) { // RE does not assume any config when EGL_KHR_no_config_context is supported if (mEGLConfig == EGL_NO_CONFIG_KHR) { - mEGLConfig = RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false); + mEGLConfig = + GLES20RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false); } } -Surface::~Surface() { +GLSurface::~GLSurface() { setNativeWindow(nullptr); } -void Surface::setNativeWindow(ANativeWindow* window) { +void GLSurface::setNativeWindow(ANativeWindow* window) { if (mEGLSurface != EGL_NO_SURFACE) { eglDestroySurface(mEGLDisplay, mEGLSurface); mEGLSurface = EGL_NO_SURFACE; + mSurfaceWidth = 0; + mSurfaceHeight = 0; } mWindow = window; if (mWindow) { mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr); + mSurfaceWidth = ANativeWindow_getWidth(window); + mSurfaceHeight = ANativeWindow_getHeight(window); } } -void Surface::swapBuffers() const { +void GLSurface::swapBuffers() const { if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) { EGLint error = eglGetError(); @@ -64,7 +67,7 @@ void Surface::swapBuffers() const { } } -EGLint Surface::queryConfig(EGLint attrib) const { +EGLint GLSurface::queryConfig(EGLint attrib) const { EGLint value; if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) { value = 0; @@ -73,39 +76,30 @@ EGLint Surface::queryConfig(EGLint attrib) const { return value; } -EGLint Surface::querySurface(EGLint attrib) const { - EGLint value; - if (!eglQuerySurface(mEGLDisplay, mEGLSurface, attrib, &value)) { - value = 0; - } - - return value; -} - -int32_t Surface::queryRedSize() const { +int32_t GLSurface::queryRedSize() const { return queryConfig(EGL_RED_SIZE); } -int32_t Surface::queryGreenSize() const { +int32_t GLSurface::queryGreenSize() const { return queryConfig(EGL_GREEN_SIZE); } -int32_t Surface::queryBlueSize() const { +int32_t GLSurface::queryBlueSize() const { return queryConfig(EGL_BLUE_SIZE); } -int32_t Surface::queryAlphaSize() const { +int32_t GLSurface::queryAlphaSize() const { return queryConfig(EGL_ALPHA_SIZE); } -int32_t Surface::queryWidth() const { - return querySurface(EGL_WIDTH); +int32_t GLSurface::getWidth() const { + return mSurfaceWidth; } -int32_t Surface::queryHeight() const { - return querySurface(EGL_HEIGHT); +int32_t GLSurface::getHeight() const { + return mSurfaceHeight; } -} // namespace impl -} // namespace RE +} // namespace gl +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Surface.h b/libs/renderengine/gl/GLSurface.h index d4d3d8c0f5..092d371ee9 100644 --- a/services/surfaceflinger/RenderEngine/Surface.h +++ b/libs/renderengine/gl/GLSurface.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,44 +19,23 @@ #include <cstdint> #include <EGL/egl.h> +#include <android-base/macros.h> +#include <renderengine/Surface.h> struct ANativeWindow; namespace android { -namespace RE { +namespace renderengine { +namespace gl { -class Surface { -public: - virtual ~Surface() = 0; - - virtual void setCritical(bool enable) = 0; - virtual void setAsync(bool enable) = 0; - - virtual void setNativeWindow(ANativeWindow* window) = 0; - virtual void swapBuffers() const = 0; +class GLES20RenderEngine; - virtual int32_t queryRedSize() const = 0; - virtual int32_t queryGreenSize() const = 0; - virtual int32_t queryBlueSize() const = 0; - virtual int32_t queryAlphaSize() const = 0; - - virtual int32_t queryWidth() const = 0; - virtual int32_t queryHeight() const = 0; -}; - -namespace impl { - -class RenderEngine; - -class Surface final : public RE::Surface { +class GLSurface final : public renderengine::Surface { public: - Surface(const RenderEngine& engine); - ~Surface(); - - Surface(const Surface&) = delete; - Surface& operator=(const Surface&) = delete; + GLSurface(const GLES20RenderEngine& engine); + ~GLSurface() override; - // RE::Surface implementation + // renderengine::Surface implementation void setCritical(bool enable) override { mCritical = enable; } void setAsync(bool enable) override { mAsync = enable; } @@ -68,17 +47,14 @@ public: int32_t queryBlueSize() const override; int32_t queryAlphaSize() const override; - int32_t queryWidth() const override; - int32_t queryHeight() const override; + bool getAsync() const { return mAsync; } + EGLSurface getEGLSurface() const { return mEGLSurface; } + + int32_t getWidth() const override; + int32_t getHeight() const override; private: EGLint queryConfig(EGLint attrib) const; - EGLint querySurface(EGLint attrib) const; - - // methods internal to RenderEngine - friend class RenderEngine; - bool getAsync() const { return mAsync; } - EGLSurface getEGLSurface() const { return mEGLSurface; } EGLDisplay mEGLDisplay; EGLConfig mEGLConfig; @@ -86,10 +62,15 @@ private: bool mCritical = false; bool mAsync = false; + int32_t mSurfaceWidth = 0; + int32_t mSurfaceHeight = 0; + ANativeWindow* mWindow = nullptr; EGLSurface mEGLSurface = EGL_NO_SURFACE; + + DISALLOW_COPY_AND_ASSIGN(GLSurface); }; -} // namespace impl -} // namespace RE +} // namespace gl +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/libs/renderengine/gl/Program.cpp index fe536f0c7c..7d2ea90d82 100644 --- a/services/surfaceflinger/RenderEngine/Program.cpp +++ b/libs/renderengine/gl/Program.cpp @@ -14,17 +14,18 @@ * limitations under the License. */ +#include "Program.h" + #include <stdint.h> #include <log/log.h> -#include <utils/String8.h> - #include <math/mat4.h> -#include "Description.h" -#include "Program.h" +#include <utils/String8.h> #include "ProgramCache.h" namespace android { +namespace renderengine { +namespace gl { Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment) : mInitialized(false) { @@ -73,8 +74,6 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c } } -Program::~Program() {} - bool Program::isValid() const { return mInitialized; } @@ -111,45 +110,35 @@ GLuint Program::buildShader(const char* source, GLenum type) { return shader; } -String8& Program::dumpShader(String8& result, GLenum /*type*/) { - GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader; - GLint l; - glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l); - char* src = new char[l]; - glGetShaderSource(shader, l, nullptr, src); - result.append(src); - delete[] src; - return result; -} - void Program::setUniforms(const Description& desc) { // TODO: we should have a mechanism here to not always reset uniforms that // didn't change for this program. if (mSamplerLoc >= 0) { glUniform1i(mSamplerLoc, 0); - glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.mTexture.getMatrix().asArray()); + glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray()); } if (mColorLoc >= 0) { - const float color[4] = {desc.mColor.r, desc.mColor.g, desc.mColor.b, desc.mColor.a}; + const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a}; glUniform4fv(mColorLoc, 1, color); } if (mInputTransformMatrixLoc >= 0) { - mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix); + mat4 inputTransformMatrix = desc.inputTransformMatrix; glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray()); } if (mOutputTransformMatrixLoc >= 0) { // The output transform matrix and color matrix can be combined as one matrix // that is applied right before applying OETF. - mat4 outputTransformMatrix = desc.mColorMatrix * desc.mOutputTransformMatrix; - glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, - outputTransformMatrix.asArray()); + mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix; + glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray()); } if (mDisplayMaxLuminanceLoc >= 0) { - glUniform1f(mDisplayMaxLuminanceLoc, desc.mDisplayMaxLuminance); + glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance); } // these uniforms are always present - glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray()); + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray()); } -} /* namespace android */ +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Program.h b/libs/renderengine/gl/Program.h index ae796c5852..99bf0f071b 100644 --- a/services/surfaceflinger/RenderEngine/Program.h +++ b/libs/renderengine/gl/Program.h @@ -20,14 +20,16 @@ #include <stdint.h> #include <GLES2/gl2.h> - -#include "Description.h" +#include <renderengine/private/Description.h> #include "ProgramCache.h" namespace android { class String8; +namespace renderengine { +namespace gl { + /* * Abstracts a GLSL program comprising a vertex and fragment shader */ @@ -37,7 +39,7 @@ public: enum { position = 0, texCoords = 1 }; Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); - ~Program(); + ~Program() = default; /* whether this object is usable */ bool isValid() const; @@ -56,7 +58,6 @@ public: private: GLuint buildShader(const char* source, GLenum type); - String8& dumpShader(String8& result, GLenum type); // whether the initialization succeeded bool mInitialized; @@ -86,6 +87,8 @@ private: GLint mOutputTransformMatrixLoc; }; -} /* namespace android */ +} // namespace gl +} // namespace renderengine +} // namespace android #endif /* SF_RENDER_ENGINE_PROGRAM_H */ diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp index fe992f13b6..464fc151a8 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -16,18 +16,21 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "ProgramCache.h" + #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> - +#include <log/log.h> +#include <renderengine/private/Description.h> #include <utils/String8.h> #include <utils/Trace.h> - -#include "Description.h" #include "Program.h" -#include "ProgramCache.h" + +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache) namespace android { -// ----------------------------------------------------------------------------------------------- +namespace renderengine { +namespace gl { /* * A simple formatter class to automatically add the endl and @@ -74,15 +77,7 @@ Formatter& dedent(Formatter& f) { return f; } -// ----------------------------------------------------------------------------------------------- - -ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache) - -ProgramCache::ProgramCache() {} - -ProgramCache::~ProgramCache() {} - -void ProgramCache::primeCache(bool hasWideColor) { +void ProgramCache::primeCache(bool useColorManagement) { uint32_t shaderCount = 0; uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK; // Prime the cache for all combinations of the above masks, @@ -96,16 +91,14 @@ void ProgramCache::primeCache(bool hasWideColor) { if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { continue; } - Program* program = mCache.valueFor(shaderKey); - if (program == nullptr) { - program = generateProgram(shaderKey); - mCache.add(shaderKey, program); + if (mCache.count(shaderKey) == 0) { + mCache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } // Prime for sRGB->P3 conversion - if (hasWideColor) { + if (useColorManagement) { Key shaderKey; shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK, @@ -115,10 +108,8 @@ void ProgramCache::primeCache(bool hasWideColor) { shaderKey.set(Key::OPACITY_MASK, (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); - Program* program = mCache.valueFor(shaderKey); - if (program == nullptr) { - program = generateProgram(shaderKey); - mCache.add(shaderKey, program); + if (mCache.count(shaderKey) == 0) { + mCache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } @@ -132,31 +123,31 @@ void ProgramCache::primeCache(bool hasWideColor) { ProgramCache::Key ProgramCache::computeKey(const Description& description) { Key needs; needs.set(Key::TEXTURE_MASK, - !description.mTextureEnabled + !description.textureEnabled ? Key::TEXTURE_OFF - : description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES + : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT - : description.mTexture.getTextureTarget() == GL_TEXTURE_2D + : description.texture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D : Key::TEXTURE_OFF) - .set(Key::ALPHA_MASK, - (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) + .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) .set(Key::BLEND_MASK, - description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) + description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) .set(Key::OPACITY_MASK, - description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) + description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, - description.hasInputTransformMatrix() ? - Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) + description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON + : Key::INPUT_TRANSFORM_MATRIX_OFF) .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, - description.hasOutputTransformMatrix() || description.hasColorMatrix() ? - Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF); + description.hasOutputTransformMatrix() || description.hasColorMatrix() + ? Key::OUTPUT_TRANSFORM_MATRIX_ON + : Key::OUTPUT_TRANSFORM_MATRIX_OFF); needs.set(Key::Y410_BT2020_MASK, - description.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); + description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { - switch (description.mInputTransferFunction) { + switch (description.inputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); @@ -172,7 +163,7 @@ ProgramCache::Key ProgramCache::computeKey(const Description& description) { break; } - switch (description.mOutputTransferFunction) { + switch (description.outputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); @@ -640,7 +631,8 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { // avoid divide by 0 by adding 0.5/256 to the alpha channel fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; } - fs << "gl_FragColor.rgb = OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; + fs << "gl_FragColor.rgb = " + "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; if (!needs.isOpaque() && needs.isPremultiplied()) { // and re-premultiply if needed after gamma correction fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);"; @@ -651,7 +643,7 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { return fs.getString(); } -Program* ProgramCache::generateProgram(const Key& needs) { +std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { ATRACE_CALL(); // vertex shader @@ -660,8 +652,7 @@ Program* ProgramCache::generateProgram(const Key& needs) { // fragment shader String8 fs = generateFragmentShader(needs); - Program* program = new Program(needs, vs.string(), fs.string()); - return program; + return std::make_unique<Program>(needs, vs.string(), fs.string()); } void ProgramCache::useProgram(const Description& description) { @@ -669,23 +660,25 @@ void ProgramCache::useProgram(const Description& description) { Key needs(computeKey(description)); // look-up the program in the cache - Program* program = mCache.valueFor(needs); - if (program == nullptr) { + auto it = mCache.find(needs); + if (it == mCache.end()) { // we didn't find our program, so generate one... - nsecs_t time = -systemTime(); - program = generateProgram(needs); - mCache.add(needs, program); - time += systemTime(); + nsecs_t time = systemTime(); + it = mCache.emplace(needs, generateProgram(needs)).first; + time = systemTime() - time; ALOGV(">>> generated new program: needs=%08X, time=%u ms (%zu programs)", needs.mKey, uint32_t(ns2ms(time)), mCache.size()); } // here we have a suitable program for this description + std::unique_ptr<Program>& program = it->second; if (program->isValid()) { program->use(); program->setUniforms(description); } } -} /* namespace android */ +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h index 983e7baf02..d60fee6345 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.h +++ b/libs/renderengine/gl/ProgramCache.h @@ -17,20 +17,26 @@ #ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H #define SF_RENDER_ENGINE_PROGRAMCACHE_H -#include <GLES2/gl2.h> +#include <memory> +#include <unordered_map> -#include <utils/KeyedVector.h> +#include <GLES2/gl2.h> +#include <renderengine/private/Description.h> #include <utils/Singleton.h> #include <utils/TypeHelpers.h> -#include "Description.h" - namespace android { -class Description; +class String8; + +namespace renderengine { + +struct Description; + +namespace gl { + class Formatter; class Program; -class String8; /* * This class generates GLSL programs suitable to handle a given @@ -151,17 +157,22 @@ public: } inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; } - // this is the definition of a friend function -- not a method of class Needs - friend inline int strictly_order_type(const Key& lhs, const Key& rhs) { - return (lhs.mKey < rhs.mKey) ? 1 : 0; - } + // for use by std::unordered_map + + bool operator==(const Key& other) const { return mKey == other.mKey; } + + struct Hash { + size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); } + }; }; - ProgramCache(); - ~ProgramCache(); + ProgramCache() = default; + ~ProgramCache() = default; // Generate shaders to populate the cache - void primeCache(bool hasWideColor); + void primeCache(bool useColorManagement); + + size_t getSize() const { return mCache.size(); } // useProgram lookup a suitable program in the cache or generates one // if none can be found. @@ -179,19 +190,22 @@ private: // Generate OETF based from Key. static void generateOETF(Formatter& fs, const Key& needs); // generates a program from the Key - static Program* generateProgram(const Key& needs); + static std::unique_ptr<Program> generateProgram(const Key& needs); // generates the vertex shader from the Key static String8 generateVertexShader(const Key& needs); // generates the fragment shader from the Key static String8 generateFragmentShader(const Key& needs); // Key/Value map used for caching Programs. Currently the cache - // is never shrunk. - DefaultKeyedVector<Key, Program*> mCache; + // is never shrunk (and the GL program objects are never deleted). + std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash> mCache; }; -ANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key) +} // namespace gl +} // namespace renderengine + +ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) -} /* namespace android */ +} // namespace android #endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */ diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h new file mode 100644 index 0000000000..5941cdfef0 --- /dev/null +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +namespace android { +namespace renderengine { + +// DisplaySettings contains the settings that are applicable when drawing all +// layers for a given display. +struct DisplaySettings { + // Rectangle describing the physical display. We will project from the + // logical clip onto this rectangle. + Rect physicalDisplay; + + // Rectangle bounded by the x,y- clipping planes in the logical display, so + // that the orthographic projection matrix can be computed. When + // constructing this matrix, z-coordinate bound are assumed to be at z=0 and + // z=1. + Rect clip; + + // Global transform to apply to all layers. + mat4 globalTransform; + + // Maximum luminance pulled from the display's HDR capabilities. + float maxLuminence; + + // Output dataspace that will be populated if wide color gamut is used, or + // DataSpace::UNKNOWN otherwise. + ui::Dataspace outputDataspace; + + // Additional color transform to apply in linear space after transforming + // to the output dataspace. + mat4 colorTransform; + + // Region that will be cleared to (0, 0, 0, 0) prior to rendering. + // clearRegion will first be transformed by globalTransform so that it will + // be in the same coordinate space as the rendered layers. + Region clearRegion; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h new file mode 100644 index 0000000000..558b9c7b21 --- /dev/null +++ b/libs/renderengine/include/renderengine/Framebuffer.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Framebuffer { +public: + virtual ~Framebuffer() = default; + + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/renderengine/include/renderengine/Image.h new file mode 100644 index 0000000000..3bb47318ef --- /dev/null +++ b/libs/renderengine/include/renderengine/Image.h @@ -0,0 +1,31 @@ +/* + * 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 + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Image { +public: + virtual ~Image() = default; + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h new file mode 100644 index 0000000000..facea214a9 --- /dev/null +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -0,0 +1,92 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <math/vec3.h> +#include <renderengine/Texture.h> +#include <ui/FloatRect.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/Transform.h> + +namespace android { +namespace renderengine { + +// Metadata describing the input buffer to render from. +struct Buffer { + // Buffer containing the image that we will render. + // If buffer == nullptr, then the rest of the fields in this struct will be + // ignored. + sp<GraphicBuffer> buffer; + + // Texture identifier to bind the external texture to. + // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. + uint32_t textureName; + + // Whether to use filtering when rendering the texture. + bool useTextureFiltering; + + // Transform matrix to apply to texture coordinates. + mat4 textureTransform; + + // Wheteher to use pre-multiplied alpha + bool usePremultipliedAlpha; + + // HDR color-space setting for Y410. + bool isY410BT2020; +}; + +// Metadata describing the layer geometry. +struct Geometry { + // Boundaries of the layer. + FloatRect boundaries; + + // Transform matrix to apply to mesh coordinates. + mat4 positionTransform; +}; + +// Descriptor of the source pixels for this layer. +struct PixelSource { + // Source buffer + Buffer buffer; + + // The solid color with which to fill the layer. + // This should only be populated if we don't render from an application + // buffer. + half3 solidColor; +}; + +// The settings that RenderEngine requires for correctly rendering a Layer. +struct LayerSettings { + // Geometry information + Geometry geometry; + + // Source pixels for this layer. + PixelSource source; + + // Alpha option to apply to the source pixels + half alpha; + + // Color space describing how the source pixels should be interpreted. + ui::Dataspace sourceDataspace; +}; + +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h index d0a9ac0c65..fe9022db03 100644 --- a/services/surfaceflinger/RenderEngine/Mesh.h +++ b/libs/renderengine/include/renderengine/Mesh.h @@ -17,9 +17,12 @@ #ifndef SF_RENDER_ENGINE_MESH_H #define SF_RENDER_ENGINE_MESH_H +#include <vector> + #include <stdint.h> namespace android { +namespace renderengine { class Mesh { public: @@ -30,7 +33,7 @@ public: }; Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0); - ~Mesh(); + ~Mesh() = default; /* * VertexArray handles the stride automatically. @@ -89,7 +92,8 @@ private: float* getPositions(); float* getTexCoords(); - float* mVertices; + + std::vector<float> mVertices; size_t mVertexCount; size_t mVertexSize; size_t mTexCoordsSize; @@ -97,5 +101,6 @@ private: Primitive mPrimitive; }; -} /* namespace android */ +} // namespace renderengine +} // namespace android #endif /* SF_RENDER_ENGINE_MESH_H */ diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h new file mode 100644 index 0000000000..becb3c3a10 --- /dev/null +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -0,0 +1,215 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDERENGINE_H_ +#define SF_RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <memory> + +#include <android-base/unique_fd.h> +#include <math/mat4.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <ui/GraphicTypes.h> +#include <ui/Transform.h> + +/** + * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). + */ +#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" + +struct ANativeWindowBuffer; + +namespace android { + +class String8; +class Rect; +class Region; + +namespace renderengine { + +class BindNativeBufferAsFramebuffer; +class Image; +class Mesh; +class Surface; +class Texture; + +namespace impl { +class RenderEngine; +} + +class RenderEngine { +public: + enum FeatureFlag { + USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color + USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context + }; + + static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags); + + virtual ~RenderEngine() = 0; + + // ----- BEGIN DEPRECATED INTERFACE ----- + // This interface, while still in use until a suitable replacement is built, + // should be considered deprecated, minus some methods which still may be + // used to support legacy behavior. + + virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0; + virtual std::unique_ptr<Surface> createSurface() = 0; + virtual std::unique_ptr<Image> createImage() = 0; + + virtual void primeCache() const = 0; + + // dump the extension strings. always call the base class. + virtual void dump(String8& result) = 0; + + virtual bool useNativeFenceSync() const = 0; + virtual bool useWaitSync() const = 0; + + virtual bool isCurrent() const = 0; + virtual bool setCurrentSurface(const Surface& surface) = 0; + virtual void resetCurrentSurface() = 0; + + // helpers + // flush submits RenderEngine command stream for execution and returns a + // native fence fd that is signaled when the execution has completed. It + // returns -1 on errors. + virtual base::unique_fd flush() = 0; + // finish waits until RenderEngine command stream has been executed. It + // returns false on errors. + virtual bool finish() = 0; + // waitFence inserts a wait on an external fence fd to RenderEngine + // command stream. It returns false on errors. + virtual bool waitFence(base::unique_fd fenceFd) = 0; + + virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; + virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) = 0; + + virtual void setScissor(const Rect& region) = 0; + virtual void disableScissor() = 0; + virtual void genTextures(size_t count, uint32_t* names) = 0; + virtual void deleteTextures(size_t count, uint32_t const* names) = 0; + virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; + // When binding a native buffer, it must be done before setViewportAndProjection + // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. + virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; + virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; + + // set-up + virtual void checkErrors() const = 0; + virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) = 0; + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color) = 0; + virtual void setupLayerTexturing(const Texture& texture) = 0; + virtual void setupLayerBlackedOut() = 0; + virtual void setupFillWithColor(float r, float g, float b, float a) = 0; + + // Set a color transform matrix that is applied in linear space right before OETF. + virtual void setColorTransform(const mat4& /* colorTransform */) = 0; + virtual void disableTexturing() = 0; + virtual void disableBlending() = 0; + + // HDR and color management support + virtual void setSourceY410BT2020(bool enable) = 0; + virtual void setSourceDataSpace(ui::Dataspace source) = 0; + virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; + virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; + + // drawing + virtual void drawMesh(const Mesh& mesh) = 0; + + // queries + virtual size_t getMaxTextureSize() const = 0; + virtual size_t getMaxViewportDims() const = 0; + + // ----- END DEPRECATED INTERFACE ----- + + // ----- BEGIN NEW INTERFACE ----- + + // Renders layers for a particular display via GPU composition. This method + // should be called for every display that needs to be rendered via the GPU. + // @param settings The display-wide settings that should be applied prior to + // drawing any layers. + // @param layers The layers to draw onto the display, in Z-order. + // @param buffer The buffer which will be drawn to. This buffer will be + // ready once displayFence fires. + // @param displayFence 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. + // TODO(alecmouri): Consider making this a multi-display API, so that the + // caller deoes not need to handle multiple fences. + virtual status_t drawLayers(const DisplaySettings& settings, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* const buffer, + base::unique_fd* displayFence) const = 0; + + // TODO(alecmouri): Expose something like bindTexImage() so that devices + // that don't support native sync fences can get rid of code duplicated + // between BufferStateLayer and BufferQueueLayer for binding an external + // texture. + + // TODO(alecmouri): Add API to help with managing a texture pool. +}; + +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer) + : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer) + ? mEngine.bindFrameBuffer(mFramebuffer.get()) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr); + mEngine.unbindFrameBuffer(mFramebuffer.get()); + } + status_t getStatus() const { return mStatus; } + +private: + RenderEngine& mEngine; + std::unique_ptr<Framebuffer> mFramebuffer; + status_t mStatus; +}; + +namespace impl { + +// impl::RenderEngine contains common implementation that is graphics back-end agnostic. +class RenderEngine : public renderengine::RenderEngine { +public: + virtual ~RenderEngine() = 0; + + bool useNativeFenceSync() const override; + bool useWaitSync() const override; + +protected: + RenderEngine(uint32_t featureFlags); + const uint32_t mFeatureFlags; +}; + +} // namespace impl +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDERENGINE_H_ */ diff --git a/libs/renderengine/include/renderengine/Surface.h b/libs/renderengine/include/renderengine/Surface.h new file mode 100644 index 0000000000..ba7331daab --- /dev/null +++ b/libs/renderengine/include/renderengine/Surface.h @@ -0,0 +1,46 @@ +/* + * 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 <cstdint> + +struct ANativeWindow; + +namespace android { +namespace renderengine { + +class Surface { +public: + virtual ~Surface() = default; + + virtual void setCritical(bool enable) = 0; + virtual void setAsync(bool enable) = 0; + + virtual void setNativeWindow(ANativeWindow* window) = 0; + virtual void swapBuffers() const = 0; + + virtual int32_t queryRedSize() const = 0; + virtual int32_t queryGreenSize() const = 0; + virtual int32_t queryBlueSize() const = 0; + virtual int32_t queryAlphaSize() const = 0; + + virtual int32_t getWidth() const = 0; + virtual int32_t getHeight() const = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Texture.h b/libs/renderengine/include/renderengine/Texture.h index 56b6b31573..c69ace0603 100644 --- a/services/surfaceflinger/RenderEngine/Texture.h +++ b/libs/renderengine/include/renderengine/Texture.h @@ -14,22 +14,17 @@ * limitations under the License. */ -#include <math/mat4.h> -#include <stdint.h> - #ifndef SF_RENDER_ENGINE_TEXTURE_H #define SF_RENDER_ENGINE_TEXTURE_H +#include <stdint.h> + +#include <math/mat4.h> + namespace android { +namespace renderengine { class Texture { - uint32_t mTextureName; - uint32_t mTextureTarget; - size_t mWidth; - size_t mHeight; - bool mFiltering; - mat4 mTextureMatrix; - public: enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; @@ -50,7 +45,16 @@ public: bool getFiltering() const; size_t getWidth() const; size_t getHeight() const; + +private: + uint32_t mTextureName; + uint32_t mTextureTarget; + size_t mWidth; + size_t mHeight; + bool mFiltering; + mat4 mTextureMatrix; }; -} /* namespace android */ +} // namespace renderengine +} // namespace android #endif /* SF_RENDER_ENGINE_TEXTURE_H */ diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h new file mode 100644 index 0000000000..eadd656749 --- /dev/null +++ b/libs/renderengine/include/renderengine/private/Description.h @@ -0,0 +1,81 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ +#define SF_RENDER_ENGINE_DESCRIPTION_H_ + +#include <renderengine/Texture.h> +#include <ui/GraphicTypes.h> + +namespace android { +namespace renderengine { + +/* + * This is the structure that holds the state of the rendering engine. + * This class is used to generate a corresponding GLSL program and set the + * appropriate uniform. + */ +struct Description { + enum class TransferFunction : int { + LINEAR, + SRGB, + ST2084, + HLG, // Hybrid Log-Gamma for HDR. + }; + + static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace); + + Description() = default; + ~Description() = default; + + bool hasInputTransformMatrix() const; + bool hasOutputTransformMatrix() const; + bool hasColorMatrix() const; + + // whether textures are premultiplied + bool isPremultipliedAlpha = false; + // whether this layer is marked as opaque + bool isOpaque = true; + + // Texture this layer uses + Texture texture; + bool textureEnabled = false; + + // color used when texturing is disabled or when setting alpha. + half4 color; + + // true if the sampled pixel values are in Y410/BT2020 rather than RGBA + bool isY410BT2020 = false; + + // transfer functions for the input/output + TransferFunction inputTransferFunction = TransferFunction::LINEAR; + TransferFunction outputTransferFunction = TransferFunction::LINEAR; + + float displayMaxLuminance; + + // projection matrix + mat4 projectionMatrix; + + // The color matrix will be applied in linear space right before OETF. + mat4 colorMatrix; + mat4 inputTransformMatrix; + mat4 outputTransformMatrix; +}; + +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp new file mode 100644 index 0000000000..65b7c82a0f --- /dev/null +++ b/libs/renderengine/tests/Android.bp @@ -0,0 +1,36 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "librenderengine_test", + defaults: ["surfaceflinger_defaults"], + test_suites: ["device-tests"], + srcs: [ + "RenderEngineTest.cpp", + ], + static_libs: [ + "libgmock", + "librenderengine", + ], + shared_libs: [ + "libcutils", + "libEGL", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libui", + "libutils", + ], +} diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp new file mode 100644 index 0000000000..345c7eacf5 --- /dev/null +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <renderengine/RenderEngine.h> +#include <ui/PixelFormat.h> + +namespace android { + +class RenderEngineTest : public ::testing::Test { +public: + RenderEngineTest() { + // Initialize with some sane defaults. + // TODO(alecmouri): This should probably be the same instance used by + // SurfaceFlinger eventually. + mRE = renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), + 0); + } + + status_t drawEmptyLayers() { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + // Meaningless buffer since we don't do any drawing + sp<GraphicBuffer> buffer = new GraphicBuffer(); + base::unique_fd fence; + return mRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence); + } + +private: + std::unique_ptr<renderengine::RenderEngine> mRE; +}; + +TEST_F(RenderEngineTest, drawLayers_noLayersToDraw_works) { + status_t result = drawEmptyLayers(); + ASSERT_EQ(NO_ERROR, result); +} + +} // namespace android diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index a0e368c7e4..d9a986ed00 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -318,7 +318,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi // If the sensor is protected by a permission we need to know if it is // a runtime one to determine whether we can use the permission cache. sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder); mRequiredPermissionRuntime = permCtrl->isRuntimePermission( String16(mRequiredPermission)); diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 6f68fb5f7b..46ba7c6250 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -37,7 +37,7 @@ namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0), + : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0), mNumAcksToSend(0) { mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } @@ -82,9 +82,9 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { sp<Looper> SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); - if (mLooper == 0) { + if (mLooper == nullptr) { mLooper = new Looper(true); - mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); + mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr); } return mLooper; } @@ -97,7 +97,7 @@ status_t SensorEventQueue::waitForEvent() const int events; int32_t result; do { - result = looper->pollOnce(-1, NULL, &events, NULL); + result = looper->pollOnce(-1, nullptr, &events, nullptr); if (result == ALOOPER_POLL_ERROR) { ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno); result = -EPIPE; // unknown error, so we make up one diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index b9ae524ee8..5840d51079 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -62,7 +62,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector<String16> packages; interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); @@ -93,7 +93,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) } SensorManager::SensorManager(const String16& opPackageName) - : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { + : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { // okay we're not locked here, but it's not needed during construction assertStateLocked(); } @@ -128,13 +128,13 @@ void SensorManager::sensorManagerDied() { Mutex::Autolock _l(mLock); mSensorServer.clear(); free(mSensorList); - mSensorList = NULL; + mSensorList = nullptr; mSensors.clear(); } status_t SensorManager::assertStateLocked() { bool initSensorManager = false; - if (mSensorServer == NULL) { + if (mSensorServer == nullptr) { initSensorManager = true; } else { // Ping binder to check if sensorservice is alive. @@ -164,7 +164,7 @@ status_t SensorManager::assertStateLocked() { size_t count = mSensors.size(); mSensorList = static_cast<Sensor const**>(malloc(count * sizeof(Sensor*))); - LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL"); + LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); for (size_t i=0 ; i<count ; i++) { mSensorList[i] = mSensors.array() + i; @@ -222,7 +222,7 @@ Sensor const* SensorManager::getDefaultSensor(int type) } } } - return NULL; + return nullptr; } sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) { @@ -232,10 +232,10 @@ sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mo while (assertStateLocked() == NO_ERROR) { sp<ISensorEventConnection> connection = mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName); - if (connection == NULL) { + if (connection == nullptr) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); - return NULL; + return nullptr; } queue = new SensorEventQueue(connection); break; diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index d25ad1a46d..0a0c8ca014 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -26,6 +26,7 @@ cc_library_shared { "-Werror", ], cppflags: [ + "-std=c++1z", "-Weverything", // The static constructors and destructors in this library have not been noted to @@ -53,6 +54,8 @@ cc_library_shared { srcs: [ "ColorSpace.cpp", + "BufferHubBuffer.cpp", + "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", @@ -65,6 +68,7 @@ cc_library_shared { "PixelFormat.cpp", "Rect.cpp", "Region.cpp", + "Transform.cpp", "UiConfig.cpp", ], @@ -74,7 +78,7 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.configstore@1.0", @@ -89,10 +93,11 @@ cc_library_shared { "libutils", "libutilscallstack", "liblog", + "libpdx_default_transport", // TODO(b/112338294): Remove this once BufferHub moved to use Binder. ], export_shared_lib_headers: [ - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", ], static_libs: [ @@ -101,8 +106,27 @@ cc_library_shared { "libmath", ], + // bufferhub is not used when building libgui for vendors + target: { + vendor: { + exclude_srcs: [ + "BufferHubBuffer.cpp", + "BufferHubMetadata.cpp", + ], + exclude_header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + ], + exclude_shared_libs: [ + "libpdx_default_transport", + ], + }, + }, + header_libs: [ "libbase_headers", + "libbufferhub_headers", + "libdvr_headers", "libnativebase_headers", "libhardware_headers", "libui_headers", @@ -120,6 +144,9 @@ cc_library_shared { "libhardware_headers", "libui_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_library_headers { diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp new file mode 100644 index 0000000000..8cc1a4e3b4 --- /dev/null +++ b/libs/ui/BufferHubBuffer.cpp @@ -0,0 +1,189 @@ +/* + * 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. + */ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <private/dvr/bufferhub_rpc.h> +#pragma clang diagnostic pop + +#include <ui/BufferHubBuffer.h> + +#include <poll.h> + +using android::dvr::BufferTraits; +using android::dvr::DetachedBufferRPC; +using android::dvr::NativeHandleWrapper; + +// TODO(b/112338294): Remove PDX dependencies from libui. +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; +using android::pdx::default_transport::ClientChannel; +using android::pdx::default_transport::ClientChannelFactory; + +namespace android { + +namespace { + +// TODO(b/112338294): Remove this string literal after refactoring BufferHub +// to use Binder. +static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client"; + +} // namespace + +BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {} + +BufferHubClient::BufferHubClient(LocalChannelHandle mChannelHandle) + : Client(ClientChannel::Create(std::move(mChannelHandle))) {} + +BufferHubClient::~BufferHubClient() {} + +bool BufferHubClient::IsValid() const { + return IsConnected() && GetChannelHandle().valid(); +} + +LocalChannelHandle BufferHubClient::TakeChannelHandle() { + if (IsConnected()) { + return std::move(GetChannelHandle()); + } else { + return {}; + } +} + +BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, + uint32_t format, uint64_t usage, size_t mUserMetadataSize) { + ATRACE_CALL(); + ALOGD("BufferHubBuffer::BufferHubBuffer: width=%u height=%u layerCount=%u, format=%u " + "usage=%" PRIx64 " mUserMetadataSize=%zu", + width, height, layerCount, format, usage, mUserMetadataSize); + + auto status = + mClient.InvokeRemoteMethod<DetachedBufferRPC::Create>(width, height, layerCount, format, + usage, mUserMetadataSize); + if (!status) { + ALOGE("BufferHubBuffer::BufferHubBuffer: Failed to create detached buffer: %s", + status.GetErrorMessage().c_str()); + mClient.Close(-status.error()); + } + + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("BufferHubBuffer::BufferHubBuffer: Failed to import buffer: %s", strerror(-ret)); + mClient.Close(ret); + } +} + +BufferHubBuffer::BufferHubBuffer(LocalChannelHandle mChannelHandle) + : mClient(std::move(mChannelHandle)) { + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("BufferHubBuffer::BufferHubBuffer: Failed to import buffer: %s", strerror(-ret)); + mClient.Close(ret); + } +} + +int BufferHubBuffer::ImportGraphicBuffer() { + ATRACE_CALL(); + + auto status = mClient.InvokeRemoteMethod<DetachedBufferRPC::Import>(); + if (!status) { + ALOGE("BufferHubBuffer::BufferHubBuffer: Failed to import GraphicBuffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } + + BufferTraits<LocalHandle> bufferTraits = status.take(); + if (bufferTraits.id() < 0) { + ALOGE("BufferHubBuffer::BufferHubBuffer: Received an invalid id!"); + return -EIO; + } + + // Stash the buffer id to replace the value in mId. + const int bufferId = bufferTraits.id(); + + // Import the metadata. + mMetadata = BufferHubMetadata::Import(bufferTraits.take_metadata_handle()); + + if (!mMetadata.IsValid()) { + ALOGE("BufferHubBuffer::ImportGraphicBuffer: invalid metadata."); + return -ENOMEM; + } + + if (mMetadata.metadata_size() != bufferTraits.metadata_size()) { + ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata buffer too small: " + "%zu, expected: %" PRIu64 ".", + mMetadata.metadata_size(), bufferTraits.metadata_size()); + return -ENOMEM; + } + + size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size()); + if (metadataSize < dvr::BufferHubDefs::kMetadataHeaderSize) { + ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata too small: %zu", metadataSize); + return -EINVAL; + } + + // Import the buffer: We only need to hold on the native_handle_t here so that + // GraphicBuffer instance can be created in future. + mBufferHandle = bufferTraits.take_buffer_handle(); + + // If all imports succeed, replace the previous buffer and id. + mId = bufferId; + mClientStateMask = bufferTraits.client_state_mask(); + + // TODO(b/112012161) Set up shared fences. + ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(), + mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::Poll(int timeoutMs) { + ATRACE_CALL(); + + pollfd p = {mClient.event_fd(), POLLIN, 0}; + return poll(&p, 1, timeoutMs); +} + +Status<LocalChannelHandle> BufferHubBuffer::Duplicate() { + ATRACE_CALL(); + ALOGD("BufferHubBuffer::Duplicate: id=%d.", mId); + + auto statusOrHandle = mClient.InvokeRemoteMethod<DetachedBufferRPC::Duplicate>(); + + if (!statusOrHandle.ok()) { + ALOGE("BufferHubBuffer::Duplicate: Failed to duplicate buffer (id=%d): %s.", mId, + statusOrHandle.GetErrorMessage().c_str()); + } + return statusOrHandle; +} + +} // namespace android diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp new file mode 100644 index 0000000000..1e08ed1388 --- /dev/null +++ b/libs/ui/BufferHubMetadata.cpp @@ -0,0 +1,103 @@ +/* + * 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 <errno.h> +#include <sys/mman.h> + +#include <cutils/ashmem.h> +#include <log/log.h> +#include <ui/BufferHubMetadata.h> + +namespace android { + +namespace { + +static const int kAshmemProt = PROT_READ | PROT_WRITE; + +} // namespace + +using dvr::BufferHubDefs::kMetadataHeaderSize; +using dvr::BufferHubDefs::MetadataHeader; + +/* static */ +BufferHubMetadata BufferHubMetadata::Create(size_t userMetadataSize) { + // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it + // cannot overflow uint32_t. + if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) { + ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize); + return {}; + } + + const size_t metadataSize = userMetadataSize + kMetadataHeaderSize; + int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize); + if (fd < 0) { + ALOGE("BufferHubMetadata::Create: failed to create ashmem region."); + return {}; + } + + // Hand over the ownership of the fd to a pdx::LocalHandle immediately after the successful + // return of ashmem_create_region. The ashmemHandle is going to own the fd and to prevent fd + // leaks during error handling. + pdx::LocalHandle ashmemHandle{fd}; + + if (ashmem_set_prot_region(ashmemHandle.Get(), kAshmemProt) != 0) { + ALOGE("BufferHubMetadata::Create: failed to set protect region."); + return {}; + } + + return BufferHubMetadata::Import(std::move(ashmemHandle)); +} + +/* static */ +BufferHubMetadata BufferHubMetadata::Import(pdx::LocalHandle ashmemHandle) { + if (!ashmem_valid(ashmemHandle.Get())) { + ALOGE("BufferHubMetadata::Import: invalid ashmem fd."); + return {}; + } + + size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemHandle.Get())); + size_t userMetadataSize = metadataSize - kMetadataHeaderSize; + + // Note that here the buffer state is mapped from shared memory as an atomic object. The + // std::atomic's constructor will not be called so that the original value stored in the memory + // region can be preserved. + auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt, + MAP_SHARED, ashmemHandle.Get(), + /*offset=*/0)); + if (metadataHeader == nullptr) { + ALOGE("BufferHubMetadata::Import: failed to map region."); + return {}; + } + + return BufferHubMetadata(userMetadataSize, std::move(ashmemHandle), metadataHeader); +} + +BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle, + MetadataHeader* metadataHeader) + : mUserMetadataSize(userMetadataSize), + mAshmemHandle(std::move(ashmemHandle)), + mMetadataHeader(metadataHeader) {} + +BufferHubMetadata::~BufferHubMetadata() { + if (mMetadataHeader != nullptr) { + int ret = munmap(mMetadataHeader, metadata_size()); + ALOGE_IF(ret != 0, + "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno); + mMetadataHeader = nullptr; + } +} + +} // namespace android diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 61df02d41d..ee06d930d8 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -234,6 +234,9 @@ std::string decodeColorMode(ColorMode colorMode) { case ColorMode::BT2020: return std::string("ColorMode::BT2020"); + case ColorMode::DISPLAY_BT2020: + return std::string("ColorMode::DISPLAY_BT2020"); + case ColorMode::BT2100_PQ: return std::string("ColorMode::BT2100_PQ"); diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 14147663de..340231d352 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -33,18 +33,6 @@ namespace android { const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); -void* FenceTime::operator new(size_t byteCount) noexcept { - void *p = nullptr; - if (posix_memalign(&p, alignof(FenceTime), byteCount)) { - return nullptr; - } - return p; -} - -void FenceTime::operator delete(void *p) { - free(p); -} - FenceTime::FenceTime(const sp<Fence>& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index c50d1d038e..5a1ddee002 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -22,7 +22,6 @@ #include <grallocusage/GrallocUsageConversion.h> -#include <ui/DetachedBufferHandle.h> #include <ui/Gralloc2.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> @@ -490,24 +489,6 @@ status_t GraphicBuffer::unflatten( return NO_ERROR; } -bool GraphicBuffer::isDetachedBuffer() const { - return mDetachedBufferHandle && mDetachedBufferHandle->isValid(); -} - -status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) { - if (isDetachedBuffer()) { - ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this " - "GraphicBuffer. Replacing the old one."); - } - - mDetachedBufferHandle = std::move(channel); - return NO_ERROR; -} - -std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() { - return std::move(mDetachedBufferHandle); -} - // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Transform.cpp b/libs/ui/Transform.cpp index e05ed5375e..8e949ec880 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -17,17 +17,12 @@ #include <math.h> #include <cutils/compiler.h> -#include <utils/String8.h> #include <ui/Region.h> - -#include "Transform.h" -#include "clz.h" - -// --------------------------------------------------------------------------- +#include <ui/Transform.h> +#include <utils/String8.h> namespace android { - -// --------------------------------------------------------------------------- +namespace ui { Transform::Transform() { reset(); @@ -41,8 +36,7 @@ Transform::Transform(uint32_t orientation) { set(orientation, 0, 0); } -Transform::~Transform() { -} +Transform::~Transform() = default; static const float EPSILON = 0.0f; @@ -67,7 +61,7 @@ Transform Transform::operator * (const Transform& rhs) const const mat33& A(mMatrix); const mat33& B(rhs.mMatrix); mat33& D(r.mMatrix); - for (int i=0 ; i<3 ; i++) { + for (size_t i = 0; i < 3; i++) { const float v0 = A[0][i]; const float v1 = A[1][i]; const float v2 = A[2][i]; @@ -83,6 +77,12 @@ Transform Transform::operator * (const Transform& rhs) const return r; } +Transform& Transform::operator=(const Transform& other) { + mMatrix = other.mMatrix; + mType = other.mType; + return *this; +} + const vec3& Transform::operator [] (size_t i) const { return mMatrix[i]; } @@ -97,10 +97,10 @@ float Transform::ty() const { void Transform::reset() { mType = IDENTITY; - for(int i=0 ; i<3 ; i++) { + for(size_t i = 0; i < 3; i++) { vec3& v(mMatrix[i]); - for (int j=0 ; j<3 ; j++) - v[j] = ((i==j) ? 1.0f : 0.0f); + for (size_t j = 0; j < 3; j++) + v[j] = ((i == j) ? 1.0f : 0.0f); } } @@ -137,7 +137,7 @@ status_t Transform::set(uint32_t flags, float w, float h) Transform H, V, R; if (flags & ROT_90) { // w & h are inverted when rotating by 90 degrees - swap(w, h); + std::swap(w, h); } if (flags & FLIP_H) { @@ -210,15 +210,15 @@ Rect Transform::transform(const Rect& bounds, bool roundOutwards) const rb = transform(rb); if (roundOutwards) { - r.left = floorf(min(lt[0], rt[0], lb[0], rb[0])); - r.top = floorf(min(lt[1], rt[1], lb[1], rb[1])); - r.right = ceilf(max(lt[0], rt[0], lb[0], rb[0])); - r.bottom = ceilf(max(lt[1], rt[1], lb[1], rb[1])); + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}))); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}))); + r.right = static_cast<int32_t>(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]}))); + r.bottom = static_cast<int32_t>(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]}))); } else { - r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); - r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); - r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); - r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); + r.right = static_cast<int32_t>(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.bottom = static_cast<int32_t>(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); } return r; @@ -237,10 +237,10 @@ FloatRect Transform::transform(const FloatRect& bounds) const rb = transform(rb); FloatRect r; - r.left = min(lt[0], rt[0], lb[0], rb[0]); - r.top = min(lt[1], rt[1], lb[1], rb[1]); - r.right = max(lt[0], rt[0], lb[0], rb[0]); - r.bottom = max(lt[1], rt[1], lb[1], rb[1]); + r.left = std::min({lt[0], rt[0], lb[0], rb[0]}); + r.top = std::min({lt[1], rt[1], lb[1], rb[1]}); + r.right = std::max({lt[0], rt[0], lb[0], rb[0]}); + r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]}); return r; } @@ -259,8 +259,8 @@ Region Transform::transform(const Region& reg) const out.set(transform(reg.bounds())); } } else { - int xpos = floorf(tx() + 0.5f); - int ypos = floorf(ty() + 0.5f); + int xpos = static_cast<int>(floorf(tx() + 0.5f)); + int ypos = static_cast<int>(floorf(ty() + 0.5f)); out = reg.translate(xpos, ypos); } return out; @@ -343,7 +343,7 @@ Transform Transform::inverse() const { const float x = M[2][0]; const float y = M[2][1]; - const float idet = 1.0 / (a*d - b*c); + const float idet = 1.0f / (a*d - b*c); result.mMatrix[0][0] = d*idet; result.mMatrix[0][1] = -c*idet; result.mMatrix[1][0] = -b*idet; @@ -404,28 +404,13 @@ void Transform::dump(const char* name) const type.append("TRANSLATE "); ALOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string()); - ALOGD("%.4f %.4f %.4f", m[0][0], m[1][0], m[2][0]); - ALOGD("%.4f %.4f %.4f", m[0][1], m[1][1], m[2][1]); - ALOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]); -} - -Transform::orientation_flags Transform::fromRotation(ISurfaceComposer::Rotation rotation) { - // Convert to surfaceflinger's internal rotation type. - switch (rotation) { - case ISurfaceComposer::eRotateNone: - return Transform::ROT_0; - case ISurfaceComposer::eRotate90: - return Transform::ROT_90; - case ISurfaceComposer::eRotate180: - return Transform::ROT_180; - case ISurfaceComposer::eRotate270: - return Transform::ROT_270; - default: - ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation); - return Transform::ROT_0; - } + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][0]), static_cast<double>(m[1][0]), + static_cast<double>(m[2][0])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][1]), static_cast<double>(m[1][1]), + static_cast<double>(m[2][1])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][2]), static_cast<double>(m[1][2]), + static_cast<double>(m[2][2])); } -// --------------------------------------------------------------------------- - -}; // namespace android +} // namespace ui +} // namespace android diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h new file mode 100644 index 0000000000..daf61928a4 --- /dev/null +++ b/libs/ui/include/ui/BufferHubBuffer.h @@ -0,0 +1,144 @@ +/* + * 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. + */ + +#ifndef ANDROID_BUFFER_HUB_BUFFER_H_ +#define ANDROID_BUFFER_HUB_BUFFER_H_ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/client.h> +#include <private/dvr/buffer_hub_defs.h> +#include <private/dvr/native_handle_wrapper.h> +#pragma clang diagnostic pop + +#include <ui/BufferHubMetadata.h> + +namespace android { + +class BufferHubClient : public pdx::Client { +public: + BufferHubClient(); + virtual ~BufferHubClient(); + explicit BufferHubClient(pdx::LocalChannelHandle mChannelHandle); + + bool IsValid() const; + pdx::LocalChannelHandle TakeChannelHandle(); + + using pdx::Client::Close; + using pdx::Client::event_fd; + using pdx::Client::GetChannel; + using pdx::Client::InvokeRemoteMethod; +}; + +class BufferHubBuffer { +public: + // Allocates a standalone BufferHubBuffer not associated with any producer consumer set. + static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height, + uint32_t layerCount, uint32_t format, + uint64_t usage, size_t mUserMetadataSize) { + return std::unique_ptr<BufferHubBuffer>( + new BufferHubBuffer(width, height, layerCount, format, usage, mUserMetadataSize)); + } + + // Imports the given channel handle to a BufferHubBuffer, taking ownership. + static std::unique_ptr<BufferHubBuffer> Import(pdx::LocalChannelHandle mChannelHandle) { + return std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(std::move(mChannelHandle))); + } + + BufferHubBuffer(const BufferHubBuffer&) = delete; + void operator=(const BufferHubBuffer&) = delete; + + // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in + // bufferhubd share the same buffer id. + int id() const { return mId; } + + const native_handle_t* DuplicateHandle() { return mBufferHandle.DuplicateHandle(); } + + // Returns the current value of MetadataHeader::buffer_state. + uint64_t buffer_state() { + return mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire); + } + + // A state mask which is unique to a buffer hub client among all its siblings sharing the same + // concrete graphic buffer. + uint64_t client_state_mask() const { return mClientStateMask; } + + size_t user_metadata_size() const { return mMetadata.user_metadata_size(); } + + // Returns true if the buffer holds an open PDX channels towards bufferhubd. + bool IsConnected() const { return mClient.IsValid(); } + + // Returns true if the buffer holds an valid native buffer handle that's availble for the client + // to read from and/or write into. + bool IsValid() const { return mBufferHandle.IsValid(); } + + // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for + // all possible bits). + pdx::Status<int> GetEventMask(int events) { + if (auto* channel = mClient.GetChannel()) { + return channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + // Polls the fd for |timeoutMs| milliseconds (-1 for infinity). + int Poll(int timeoutMs); + + // Creates a BufferHubBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + pdx::Status<pdx::LocalChannelHandle> Duplicate(); + +private: + BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, + uint64_t usage, size_t mUserMetadataSize); + + BufferHubBuffer(pdx::LocalChannelHandle mChannelHandle); + + int ImportGraphicBuffer(); + + // Global id for the buffer that is consistent across processes. + int mId = -1; + uint64_t mClientStateMask = 0; + + // Wrapps the gralloc buffer handle of this buffer. + dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle; + + // An ashmem-based metadata object. The same shared memory are mapped to the + // bufferhubd daemon and all buffer clients. + BufferHubMetadata mMetadata; + + // PDX backend. + BufferHubClient mClient; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_BUFFER_H_ diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h new file mode 100644 index 0000000000..94a9000a32 --- /dev/null +++ b/libs/ui/include/ui/BufferHubMetadata.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + +#ifndef ANDROID_BUFFER_HUB_METADATA_H_ +#define ANDROID_BUFFER_HUB_METADATA_H_ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub_defs.h> +#pragma clang diagnostic pop + +namespace android { + +class BufferHubMetadata { +public: + // Creates a new BufferHubMetadata backed by an ashmem region. + // + // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata + // shared memory region to be allocated is the size of canonical + // BufferHubDefs::MetadataHeader plus userMetadataSize. + static BufferHubMetadata Create(size_t userMetadataSize); + + // Imports an existing BufferHubMetadata from an ashmem FD. + // + // TODO(b/112338294): Refactor BufferHub to use Binder as its internal IPC backend instead of + // UDS. + // + // @param ashmemHandle Ashmem file handle representing an ashmem region. + static BufferHubMetadata Import(pdx::LocalHandle ashmemHandle); + + BufferHubMetadata() = default; + + BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); } + + ~BufferHubMetadata(); + + BufferHubMetadata& operator=(BufferHubMetadata&& other) { + if (this != &other) { + mUserMetadataSize = other.mUserMetadataSize; + other.mUserMetadataSize = 0; + + mAshmemHandle = std::move(other.mAshmemHandle); + + // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will + // automatically mummap() the shared memory. + mMetadataHeader = other.mMetadataHeader; + other.mMetadataHeader = nullptr; + } + return *this; + } + + // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem + // has been mapped into virtual address space. + bool IsValid() const { return mAshmemHandle.IsValid() && mMetadataHeader != nullptr; } + + size_t user_metadata_size() const { return mUserMetadataSize; } + size_t metadata_size() const { + return mUserMetadataSize + dvr::BufferHubDefs::kMetadataHeaderSize; + } + + const pdx::LocalHandle& ashmem_handle() const { return mAshmemHandle; } + dvr::BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; } + +private: + BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle, + dvr::BufferHubDefs::MetadataHeader* metadataHeader); + + BufferHubMetadata(const BufferHubMetadata&) = delete; + void operator=(const BufferHubMetadata&) = delete; + + size_t mUserMetadataSize = 0; + pdx::LocalHandle mAshmemHandle; + dvr::BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_METADATA_H_ diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h deleted file mode 100644 index f3c328d52b..0000000000 --- a/libs/ui/include/ui/DetachedBufferHandle.h +++ /dev/null @@ -1,52 +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. - */ - -#ifndef ANDROID_DETACHED_BUFFER_HUB_HANDLE_H -#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H - -#include <pdx/channel_handle.h> - -#include <memory> - -namespace android { - -// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be -// created. Current implementation assumes that the underlying transport is using libpdx (thus -// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder -// backend if ever needed. -class DetachedBufferHandle { -public: - static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) { - return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle))); - } - - // Accessors to get or take the internal pdx::LocalChannelHandle. - pdx::LocalChannelHandle& handle() { return mHandle; } - const pdx::LocalChannelHandle& handle() const { return mHandle; } - - // Returns whether the DetachedBufferHandle holds a BufferHub channel. - bool isValid() const { return mHandle.valid(); } - -private: - // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle. - explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {} - - pdx::LocalChannelHandle mHandle; -}; - -} // namespace android - -#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 94caf6b9d3..8976d2d584 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -35,6 +35,8 @@ struct DisplayInfo { bool secure{false}; nsecs_t appVsyncOffset{0}; nsecs_t presentationDeadline{0}; + uint32_t viewportW{0}; + uint32_t viewportH{0}; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h index 871fcf2dfe..a5a1fcbde7 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h @@ -113,11 +113,6 @@ public: void signalForTest(nsecs_t signalTime); - // Override new and delete since this needs 8-byte alignment, which - // is not guaranteed on x86. - static void* operator new(size_t nbytes) noexcept; - static void operator delete(void *p); - private: // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 6a7479a68a..4cd9a0b236 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h @@ -28,7 +28,7 @@ public: float getHeight() const { return bottom - top; } FloatRect intersect(const FloatRect& other) const { - return { + FloatRect intersection = { // Inline to avoid tromping on other min/max defines or adding a // dependency on STL (left > other.left) ? left : other.left, @@ -36,6 +36,10 @@ public: (right < other.right) ? right : other.right, (bottom < other.bottom) ? bottom : other.bottom }; + if (intersection.getWidth() < 0 || intersection.getHeight() < 0) { + return {0, 0, 0, 0}; + } + return intersection; } float left = 0.0f; diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index cc38982e64..e794462f60 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -34,7 +34,6 @@ namespace android { -class DetachedBufferHandle; class GraphicBufferMapper; // =========================================================================== @@ -191,11 +190,6 @@ public: status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); - // Sets and takes DetachedBuffer. Should only be called from BufferHub. - bool isDetachedBuffer() const; - status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer); - std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle(); - private: ~GraphicBuffer(); @@ -246,17 +240,6 @@ private: // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. uint32_t mGenerationNumber; - - // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a - // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub, - // there are three different modes: - // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be - // invalid. - // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be valid. - // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be invalid. - std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle; }; }; // namespace android diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h index 0fa819dce8..1d53ac8550 100644 --- a/libs/ui/include/ui/GraphicTypes.h +++ b/libs/ui/include/ui/GraphicTypes.h @@ -17,6 +17,7 @@ #pragma once #include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/common/1.2/types.h> #include <system/graphics.h> // android::ui::* in this header file will alias different types as @@ -25,10 +26,10 @@ namespace android { namespace ui { using android::hardware::graphics::common::V1_0::Hdr; -using android::hardware::graphics::common::V1_1::ColorMode; -using android::hardware::graphics::common::V1_1::Dataspace; using android::hardware::graphics::common::V1_1::PixelFormat; using android::hardware::graphics::common::V1_1::RenderIntent; +using android::hardware::graphics::common::V1_2::ColorMode; +using android::hardware::graphics::common::V1_2::Dataspace; } // namespace ui } // namespace android diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 778845295f..68b60fc2e2 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h @@ -25,6 +25,8 @@ #include <ui/Rect.h> #include <utils/Flattenable.h> +#include <android-base/macros.h> + namespace android { // --------------------------------------------------------------------------- @@ -93,11 +95,11 @@ public: Region& subtractSelf(const Region& rhs, int dx, int dy); // these translate rhs first - const Region translate(int dx, int dy) const; - const Region merge(const Region& rhs, int dx, int dy) const; - const Region mergeExclusive(const Region& rhs, int dx, int dy) const; - const Region intersect(const Region& rhs, int dx, int dy) const; - const Region subtract(const Region& rhs, int dx, int dy) const; + const Region translate(int dx, int dy) const WARN_UNUSED; + const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED; // convenience operators overloads inline const Region operator | (const Region& rhs) const; diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h new file mode 100644 index 0000000000..42dca75f6b --- /dev/null +++ b/libs/ui/include/ui/Transform.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2007 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_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware/hardware.h> +#include <math/vec2.h> +#include <math/vec3.h> +#include <ui/Point.h> +#include <ui/Rect.h> + +namespace android { + +class Region; + +namespace ui { + +class Transform { +public: + Transform(); + Transform(const Transform& other); + explicit Transform(uint32_t orientation); + ~Transform(); + + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = HAL_TRANSFORM_FLIP_H, + FLIP_V = HAL_TRANSFORM_FLIP_V, + ROT_90 = HAL_TRANSFORM_ROT_90, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80 + }; + + enum type_mask : uint32_t { + IDENTITY = 0, + TRANSLATE = 0x1, + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 + }; + + // query the transform + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + const vec3& operator [] (size_t i) const; // returns column i + float tx() const; + float ty() const; + + // modify the transform + void reset(); + void set(float tx, float ty); + void set(float a, float b, float c, float d); + status_t set(uint32_t flags, float w, float h); + + // transform data + Rect makeBounds(int w, int h) const; + vec2 transform(int x, int y) const; + Region transform(const Region& reg) const; + Rect transform(const Rect& bounds, + bool roundOutwards = false) const; + FloatRect transform(const FloatRect& bounds) const; + Transform& operator = (const Transform& other); + Transform operator * (const Transform& rhs) const; + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + + Transform inverse() const; + + // for debugging + void dump(const char* name) const; + +private: + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (size_t i) const { return v[i]; } + inline vec3& operator [] (size_t i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + uint32_t type() const; + static bool absIsOne(float f); + static bool isZero(float f); + + mat33 mMatrix; + mutable uint32_t mType; +}; + +} // namespace ui +} // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index aef6428cc8..1521e1d758 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -29,8 +29,27 @@ cc_test { } cc_test { - name: "GraphicBuffer_test", + name: "BufferHubBuffer_test", + header_libs: [ + "libbufferhub_headers", + "libdvr_headers" + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libhidlbase", + "libhwbinder", + "libpdx_default_transport", + "libui", + "libutils" + ], + srcs: ["BufferHubBuffer_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { + name: "BufferHubMetadata_test", + header_libs: ["libbufferhub_headers", "libdvr_headers"], shared_libs: ["libpdx_default_transport", "libui", "libutils"], - srcs: ["GraphicBuffer_test.cpp"], + srcs: ["BufferHubMetadata_test.cpp"], cflags: ["-Wall", "-Werror"], } diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp new file mode 100644 index 0000000000..a247e60103 --- /dev/null +++ b/libs/ui/tests/BufferHubBuffer_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferHubBufferTest" + +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <gtest/gtest.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/BufferHubBuffer.h> + +namespace android { + +namespace { + +const int kWidth = 640; +const int kHeight = 480; +const int kLayerCount = 1; +const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +const int kUsage = 0; +const size_t kUserMetadataSize = 0; + +} // namespace + +using dvr::BufferHubDefs::IsBufferGained; +using dvr::BufferHubDefs::kFirstClientBitMask; +using dvr::BufferHubDefs::kMetadataHeaderSize; +using frameworks::bufferhub::V1_0::IBufferHub; +using hardware::hidl_handle; +using hidl::base::V1_0::IBase; +using pdx::LocalChannelHandle; + +class BufferHubBufferTest : public ::testing::Test { + void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); } +}; + +TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) { + // Buffer Creation will fail: BLOB format requires height to be 1. + auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize); + + EXPECT_FALSE(b1->IsConnected()); + EXPECT_FALSE(b1->IsValid()); + + // Buffer Creation will fail: user metadata size too large. + auto b2 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + /*userMetadataSize=*/std::numeric_limits<size_t>::max()); + + EXPECT_FALSE(b2->IsConnected()); + EXPECT_FALSE(b2->IsValid()); + + // Buffer Creation will fail: user metadata size too large. + const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize; + auto b3 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + userMetadataSize); + + EXPECT_FALSE(b3->IsConnected()); + EXPECT_FALSE(b3->IsValid()); +} + +TEST_F(BufferHubBufferTest, CreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + EXPECT_NE(b1->id(), 0); +} + +TEST_F(BufferHubBufferTest, DuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + int id1 = b1->id(); + uint64_t bufferStateMask1 = b1->client_state_mask(); + EXPECT_NE(bufferStateMask1, 0ULL); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + + auto statusOrHandle = b1->Duplicate(); + EXPECT_TRUE(statusOrHandle); + + // The detached buffer should still be valid. + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + + // Gets the channel handle for the duplicated buffer. + LocalChannelHandle h2 = statusOrHandle.take(); + EXPECT_TRUE(h2.valid()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); + EXPECT_FALSE(h2.valid()); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + + int id2 = b2->id(); + uint64_t bufferStateMask2 = b2->client_state_mask(); + EXPECT_NE(bufferStateMask2, 0ULL); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(id1, id2); + // We use client_state_mask() to tell those two instances apart. + EXPECT_NE(bufferStateMask1, bufferStateMask2); + + // Both buffer instances should be in gained state. + EXPECT_TRUE(IsBufferGained(b1->buffer_state())); + EXPECT_TRUE(IsBufferGained(b2->buffer_state())); + + // TODO(b/112338294): rewrite test after migration + return; +} + +TEST_F(BufferHubBufferTest, ConnectHidlServer) { + sp<IBufferHub> bufferhub = IBufferHub::getService(); + ASSERT_NE(nullptr, bufferhub.get()); + + // TODO(b/116681016): Fill in real test once the interface gets implemented.. + hidl_handle handle; + sp<IBase> interface = bufferhub->importBuffer(handle); + EXPECT_EQ(nullptr, interface.get()); +} + +} // namespace android diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp new file mode 100644 index 0000000000..4209392afa --- /dev/null +++ b/libs/ui/tests/BufferHubMetadata_test.cpp @@ -0,0 +1,105 @@ +/* + * 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 <gtest/gtest.h> +#include <ui/BufferHubMetadata.h> + +using android::dvr::BufferHubDefs::IsBufferGained; + +namespace android { +namespace dvr { + +constexpr size_t kEmptyUserMetadataSize = 0; + +class BufferHubMetadataTest : public ::testing::Test {}; + +TEST_F(BufferHubMetadataTest, Create_UserMetdataSizeTooBig) { + BufferHubMetadata m1 = + BufferHubMetadata::Create(std::numeric_limits<uint32_t>::max()); + EXPECT_FALSE(m1.IsValid()); +} + +TEST_F(BufferHubMetadataTest, Create_Success) { + BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); +} + +TEST_F(BufferHubMetadataTest, Import_Success) { + BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + + pdx::LocalHandle h2 = m1.ashmem_handle().Duplicate(); + EXPECT_TRUE(h2.IsValid()); + + BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2)); + EXPECT_FALSE(h2.IsValid()); + EXPECT_TRUE(m1.IsValid()); + BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header(); + EXPECT_NE(mh1, nullptr); + + // TODO(b/111976433): Update this test once BufferHub state machine gets + // updated. In the old model, buffer starts in the gained state (i.e. + // valued 0). In the new model, buffer states in the released state. + EXPECT_TRUE(IsBufferGained(mh1->fence_state.load())); + + EXPECT_TRUE(m2.IsValid()); + BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header(); + EXPECT_NE(mh2, nullptr); + + // TODO(b/111976433): Update this test once BufferHub state machine gets + // updated. In the old model, buffer starts in the gained state (i.e. + // valued 0). In the new model, buffer states in the released state. + EXPECT_TRUE(IsBufferGained(mh2->fence_state.load())); +} + +TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) { + BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int)); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + EXPECT_TRUE(m1.ashmem_handle().IsValid()); + EXPECT_EQ(m1.user_metadata_size(), sizeof(int)); + + BufferHubMetadata m2 = std::move(m1); + + // After the move, the metadata header (a raw pointer) should be reset in the + // older buffer. + EXPECT_EQ(m1.metadata_header(), nullptr); + EXPECT_NE(m2.metadata_header(), nullptr); + + EXPECT_FALSE(m1.ashmem_handle().IsValid()); + EXPECT_TRUE(m2.ashmem_handle().IsValid()); + + EXPECT_EQ(m1.user_metadata_size(), 0U); + EXPECT_EQ(m2.user_metadata_size(), sizeof(int)); + + BufferHubMetadata m3{std::move(m2)}; + + // After the move, the metadata header (a raw pointer) should be reset in the + // older buffer. + EXPECT_EQ(m2.metadata_header(), nullptr); + EXPECT_NE(m3.metadata_header(), nullptr); + + EXPECT_FALSE(m2.ashmem_handle().IsValid()); + EXPECT_TRUE(m3.ashmem_handle().IsValid()); + + EXPECT_EQ(m2.user_metadata_size(), 0U); + EXPECT_EQ(m3.user_metadata_size(), sizeof(int)); +} + +} // namespace dvr +} // namespace android diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp deleted file mode 100644 index eb679ac236..0000000000 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "GraphicBufferTest" - -#include <ui/DetachedBufferHandle.h> -#include <ui/GraphicBuffer.h> - -#include <gtest/gtest.h> - -namespace android { - -namespace { - -constexpr uint32_t kTestWidth = 1024; -constexpr uint32_t kTestHeight = 1; -constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB; -constexpr uint32_t kTestLayerCount = 1; -constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; - -} // namespace - -class GraphicBufferTest : public testing::Test {}; - -TEST_F(GraphicBufferTest, DetachedBuffer) { - sp<GraphicBuffer> buffer( - new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); - - // Currently a newly allocated GraphicBuffer is in legacy mode, i.e. not associated with - // BufferHub. But this may change in the future. - EXPECT_FALSE(buffer->isDetachedBuffer()); - - pdx::LocalChannelHandle channel{nullptr, 1234}; - EXPECT_TRUE(channel.valid()); - - std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel)); - EXPECT_FALSE(channel.valid()); - EXPECT_TRUE(handle->isValid()); - EXPECT_TRUE(handle->handle().valid()); - - buffer->setDetachedBufferHandle(std::move(handle)); - EXPECT_TRUE(handle == nullptr); - EXPECT_TRUE(buffer->isDetachedBuffer()); - - handle = buffer->takeDetachedBufferHandle(); - EXPECT_TRUE(handle != nullptr); - EXPECT_TRUE(handle->isValid()); - EXPECT_FALSE(buffer->isDetachedBuffer()); -} - -} // namespace android diff --git a/libs/vr/CleanSpec.mk b/libs/vr/CleanSpec.mk new file mode 100644 index 0000000000..a17c9b24e5 --- /dev/null +++ b/libs/vr/CleanSpec.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2012 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libdvr.so" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates) diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 69b6422d1d..43ac79ee26 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -12,15 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_library_headers { + name: "libbufferhub_headers", + export_include_dirs: ["include"], + vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor. +} + sourceFiles = [ - "buffer_hub_client.cpp", + "buffer_hub_base.cpp", "buffer_hub_rpc.cpp", - "detached_buffer.cpp", + "consumer_buffer.cpp", + "buffer_client_impl.cpp", "ion_buffer.cpp", -] - -localIncludeFiles = [ - "include", + "producer_buffer.cpp", ] sharedLibraries = [ @@ -36,6 +40,7 @@ sharedLibraries = [ ] headerLibraries = [ + "libbufferhub_headers", "libdvr_headers", "libnativebase_headers", ] @@ -49,13 +54,16 @@ cc_library { "-Wall", "-Werror", ], - export_include_dirs: localIncludeFiles, shared_libs: sharedLibraries, header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ + "libbufferhub_headers", "libnativebase_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_test { @@ -64,5 +72,7 @@ cc_test { shared_libs: sharedLibraries, header_libs: headerLibraries, name: "buffer_hub-test", -} + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libbufferhub/buffer_client_impl.cpp b/libs/vr/libbufferhub/buffer_client_impl.cpp new file mode 100644 index 0000000000..30cbb9fdd7 --- /dev/null +++ b/libs/vr/libbufferhub/buffer_client_impl.cpp @@ -0,0 +1,87 @@ +#include <log/log.h> +#include <private/dvr/IBufferClient.h> + +namespace android { +namespace dvr { + +class BpBufferClient : public BpInterface<IBufferClient> { + public: + explicit BpBufferClient(const sp<IBinder>& impl) + : BpInterface<IBufferClient>(impl) {} + + bool isValid() override; + + status_t duplicate(uint64_t* outToken) override; +}; + +IMPLEMENT_META_INTERFACE(BufferClient, "android.dvr.IBufferClient"); + +// Transaction code +enum { + IS_VALID = IBinder::FIRST_CALL_TRANSACTION, + DUPLICATE, +}; + +bool BpBufferClient::isValid() { + Parcel data, reply; + status_t ret = + data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor()); + if (ret != NO_ERROR) { + ALOGE("BpBufferClient::isValid: failed to write into parcel; errno=%d", + ret); + return false; + } + + ret = remote()->transact(IS_VALID, data, &reply); + if (ret == NO_ERROR) { + return reply.readBool(); + } else { + ALOGE("BpBufferClient::isValid: failed to transact; errno=%d", ret); + return false; + } +} + +status_t BpBufferClient::duplicate(uint64_t* outToken) { + Parcel data, reply; + status_t ret = + data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor()); + if (ret != NO_ERROR) { + ALOGE("BpBufferClient::duplicate: failed to write into parcel; errno=%d", + ret); + return ret; + } + + ret = remote()->transact(DUPLICATE, data, &reply); + if (ret == NO_ERROR) { + *outToken = reply.readUint64(); + return NO_ERROR; + } else { + ALOGE("BpBufferClient::duplicate: failed to transact; errno=%d", ret); + return ret; + } +} + +status_t BnBufferClient::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + switch (code) { + case IS_VALID: { + CHECK_INTERFACE(IBufferClient, data, reply); + return reply->writeBool(isValid()); + } + case DUPLICATE: { + CHECK_INTERFACE(IBufferClient, data, reply); + uint64_t token = 0; + status_t ret = duplicate(&token); + if (ret != NO_ERROR) { + return ret; + } + return reply->writeUint64(token); + } + default: + // Should not reach except binder defined transactions such as dumpsys + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace dvr +} // namespace android
\ No newline at end of file diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp index e24739845d..73ca69bfec 100644 --- a/libs/vr/libbufferhub/buffer_hub-test.cpp +++ b/libs/vr/libbufferhub/buffer_hub-test.cpp @@ -2,10 +2,9 @@ #include <poll.h> #include <private/dvr/buffer_hub_client.h> #include <private/dvr/bufferhub_rpc.h> -#include <private/dvr/detached_buffer.h> #include <sys/epoll.h> #include <sys/eventfd.h> -#include <ui/DetachedBufferHandle.h> +#include <ui/BufferHubBuffer.h> #include <mutex> #include <thread> @@ -19,18 +18,18 @@ return result; \ })() +using android::BufferHubBuffer; using android::GraphicBuffer; using android::sp; -using android::dvr::BufferConsumer; -using android::dvr::BufferProducer; -using android::dvr::DetachedBuffer; +using android::dvr::ConsumerBuffer; +using android::dvr::ProducerBuffer; using android::dvr::BufferHubDefs::IsBufferAcquired; using android::dvr::BufferHubDefs::IsBufferGained; using android::dvr::BufferHubDefs::IsBufferPosted; using android::dvr::BufferHubDefs::IsBufferReleased; using android::dvr::BufferHubDefs::kConsumerStateMask; +using android::dvr::BufferHubDefs::kFirstClientBitMask; using android::dvr::BufferHubDefs::kMetadataHeaderSize; -using android::dvr::BufferHubDefs::kProducerStateBit; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; @@ -41,60 +40,59 @@ const int kLayerCount = 1; const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; const int kUsage = 0; const size_t kUserMetadataSize = 0; -const uint64_t kContext = 42; -const size_t kMaxConsumerCount = 63; +// Maximum number of consumers for the buffer that only has one producer in the +// test. +const size_t kMaxConsumerCount = + android::dvr::BufferHubDefs::kMaxNumberOfClients - 1; const int kPollTimeoutMs = 100; using LibBufferHubTest = ::testing::Test; TEST_F(LibBufferHubTest, TestBasicUsage) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); // Check that consumers can spawn other consumers. - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(c->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(c->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); // Producer state mask is unique, i.e. 1. - EXPECT_EQ(p->buffer_state_bit(), kProducerStateBit); + EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask); // Consumer state mask cannot have producer bit on. - EXPECT_EQ(c->buffer_state_bit() & kProducerStateBit, 0U); + EXPECT_EQ(c->client_state_mask() & kFirstClientBitMask, 0U); // Consumer state mask must be a single, i.e. power of 2. - EXPECT_NE(c->buffer_state_bit(), 0U); - EXPECT_EQ(c->buffer_state_bit() & (c->buffer_state_bit() - 1), 0U); + EXPECT_NE(c->client_state_mask(), 0U); + EXPECT_EQ(c->client_state_mask() & (c->client_state_mask() - 1), 0U); // Consumer state mask cannot have producer bit on. - EXPECT_EQ(c2->buffer_state_bit() & kProducerStateBit, 0U); + EXPECT_EQ(c2->client_state_mask() & kFirstClientBitMask, 0U); // Consumer state mask must be a single, i.e. power of 2. - EXPECT_NE(c2->buffer_state_bit(), 0U); - EXPECT_EQ(c2->buffer_state_bit() & (c2->buffer_state_bit() - 1), 0U); + EXPECT_NE(c2->client_state_mask(), 0U); + EXPECT_EQ(c2->client_state_mask() & (c2->client_state_mask() - 1), 0U); // Each consumer should have unique bit. - EXPECT_EQ(c->buffer_state_bit() & c2->buffer_state_bit(), 0U); + EXPECT_EQ(c->client_state_mask() & c2->client_state_mask(), 0U); // Initial state: producer not available, consumers not available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(0, p->Post(LocalHandle())); // New state: producer not available, consumers available. EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - uint64_t context; LocalHandle fence; - EXPECT_EQ(0, c->Acquire(&fence, &context)); - EXPECT_EQ(kContext, context); + EXPECT_EQ(0, c->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, c2->Acquire(&fence, &context)); - EXPECT_EQ(kContext, context); + EXPECT_EQ(0, c2->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); @@ -110,11 +108,11 @@ TEST_F(LibBufferHubTest, TestBasicUsage) { } TEST_F(LibBufferHubTest, TestEpoll) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle epoll_fd{epoll_create1(EPOLL_CLOEXEC)}; @@ -147,7 +145,7 @@ TEST_F(LibBufferHubTest, TestEpoll) { ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0)); // Post the producer and check for consumer signal. - EXPECT_EQ(0, p->Post({}, kContext)); + EXPECT_EQ(0, p->Post({})); ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(), kPollTimeoutMs)); ASSERT_TRUE(events[0].events & EPOLLIN); @@ -177,21 +175,21 @@ TEST_F(LibBufferHubTest, TestEpoll) { } TEST_F(LibBufferHubTest, TestStateMask) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); // It's ok to create up to kMaxConsumerCount consumer buffers. - uint64_t buffer_state_bits = p->buffer_state_bit(); - std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs; + uint64_t client_state_masks = p->client_state_mask(); + std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs; for (size_t i = 0; i < kMaxConsumerCount; i++) { - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // Expect all buffers have unique state mask. - EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U); - buffer_state_bits |= cs[i]->buffer_state_bit(); + EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); + client_state_masks |= cs[i]->client_state_mask(); } - EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask); // The 64th creation will fail with out-of-memory error. auto state = p->CreateConsumer(); @@ -199,49 +197,48 @@ TEST_F(LibBufferHubTest, TestStateMask) { // Release any consumer should allow us to re-create. for (size_t i = 0; i < kMaxConsumerCount; i++) { - buffer_state_bits &= ~cs[i]->buffer_state_bit(); + client_state_masks &= ~cs[i]->client_state_mask(); cs[i] = nullptr; - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); // The released state mask will be reused. - EXPECT_EQ(buffer_state_bits & cs[i]->buffer_state_bit(), 0U); - buffer_state_bits |= cs[i]->buffer_state_bit(); - EXPECT_EQ(buffer_state_bits, kProducerStateBit | kConsumerStateMask); + EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U); + client_state_masks |= cs[i]->client_state_mask(); + EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask); } } TEST_F(LibBufferHubTest, TestStateTransitions) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - uint64_t context; LocalHandle fence; // The producer buffer starts in gained state. // Acquire, release, and gain in gained state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EALREADY, p->Gain(&fence)); // Post in gained state should succeed. - EXPECT_EQ(0, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(0, p->Post(LocalHandle())); // Post, release, and gain in posted state should fail. - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Acquire in posted state should succeed. - EXPECT_LE(0, c->Acquire(&fence, &context)); + EXPECT_LE(0, c->Acquire(&fence)); // Acquire, post, and gain in acquired state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); EXPECT_EQ(-EBUSY, p->Gain(&fence)); // Release in acquired state should succeed. @@ -250,24 +247,24 @@ TEST_F(LibBufferHubTest, TestStateTransitions) { // Release, acquire, and post in released state should fail. EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); - EXPECT_EQ(-EBUSY, p->Post(LocalHandle(), kContext)); + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); + EXPECT_EQ(-EBUSY, p->Post(LocalHandle())); // Gain in released state should succeed. EXPECT_EQ(0, p->Gain(&fence)); // Acquire, release, and gain in gained state should fail. - EXPECT_EQ(-EBUSY, c->Acquire(&fence, &context)); + EXPECT_EQ(-EBUSY, c->Acquire(&fence)); EXPECT_EQ(-EBUSY, c->Release(LocalHandle())); EXPECT_EQ(-EALREADY, p->Gain(&fence)); } TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); DvrNativeBufferMetadata metadata; @@ -333,8 +330,38 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { EXPECT_FALSE(invalid_fence.IsValid()); } +TEST_F(LibBufferHubTest, TestGainPostedBuffer) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + + // The producer buffer starts in gained state. Post the buffer. + ASSERT_EQ(0, p->Post(LocalHandle())); + + // Gain in posted state should only succeed with gain_posted_buffer = true. + LocalHandle invalid_fence; + EXPECT_EQ(-EBUSY, p->Gain(&invalid_fence, false)); + EXPECT_EQ(0, p->Gain(&invalid_fence, true)); +} + +TEST_F(LibBufferHubTest, TestGainPostedBufferAsync) { + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( + kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); + ASSERT_TRUE(p.get() != nullptr); + + // The producer buffer starts in gained state. Post the buffer. + ASSERT_EQ(0, p->Post(LocalHandle())); + + // GainAsync in posted state should only succeed with gain_posted_buffer + // equals true. + DvrNativeBufferMetadata metadata; + LocalHandle invalid_fence; + EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence, false)); + EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true)); +} + TEST_F(LibBufferHubTest, TestZeroConsumer) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); @@ -350,21 +377,21 @@ TEST_F(LibBufferHubTest, TestZeroConsumer) { EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); // A new consumer should still be able to acquire the buffer immediately. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); } TEST_F(LibBufferHubTest, TestMaxConsumers) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs; + std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs; for (size_t i = 0; i < kMaxConsumerCount; i++) { - cs[i] = BufferConsumer::Import(p->CreateConsumer()); + cs[i] = ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(cs[i].get() != nullptr); EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state())); } @@ -377,7 +404,7 @@ TEST_F(LibBufferHubTest, TestMaxConsumers) { EXPECT_TRUE(IsBufferPosted(p->buffer_state())); for (size_t i = 0; i < kMaxConsumerCount; i++) { EXPECT_TRUE( - IsBufferPosted(cs[i]->buffer_state(), cs[i]->buffer_state_bit())); + IsBufferPosted(cs[i]->buffer_state(), cs[i]->client_state_mask())); EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs))); EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence)); EXPECT_TRUE(IsBufferAcquired(p->buffer_state())); @@ -400,13 +427,13 @@ TEST_F(LibBufferHubTest, TestMaxConsumers) { } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); EXPECT_TRUE(IsBufferGained(p->buffer_state())); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_TRUE(IsBufferGained(c->buffer_state())); @@ -422,7 +449,7 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) { } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); EXPECT_TRUE(IsBufferGained(p->buffer_state())); @@ -435,8 +462,8 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) { EXPECT_TRUE(IsBufferPosted(p->buffer_state())); // Newly created consumer should be automatically sigalled. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); EXPECT_TRUE(IsBufferPosted(c->buffer_state())); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); @@ -444,12 +471,12 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) { } TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c1 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); DvrNativeBufferMetadata metadata; @@ -470,8 +497,8 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { // Create another consumer immediately after the release, should not make the // buffer un-released. - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); EXPECT_TRUE(IsBufferReleased(p->buffer_state())); @@ -484,23 +511,20 @@ TEST_F(LibBufferHubTest, TestWithCustomMetadata) { int64_t field1; int64_t field2; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - Metadata m = {1, 3}; - EXPECT_EQ(0, p->Post(LocalHandle(), m)); + EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata))); EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); - LocalHandle fence; Metadata m2 = {}; - EXPECT_EQ(0, c->Acquire(&fence, &m2)); + EXPECT_EQ(0, c->Acquire(&fence, &m2, sizeof(m2))); EXPECT_EQ(m.field1, m2.field1); EXPECT_EQ(m.field2, m2.field2); - EXPECT_EQ(0, c->Release(LocalHandle())); EXPECT_LT(0, RETRY_EINTR(p->Poll(0))); } @@ -515,23 +539,22 @@ TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { int64_t field2; int64_t field3; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); // It is illegal to post metadata larger than originally requested during // buffer allocation. OverSizedMetadata evil_meta = {}; - EXPECT_NE(0, p->Post(LocalHandle(), evil_meta)); + EXPECT_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata))); EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); // It is ok to post metadata smaller than originally requested during // buffer allocation. - int64_t sequence = 42; - EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); + EXPECT_EQ(0, p->Post(LocalHandle())); } TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { @@ -544,15 +567,15 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { int64_t field2; int64_t field3; }; - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(Metadata)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); Metadata m = {1, 3}; - EXPECT_EQ(0, p->Post(LocalHandle(), m)); + EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m))); LocalHandle fence; int64_t sequence; @@ -560,53 +583,53 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { // It is illegal to acquire metadata larger than originally requested during // buffer allocation. - EXPECT_NE(0, c->Acquire(&fence, &e)); + EXPECT_NE(0, c->Acquire(&fence, &e, sizeof(e))); // It is ok to acquire metadata smaller than originally requested during // buffer allocation. - EXPECT_EQ(0, c->Acquire(&fence, &sequence)); + EXPECT_EQ(0, c->Acquire(&fence, &sequence, sizeof(sequence))); EXPECT_EQ(m.field1, sequence); } TEST_F(LibBufferHubTest, TestAcquireWithNoMeta) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); int64_t sequence = 3; - EXPECT_EQ(0, p->Post(LocalHandle(), sequence)); + EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); LocalHandle fence; EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestWithNoMeta) { - std::unique_ptr<BufferProducer> p = - BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + std::unique_ptr<ProducerBuffer> p = + ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); LocalHandle fence; - EXPECT_EQ(0, p->Post<void>(LocalHandle())); + EXPECT_EQ(0, p->Post(LocalHandle())); EXPECT_EQ(0, c->Acquire(&fence)); } TEST_F(LibBufferHubTest, TestFailureToPostMetaFromABufferWithoutMeta) { - std::unique_ptr<BufferProducer> p = - BufferProducer::Create(kWidth, kHeight, kFormat, kUsage); + std::unique_ptr<ProducerBuffer> p = + ProducerBuffer::Create(kWidth, kHeight, kFormat, kUsage); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); int64_t sequence = 3; - EXPECT_NE(0, p->Post(LocalHandle(), sequence)); + EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence))); } namespace { @@ -619,11 +642,11 @@ int PollFd(int fd, int timeout_ms) { } // namespace TEST_F(LibBufferHubTest, TestAcquireFence) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, /*metadata_size=*/0); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); DvrNativeBufferMetadata meta; @@ -680,13 +703,13 @@ TEST_F(LibBufferHubTest, TestAcquireFence) { } TEST_F(LibBufferHubTest, TestOrphanedAcquire) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p.get() != nullptr); - std::unique_ptr<BufferConsumer> c1 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c1 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c1.get() != nullptr); - const uint64_t consumer_state_bit1 = c1->buffer_state_bit(); + const uint64_t client_state_mask1 = c1->client_state_mask(); DvrNativeBufferMetadata meta; EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle())); @@ -699,11 +722,11 @@ TEST_F(LibBufferHubTest, TestOrphanedAcquire) { c1 = nullptr; EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - std::unique_ptr<BufferConsumer> c2 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c2 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c2.get() != nullptr); - const uint64_t consumer_state_bit2 = c2->buffer_state_bit(); - EXPECT_NE(consumer_state_bit1, consumer_state_bit2); + const uint64_t client_state_mask2 = c2->client_state_mask(); + EXPECT_NE(client_state_mask1, client_state_mask2); // The new consumer is available for acquire. EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); @@ -715,11 +738,11 @@ TEST_F(LibBufferHubTest, TestOrphanedAcquire) { EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); // But if another consumer is created in released state. - std::unique_ptr<BufferConsumer> c3 = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c3 = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c3.get() != nullptr); - const uint64_t consumer_state_bit3 = c3->buffer_state_bit(); - EXPECT_NE(consumer_state_bit2, consumer_state_bit3); + const uint64_t client_state_mask3 = c3->client_state_mask(); + EXPECT_NE(client_state_mask2, client_state_mask3); // The consumer buffer is not acquirable. EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs))); EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence)); @@ -729,10 +752,13 @@ TEST_F(LibBufferHubTest, TestOrphanedAcquire) { } TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { - std::unique_ptr<BufferProducer> p = BufferProducer::Create( + // TODO(b/112338294) rewrite test after migration + return; + + std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(p.get() != nullptr); ASSERT_TRUE(c.get() != nullptr); @@ -789,125 +815,56 @@ TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { // is gone. EXPECT_EQ(s3.error(), EPIPE); - // Detached buffer handle can be use to construct a new DetachedBuffer object. - auto d = DetachedBuffer::Import(std::move(handle)); + // Detached buffer handle can be use to construct a new BufferHubBuffer + // object. + auto d = BufferHubBuffer::Import(std::move(handle)); EXPECT_FALSE(handle.valid()); EXPECT_TRUE(d->IsConnected()); EXPECT_TRUE(d->IsValid()); - ASSERT_TRUE(d->buffer() != nullptr); - EXPECT_EQ(d->buffer()->initCheck(), 0); EXPECT_EQ(d->id(), p_id); } -TEST_F(LibBufferHubTest, TestCreateDetachedBufferFails) { +TEST_F(LibBufferHubTest, TestCreateBufferHubBufferFails) { // Buffer Creation will fail: BLOB format requires height to be 1. - auto b1 = DetachedBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount, - /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, - kUserMetadataSize); + auto b1 = BufferHubBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, + kUserMetadataSize); EXPECT_FALSE(b1->IsConnected()); EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(b1->buffer() == nullptr); // Buffer Creation will fail: user metadata size too large. - auto b2 = DetachedBuffer::Create( + auto b2 = BufferHubBuffer::Create( kWidth, kHeight, kLayerCount, kFormat, kUsage, /*user_metadata_size=*/std::numeric_limits<size_t>::max()); EXPECT_FALSE(b2->IsConnected()); EXPECT_FALSE(b2->IsValid()); - EXPECT_TRUE(b2->buffer() == nullptr); // Buffer Creation will fail: user metadata size too large. - auto b3 = DetachedBuffer::Create( + auto b3 = BufferHubBuffer::Create( kWidth, kHeight, kLayerCount, kFormat, kUsage, /*user_metadata_size=*/std::numeric_limits<size_t>::max() - kMetadataHeaderSize); EXPECT_FALSE(b3->IsConnected()); EXPECT_FALSE(b3->IsValid()); - EXPECT_TRUE(b3->buffer() == nullptr); } -TEST_F(LibBufferHubTest, TestCreateDetachedBuffer) { - auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, - kUsage, kUserMetadataSize); - int b1_id = b1->id(); - +TEST_F(LibBufferHubTest, TestCreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); EXPECT_TRUE(b1->IsConnected()); EXPECT_TRUE(b1->IsValid()); - ASSERT_TRUE(b1->buffer() != nullptr); EXPECT_NE(b1->id(), 0); - EXPECT_EQ(b1->buffer()->initCheck(), 0); - EXPECT_FALSE(b1->buffer()->isDetachedBuffer()); - - // Takes a standalone GraphicBuffer which still holds on an - // PDX::LocalChannelHandle towards BufferHub. - sp<GraphicBuffer> g1 = b1->TakeGraphicBuffer(); - ASSERT_TRUE(g1 != nullptr); - EXPECT_TRUE(g1->isDetachedBuffer()); - - EXPECT_FALSE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(b1->buffer() == nullptr); - - sp<GraphicBuffer> g2 = b1->TakeGraphicBuffer(); - ASSERT_TRUE(g2 == nullptr); - - auto h1 = g1->takeDetachedBufferHandle(); - ASSERT_TRUE(h1 != nullptr); - ASSERT_TRUE(h1->isValid()); - EXPECT_FALSE(g1->isDetachedBuffer()); - - auto b2 = DetachedBuffer::Import(std::move(h1->handle())); - ASSERT_FALSE(h1->isValid()); - EXPECT_TRUE(b2->IsConnected()); - EXPECT_TRUE(b2->IsValid()); - - ASSERT_TRUE(b2->buffer() != nullptr); - EXPECT_EQ(b2->buffer()->initCheck(), 0); - - // The newly created DetachedBuffer should share the original buffer_id. - EXPECT_EQ(b2->id(), b1_id); - EXPECT_FALSE(b2->buffer()->isDetachedBuffer()); } -TEST_F(LibBufferHubTest, TestPromoteDetachedBuffer) { - auto b1 = DetachedBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, - kUsage, kUserMetadataSize); - int b1_id = b1->id(); - EXPECT_TRUE(b1->IsValid()); - - auto status_or_handle = b1->Promote(); - EXPECT_TRUE(status_or_handle); - - // The detached buffer should have hangup. - EXPECT_GT(RETRY_EINTR(b1->Poll(kPollTimeoutMs)), 0); - auto status_or_int = b1->GetEventMask(POLLHUP); - EXPECT_TRUE(status_or_int.ok()); - EXPECT_EQ(status_or_int.get(), POLLHUP); - - // The buffer client is still considered as connected but invalid. - EXPECT_TRUE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - - // Gets the channel handle for the producer. - LocalChannelHandle h1 = status_or_handle.take(); - EXPECT_TRUE(h1.valid()); - - std::unique_ptr<BufferProducer> p1 = BufferProducer::Import(std::move(h1)); - EXPECT_FALSE(h1.valid()); - ASSERT_TRUE(p1 != nullptr); - int p1_id = p1->id(); +TEST_F(LibBufferHubTest, TestDetach) { + // TODO(b/112338294) rewrite test after migration + return; - // A newly promoted ProducerBuffer should inherit the same buffer id. - EXPECT_EQ(b1_id, p1_id); - EXPECT_TRUE(IsBufferGained(p1->buffer_state())); -} - -TEST_F(LibBufferHubTest, TestDetachThenPromote) { - std::unique_ptr<BufferProducer> p1 = BufferProducer::Create( + std::unique_ptr<ProducerBuffer> p1 = ProducerBuffer::Create( kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t)); ASSERT_TRUE(p1.get() != nullptr); int p1_id = p1->id(); @@ -918,30 +875,53 @@ TEST_F(LibBufferHubTest, TestDetachThenPromote) { LocalChannelHandle h1 = status_or_handle.take(); EXPECT_TRUE(h1.valid()); - // Detached buffer handle can be use to construct a new DetachedBuffer object. - auto b1 = DetachedBuffer::Import(std::move(h1)); + // Detached buffer handle can be use to construct a new BufferHubBuffer + // object. + auto b1 = BufferHubBuffer::Import(std::move(h1)); EXPECT_FALSE(h1.valid()); EXPECT_TRUE(b1->IsValid()); int b1_id = b1->id(); EXPECT_EQ(b1_id, p1_id); +} + +TEST_F(LibBufferHubTest, TestDuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); + int b1_id = b1->id(); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b1->client_state_mask(), 0ULL); + + auto status_or_handle = b1->Duplicate(); + EXPECT_TRUE(status_or_handle); - // Promote the detached buffer. - status_or_handle = b1->Promote(); - // The buffer client is still considered as connected but invalid. + // The detached buffer should still be valid. EXPECT_TRUE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(status_or_handle.ok()); + EXPECT_TRUE(b1->IsValid()); - // Gets the channel handle for the producer. + // Gets the channel handle for the duplicated buffer. LocalChannelHandle h2 = status_or_handle.take(); EXPECT_TRUE(h2.valid()); - std::unique_ptr<BufferProducer> p2 = BufferProducer::Import(std::move(h2)); + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); EXPECT_FALSE(h2.valid()); - ASSERT_TRUE(p2 != nullptr); - int p2_id = p2->id(); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b2->client_state_mask(), 0ULL); + + int b2_id = b2->id(); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(b1_id, b2_id); + // We use client_state_mask() to tell those two instances apart. + EXPECT_NE(b1->client_state_mask(), b2->client_state_mask()); + + // Both buffer instances should be in gained state. + EXPECT_TRUE(IsBufferGained(b1->buffer_state())); + EXPECT_TRUE(IsBufferGained(b2->buffer_state())); - // A newly promoted ProducerBuffer should inherit the same buffer id. - EXPECT_EQ(b1_id, p2_id); - EXPECT_TRUE(IsBufferGained(p2->buffer_state())); + // TODO(b/112338294) rewrite test after migration + return; } diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp new file mode 100644 index 0000000000..68cc766ad8 --- /dev/null +++ b/libs/vr/libbufferhub/buffer_hub_base.cpp @@ -0,0 +1,227 @@ +#include <poll.h> +#include <sys/epoll.h> + +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <private/dvr/buffer_hub_base.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; +using android::pdx::default_transport::ClientChannel; +using android::pdx::default_transport::ClientChannelFactory; + +namespace android { +namespace dvr { + +BufferHubBase::BufferHubBase(LocalChannelHandle channel_handle) + : Client{pdx::default_transport::ClientChannel::Create( + std::move(channel_handle))}, + id_(-1), + cid_(-1) {} +BufferHubBase::BufferHubBase(const std::string& endpoint_path) + : Client{pdx::default_transport::ClientChannelFactory::Create( + endpoint_path)}, + id_(-1), + cid_(-1) {} + +BufferHubBase::~BufferHubBase() { + if (metadata_header_ != nullptr) { + metadata_buffer_.Unlock(); + } +} + +Status<LocalChannelHandle> BufferHubBase::CreateConsumer() { + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::NewConsumer>(); + ALOGE_IF(!status, + "BufferHub::CreateConsumer: Failed to create consumer channel: %s", + status.GetErrorMessage().c_str()); + return status; +} + +int BufferHubBase::ImportBuffer() { + ATRACE_NAME("BufferHubBase::ImportBuffer"); + + Status<BufferDescription<LocalHandle>> status = + InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); + if (!status) { + ALOGE("BufferHubBase::ImportBuffer: Failed to get buffer: %s", + status.GetErrorMessage().c_str()); + return -status.error(); + } else if (status.get().id() < 0) { + ALOGE("BufferHubBase::ImportBuffer: Received an invalid id!"); + return -EIO; + } + + auto buffer_desc = status.take(); + + // Stash the buffer id to replace the value in id_. + const int new_id = buffer_desc.id(); + + // Import the buffer. + IonBuffer ion_buffer; + ALOGD_IF(TRACE, "BufferHubBase::ImportBuffer: id=%d.", buffer_desc.id()); + + if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) + return ret; + + // Import the metadata. + IonBuffer metadata_buffer; + if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { + ALOGE("Failed to import metadata buffer, error=%d", ret); + return ret; + } + size_t metadata_buf_size = metadata_buffer.width(); + if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("BufferHubBase::ImportBuffer: metadata buffer too small: %zu", + metadata_buf_size); + return -ENOMEM; + } + + // If all imports succee, replace the previous buffer and id. + buffer_ = std::move(ion_buffer); + metadata_buffer_ = std::move(metadata_buffer); + metadata_buf_size_ = metadata_buf_size; + user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; + + void* metadata_ptr = nullptr; + if (const int ret = + metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, + /*y=*/0, metadata_buf_size_, + /*height=*/1, &metadata_ptr)) { + ALOGE("BufferHubBase::ImportBuffer: Failed to lock metadata."); + return ret; + } + + // Set up shared fences. + shared_acquire_fence_ = buffer_desc.take_acquire_fence(); + shared_release_fence_ = buffer_desc.take_release_fence(); + if (!shared_acquire_fence_ || !shared_release_fence_) { + ALOGE("BufferHubBase::ImportBuffer: Failed to import shared fences."); + return -EIO; + } + + metadata_header_ = + reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); + if (user_metadata_size_) { + user_metadata_ptr_ = + reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) + + BufferHubDefs::kMetadataHeaderSize); + } else { + user_metadata_ptr_ = nullptr; + } + + id_ = new_id; + cid_ = buffer_desc.buffer_cid(); + client_state_mask_ = buffer_desc.client_state_mask(); + + // Note that here the buffer_state, fence_state and active_clients_bit_mask + // are mapped from shared memory as an atomic object. The std::atomic's + // constructor will not be called so that the original value stored in the + // memory region will be preserved. + buffer_state_ = &metadata_header_->buffer_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", + id(), buffer_state_->load(std::memory_order_acquire)); + fence_state_ = &metadata_header_->fence_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", id(), + fence_state_->load(std::memory_order_acquire)); + active_clients_bit_mask_ = &metadata_header_->active_clients_bit_mask; + ALOGD_IF( + TRACE, + "BufferHubBase::ImportBuffer: id=%d, active_clients_bit_mask=%" PRIx64 + ".", + id(), active_clients_bit_mask_->load(std::memory_order_acquire)); + + return 0; +} + +int BufferHubBase::CheckMetadata(size_t user_metadata_size) const { + if (user_metadata_size && !user_metadata_ptr_) { + ALOGE("BufferHubBase::CheckMetadata: doesn't support custom metadata."); + return -EINVAL; + } + if (user_metadata_size > user_metadata_size_) { + ALOGE("BufferHubBase::CheckMetadata: too big: %zu, maximum: %zu.", + user_metadata_size, user_metadata_size_); + return -E2BIG; + } + return 0; +} + +int BufferHubBase::UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence) { + if (pending_fence_fd_.Get() != new_fence.Get()) { + // First, replace the old fd if there was already one. Skipping if the new + // one is the same as the old. + if (pending_fence_fd_.IsValid()) { + const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, + pending_fence_fd_.Get(), nullptr); + ALOGW_IF(ret, + "BufferHubBase::UpdateSharedFence: failed to remove old fence " + "fd from epoll set, error: %s.", + strerror(errno)); + } + + if (new_fence.IsValid()) { + // If ready fence is valid, we put that into the epoll set. + epoll_event event; + event.events = EPOLLIN; + event.data.u64 = client_state_mask(); + pending_fence_fd_ = new_fence.Duplicate(); + if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), + &event) < 0) { + const int error = errno; + ALOGE( + "BufferHubBase::UpdateSharedFence: failed to add new fence fd " + "into epoll set, error: %s.", + strerror(error)); + return -error; + } + // Set bit in fence state to indicate that there is a fence from this + // producer or consumer. + fence_state_->fetch_or(client_state_mask()); + } else { + // Unset bit in fence state to indicate that there is no fence, so that + // when consumer to acquire or producer to acquire, it knows no need to + // check fence for this buffer. + fence_state_->fetch_and(~client_state_mask()); + } + } + + return 0; +} + +int BufferHubBase::Poll(int timeout_ms) { + ATRACE_NAME("BufferHubBase::Poll"); + pollfd p = {event_fd(), POLLIN, 0}; + return poll(&p, 1, timeout_ms); +} + +int BufferHubBase::Lock(int usage, int x, int y, int width, int height, + void** address) { + return buffer_.Lock(usage, x, y, width, height, address); +} + +int BufferHubBase::Unlock() { return buffer_.Unlock(); } + +int BufferHubBase::GetBlobReadWritePointer(size_t size, void** addr) { + int width = static_cast<int>(size); + int height = 1; + int ret = Lock(usage(), 0, 0, width, height, addr); + if (ret == 0) + Unlock(); + return ret; +} + +void BufferHubBase::GetBlobFds(int* fds, size_t* fds_count, + size_t max_fds_count) const { + size_t numFds = static_cast<size_t>(native_handle()->numFds); + *fds_count = std::min(max_fds_count, numFds); + std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/buffer_hub_client.cpp b/libs/vr/libbufferhub/buffer_hub_client.cpp deleted file mode 100644 index 159f2bd1b3..0000000000 --- a/libs/vr/libbufferhub/buffer_hub_client.cpp +++ /dev/null @@ -1,650 +0,0 @@ -#include <private/dvr/buffer_hub_client.h> - -#include <log/log.h> -#include <poll.h> -#include <sys/epoll.h> -#include <utils/Trace.h> - -#include <mutex> - -#include <pdx/default_transport/client_channel.h> -#include <pdx/default_transport/client_channel_factory.h> - -#include "include/private/dvr/bufferhub_rpc.h" - -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; -using android::pdx::default_transport::ClientChannel; -using android::pdx::default_transport::ClientChannelFactory; - -namespace android { -namespace dvr { - -BufferHubClient::BufferHubClient() - : Client(ClientChannelFactory::Create(BufferHubRPC::kClientPath)) {} - -BufferHubClient::BufferHubClient(LocalChannelHandle channel_handle) - : Client(ClientChannel::Create(std::move(channel_handle))) {} - -bool BufferHubClient::IsValid() const { - return IsConnected() && GetChannelHandle().valid(); -} - -LocalChannelHandle BufferHubClient::TakeChannelHandle() { - if (IsConnected()) { - return std::move(GetChannelHandle()); - } else { - return {}; - } -} - -BufferHubBuffer::BufferHubBuffer(LocalChannelHandle channel_handle) - : Client{pdx::default_transport::ClientChannel::Create( - std::move(channel_handle))}, - id_(-1) {} -BufferHubBuffer::BufferHubBuffer(const std::string& endpoint_path) - : Client{pdx::default_transport::ClientChannelFactory::Create( - endpoint_path)}, - id_(-1) {} - -BufferHubBuffer::~BufferHubBuffer() { - if (metadata_header_ != nullptr) { - metadata_buffer_.Unlock(); - } -} - -Status<LocalChannelHandle> BufferHubBuffer::CreateConsumer() { - Status<LocalChannelHandle> status = - InvokeRemoteMethod<BufferHubRPC::NewConsumer>(); - ALOGE_IF(!status, - "BufferHub::CreateConsumer: Failed to create consumer channel: %s", - status.GetErrorMessage().c_str()); - return status; -} - -int BufferHubBuffer::ImportBuffer() { - ATRACE_NAME("BufferHubBuffer::ImportBuffer"); - - Status<BufferDescription<LocalHandle>> status = - InvokeRemoteMethod<BufferHubRPC::GetBuffer>(); - if (!status) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to get buffer: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } else if (status.get().id() < 0) { - ALOGE("BufferHubBuffer::ImportBuffer: Received an invalid id!"); - return -EIO; - } - - auto buffer_desc = status.take(); - - // Stash the buffer id to replace the value in id_. - const int new_id = buffer_desc.id(); - - // Import the buffer. - IonBuffer ion_buffer; - ALOGD_IF(TRACE, "BufferHubBuffer::ImportBuffer: id=%d.", buffer_desc.id()); - - if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) - return ret; - - // Import the metadata. - IonBuffer metadata_buffer; - if (const int ret = buffer_desc.ImportMetadata(&metadata_buffer)) { - ALOGE("Failed to import metadata buffer, error=%d", ret); - return ret; - } - size_t metadata_buf_size = metadata_buffer.width(); - if (metadata_buf_size < BufferHubDefs::kMetadataHeaderSize) { - ALOGE("BufferHubBuffer::ImportBuffer: metadata buffer too small: %zu", - metadata_buf_size); - return -ENOMEM; - } - - // If all imports succee, replace the previous buffer and id. - buffer_ = std::move(ion_buffer); - metadata_buffer_ = std::move(metadata_buffer); - metadata_buf_size_ = metadata_buf_size; - user_metadata_size_ = metadata_buf_size_ - BufferHubDefs::kMetadataHeaderSize; - - void* metadata_ptr = nullptr; - if (const int ret = - metadata_buffer_.Lock(BufferHubDefs::kMetadataUsage, /*x=*/0, - /*y=*/0, metadata_buf_size_, - /*height=*/1, &metadata_ptr)) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to lock metadata."); - return ret; - } - - // Set up shared fences. - shared_acquire_fence_ = buffer_desc.take_acquire_fence(); - shared_release_fence_ = buffer_desc.take_release_fence(); - if (!shared_acquire_fence_ || !shared_release_fence_) { - ALOGE("BufferHubBuffer::ImportBuffer: Failed to import shared fences."); - return -EIO; - } - - metadata_header_ = - reinterpret_cast<BufferHubDefs::MetadataHeader*>(metadata_ptr); - if (user_metadata_size_) { - user_metadata_ptr_ = - reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_ptr) + - BufferHubDefs::kMetadataHeaderSize); - } else { - user_metadata_ptr_ = nullptr; - } - - id_ = new_id; - buffer_state_bit_ = buffer_desc.buffer_state_bit(); - - // Note that here the buffer state is mapped from shared memory as an atomic - // object. The std::atomic's constructor will not be called so that the - // original value stored in the memory region will be preserved. - buffer_state_ = &metadata_header_->buffer_state; - ALOGD_IF(TRACE, - "BufferHubBuffer::ImportBuffer: id=%d, buffer_state=%" PRIx64 ".", - id(), buffer_state_->load()); - fence_state_ = &metadata_header_->fence_state; - ALOGD_IF(TRACE, - "BufferHubBuffer::ImportBuffer: id=%d, fence_state=%" PRIx64 ".", - id(), fence_state_->load()); - - return 0; -} - -inline int BufferHubBuffer::CheckMetadata(size_t user_metadata_size) const { - if (user_metadata_size && !user_metadata_ptr_) { - ALOGE("BufferHubBuffer::CheckMetadata: doesn't support custom metadata."); - return -EINVAL; - } - if (user_metadata_size > user_metadata_size_) { - ALOGE("BufferHubBuffer::CheckMetadata: too big: %zu, maximum: %zu.", - user_metadata_size, user_metadata_size_); - return -E2BIG; - } - return 0; -} - -int BufferHubBuffer::UpdateSharedFence(const LocalHandle& new_fence, - const LocalHandle& shared_fence) { - if (pending_fence_fd_.Get() != new_fence.Get()) { - // First, replace the old fd if there was already one. Skipping if the new - // one is the same as the old. - if (pending_fence_fd_.IsValid()) { - const int ret = epoll_ctl(shared_fence.Get(), EPOLL_CTL_DEL, - pending_fence_fd_.Get(), nullptr); - ALOGW_IF(ret, - "BufferHubBuffer::UpdateSharedFence: failed to remove old fence " - "fd from epoll set, error: %s.", - strerror(errno)); - } - - if (new_fence.IsValid()) { - // If ready fence is valid, we put that into the epoll set. - epoll_event event; - event.events = EPOLLIN; - event.data.u64 = buffer_state_bit(); - pending_fence_fd_ = new_fence.Duplicate(); - if (epoll_ctl(shared_fence.Get(), EPOLL_CTL_ADD, pending_fence_fd_.Get(), - &event) < 0) { - const int error = errno; - ALOGE( - "BufferHubBuffer::UpdateSharedFence: failed to add new fence fd " - "into epoll set, error: %s.", - strerror(error)); - return -error; - } - // Set bit in fence state to indicate that there is a fence from this - // producer or consumer. - fence_state_->fetch_or(buffer_state_bit()); - } else { - // Unset bit in fence state to indicate that there is no fence, so that - // when consumer to acquire or producer to acquire, it knows no need to - // check fence for this buffer. - fence_state_->fetch_and(~buffer_state_bit()); - } - } - - return 0; -} - -int BufferHubBuffer::Poll(int timeout_ms) { - ATRACE_NAME("BufferHubBuffer::Poll"); - pollfd p = {event_fd(), POLLIN, 0}; - return poll(&p, 1, timeout_ms); -} - -int BufferHubBuffer::Lock(int usage, int x, int y, int width, int height, - void** address) { - return buffer_.Lock(usage, x, y, width, height, address); -} - -int BufferHubBuffer::Unlock() { return buffer_.Unlock(); } - -int BufferHubBuffer::GetBlobReadWritePointer(size_t size, void** addr) { - int width = static_cast<int>(size); - int height = 1; - int ret = Lock(usage(), 0, 0, width, height, addr); - if (ret == 0) - Unlock(); - return ret; -} - -int BufferHubBuffer::GetBlobReadOnlyPointer(size_t size, void** addr) { - return GetBlobReadWritePointer(size, addr); -} - -void BufferHubBuffer::GetBlobFds(int* fds, size_t* fds_count, - size_t max_fds_count) const { - size_t numFds = static_cast<size_t>(native_handle()->numFds); - *fds_count = std::min(max_fds_count, numFds); - std::copy(native_handle()->data, native_handle()->data + *fds_count, fds); -} - -BufferConsumer::BufferConsumer(LocalChannelHandle channel) - : BASE(std::move(channel)) { - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE("BufferConsumer::BufferConsumer: Failed to import buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -std::unique_ptr<BufferConsumer> BufferConsumer::Import( - LocalChannelHandle channel) { - ATRACE_NAME("BufferConsumer::Import"); - ALOGD_IF(TRACE, "BufferConsumer::Import: channel=%d", channel.value()); - return BufferConsumer::Create(std::move(channel)); -} - -std::unique_ptr<BufferConsumer> BufferConsumer::Import( - Status<LocalChannelHandle> status) { - return Import(status ? status.take() - : LocalChannelHandle{nullptr, -status.error()}); -} - -int BufferConsumer::LocalAcquire(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - if (!out_meta) - return -EINVAL; - - // Only check producer bit and this consumer buffer's particular consumer bit. - // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit - // is not set. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferPosted(buffer_state, buffer_state_bit())) { - ALOGE("BufferConsumer::LocalAcquire: not posted, id=%d state=%" PRIx64 - " buffer_state_bit=%" PRIx64 ".", - id(), buffer_state, buffer_state_bit()); - return -EBUSY; - } - - // Copy the canonical metadata. - void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); - memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); - // Fill in the user_metadata_ptr in address space of the local process. - if (out_meta->user_metadata_size) { - out_meta->user_metadata_ptr = - reinterpret_cast<uint64_t>(user_metadata_ptr_); - } else { - out_meta->user_metadata_ptr = 0; - } - - uint64_t fence_state = fence_state_->load(); - // If there is an acquire fence from producer, we need to return it. - if (fence_state & BufferHubDefs::kProducerStateBit) { - *out_fence = shared_acquire_fence_.Duplicate(); - } - - // Set the consumer bit unique to this consumer. - BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, buffer_state_bit()); - return 0; -} - -int BufferConsumer::Acquire(LocalHandle* ready_fence) { - return Acquire(ready_fence, nullptr, 0); -} - -int BufferConsumer::Acquire(LocalHandle* ready_fence, void* meta, - size_t user_metadata_size) { - ATRACE_NAME("BufferConsumer::Acquire"); - - if (const int error = CheckMetadata(user_metadata_size)) - return error; - - DvrNativeBufferMetadata canonical_meta; - if (const int error = LocalAcquire(&canonical_meta, ready_fence)) - return error; - - if (meta && user_metadata_size) { - void* metadata_src = - reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); - if (metadata_src) { - memcpy(meta, metadata_src, user_metadata_size); - } else { - ALOGW("BufferConsumer::Acquire: no user-defined metadata."); - } - } - - auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>(); - if (!status) - return -status.error(); - return 0; -} - -int BufferConsumer::AcquireAsync(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - ATRACE_NAME("BufferConsumer::AcquireAsync"); - - if (const int error = LocalAcquire(out_meta, out_fence)) - return error; - - auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); - if (!status) - return -status.error(); - return 0; -} - -int BufferConsumer::LocalRelease(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence) { - if (const int error = CheckMetadata(meta->user_metadata_size)) - return error; - - // Check invalid state transition. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { - ALOGE("BufferConsumer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // On release, only the user requested metadata is copied back into the shared - // memory for metadata. Since there are multiple consumers, it doesn't make - // sense to send the canonical metadata back to the producer. However, one of - // the consumer can still choose to write up to user_metadata_size bytes of - // data into user_metadata_ptr. - if (meta->user_metadata_ptr && meta->user_metadata_size) { - void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); - memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); - } - - // Send out the release fence through the shared epoll fd. Note that during - // releasing the producer is not expected to be polling on the fence. - if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) - return error; - - // For release operation, the client don't need to change the state as it's - // bufferhubd's job to flip the produer bit once all consumers are released. - return 0; -} - -int BufferConsumer::Release(const LocalHandle& release_fence) { - ATRACE_NAME("BufferConsumer::Release"); - - DvrNativeBufferMetadata meta; - if (const int error = LocalRelease(&meta, release_fence)) - return error; - - return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( - BorrowedFence(release_fence.Borrow()))); -} - -int BufferConsumer::ReleaseAsync() { - DvrNativeBufferMetadata meta; - return ReleaseAsync(&meta, LocalHandle()); -} - -int BufferConsumer::ReleaseAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence) { - ATRACE_NAME("BufferConsumer::ReleaseAsync"); - - if (const int error = LocalRelease(meta, release_fence)) - return error; - - return ReturnStatusOrError( - SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); -} - -int BufferConsumer::Discard() { return Release(LocalHandle()); } - -int BufferConsumer::SetIgnore(bool ignore) { - return ReturnStatusOrError( - InvokeRemoteMethod<BufferHubRPC::ConsumerSetIgnore>(ignore)); -} - -BufferProducer::BufferProducer(uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t user_metadata_size) - : BASE(BufferHubRPC::kClientPath) { - ATRACE_NAME("BufferProducer::BufferProducer"); - ALOGD_IF(TRACE, - "BufferProducer::BufferProducer: fd=%d width=%u height=%u format=%u " - "usage=%" PRIx64 " user_metadata_size=%zu", - event_fd(), width, height, format, usage, user_metadata_size); - - auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( - width, height, format, usage, user_metadata_size); - if (!status) { - ALOGE( - "BufferProducer::BufferProducer: Failed to create producer buffer: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -BufferProducer::BufferProducer(uint64_t usage, size_t size) - : BASE(BufferHubRPC::kClientPath) { - ATRACE_NAME("BufferProducer::BufferProducer"); - ALOGD_IF(TRACE, "BufferProducer::BufferProducer: usage=%" PRIx64 " size=%zu", - usage, size); - const int width = static_cast<int>(size); - const int height = 1; - const int format = HAL_PIXEL_FORMAT_BLOB; - const size_t user_metadata_size = 0; - - auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( - width, height, format, usage, user_metadata_size); - if (!status) { - ALOGE("BufferProducer::BufferProducer: Failed to create blob: %s", - status.GetErrorMessage().c_str()); - Close(-status.error()); - return; - } - - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -BufferProducer::BufferProducer(LocalChannelHandle channel) - : BASE(std::move(channel)) { - const int ret = ImportBuffer(); - if (ret < 0) { - ALOGE( - "BufferProducer::BufferProducer: Failed to import producer buffer: %s", - strerror(-ret)); - Close(ret); - } -} - -int BufferProducer::LocalPost(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence) { - if (const int error = CheckMetadata(meta->user_metadata_size)) - return error; - - // Check invalid state transition. - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferGained(buffer_state)) { - ALOGE("BufferProducer::LocalPost: not gained, id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // Copy the canonical metadata. - void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); - memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); - // Copy extra user requested metadata. - if (meta->user_metadata_ptr && meta->user_metadata_size) { - void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); - memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); - } - - // Send out the acquire fence through the shared epoll fd. Note that during - // posting no consumer is not expected to be polling on the fence. - if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) - return error; - - // Set the producer bit atomically to transit into posted state. - BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, - BufferHubDefs::kProducerStateBit); - return 0; -} - -int BufferProducer::Post(const LocalHandle& ready_fence, const void* meta, - size_t user_metadata_size) { - ATRACE_NAME("BufferProducer::Post"); - - // Populate cononical metadata for posting. - DvrNativeBufferMetadata canonical_meta; - canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta); - canonical_meta.user_metadata_size = user_metadata_size; - - if (const int error = LocalPost(&canonical_meta, ready_fence)) - return error; - - return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( - BorrowedFence(ready_fence.Borrow()))); -} - -int BufferProducer::PostAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence) { - ATRACE_NAME("BufferProducer::PostAsync"); - - if (const int error = LocalPost(meta, ready_fence)) - return error; - - return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); -} - -int BufferProducer::LocalGain(DvrNativeBufferMetadata* out_meta, - LocalHandle* out_fence) { - uint64_t buffer_state = buffer_state_->load(); - ALOGD_IF(TRACE, "BufferProducer::LocalGain: buffer=%d, state=%" PRIx64 ".", - id(), buffer_state); - - if (!out_meta) - return -EINVAL; - - if (!BufferHubDefs::IsBufferReleased(buffer_state)) { - if (BufferHubDefs::IsBufferGained(buffer_state)) { - // We don't want to log error when gaining a newly allocated - // buffer. - ALOGI("BufferProducer::LocalGain: already gained id=%d.", id()); - return -EALREADY; - } - ALOGE("BufferProducer::LocalGain: not released id=%d state=%" PRIx64 ".", - id(), buffer_state); - return -EBUSY; - } - - // Canonical metadata is undefined on Gain. Except for user_metadata and - // release_fence_mask. Fill in the user_metadata_ptr in address space of the - // local process. - if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { - out_meta->user_metadata_size = - metadata_header_->metadata.user_metadata_size; - out_meta->user_metadata_ptr = - reinterpret_cast<uint64_t>(user_metadata_ptr_); - } else { - out_meta->user_metadata_size = 0; - out_meta->user_metadata_ptr = 0; - } - - uint64_t fence_state = fence_state_->load(); - // If there is an release fence from consumer, we need to return it. - if (fence_state & BufferHubDefs::kConsumerStateMask) { - *out_fence = shared_release_fence_.Duplicate(); - out_meta->release_fence_mask = - fence_state & BufferHubDefs::kConsumerStateMask; - } - - // Clear out all bits and the buffer is now back to gained state. - buffer_state_->store(0ULL); - return 0; -} - -int BufferProducer::Gain(LocalHandle* release_fence) { - ATRACE_NAME("BufferProducer::Gain"); - - DvrNativeBufferMetadata meta; - if (const int error = LocalGain(&meta, release_fence)) - return error; - - auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); - if (!status) - return -status.error(); - return 0; -} - -int BufferProducer::GainAsync(DvrNativeBufferMetadata* out_meta, - LocalHandle* release_fence) { - ATRACE_NAME("BufferProducer::GainAsync"); - - if (const int error = LocalGain(out_meta, release_fence)) - return error; - - return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); -} - -int BufferProducer::GainAsync() { - DvrNativeBufferMetadata meta; - LocalHandle fence; - return GainAsync(&meta, &fence); -} - -std::unique_ptr<BufferProducer> BufferProducer::Import( - LocalChannelHandle channel) { - ALOGD_IF(TRACE, "BufferProducer::Import: channel=%d", channel.value()); - return BufferProducer::Create(std::move(channel)); -} - -std::unique_ptr<BufferProducer> BufferProducer::Import( - Status<LocalChannelHandle> status) { - return Import(status ? status.take() - : LocalChannelHandle{nullptr, -status.error()}); -} - -Status<LocalChannelHandle> BufferProducer::Detach() { - uint64_t buffer_state = buffer_state_->load(); - if (!BufferHubDefs::IsBufferGained(buffer_state)) { - // Can only detach a BufferProducer when it's in gained state. - ALOGW("BufferProducer::Detach: The buffer (id=%d, state=0x%" PRIx64 - ") is not in gained state.", - id(), buffer_state); - return {}; - } - - Status<LocalChannelHandle> status = - InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>(); - ALOGE_IF(!status, - "BufferProducer::Detach: Failed to detach buffer (id=%d): %s.", id(), - status.GetErrorMessage().c_str()); - return status; -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp new file mode 100644 index 0000000000..8e630ec725 --- /dev/null +++ b/libs/vr/libbufferhub/consumer_buffer.cpp @@ -0,0 +1,179 @@ +#include <private/dvr/consumer_buffer.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; + +namespace android { +namespace dvr { + +ConsumerBuffer::ConsumerBuffer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE("ConsumerBuffer::ConsumerBuffer: Failed to import buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import( + LocalChannelHandle channel) { + ATRACE_NAME("ConsumerBuffer::Import"); + ALOGD_IF(TRACE, "ConsumerBuffer::Import: channel=%d", channel.value()); + return ConsumerBuffer::Create(std::move(channel)); +} + +std::unique_ptr<ConsumerBuffer> ConsumerBuffer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + if (!out_meta) + return -EINVAL; + + // Only check producer bit and this consumer buffer's particular consumer bit. + // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit + // is not set. + uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsBufferPosted(buffer_state, client_state_mask())) { + ALOGE("ConsumerBuffer::LocalAcquire: not posted, id=%d state=%" PRIx64 + " client_state_mask=%" PRIx64 ".", + id(), buffer_state, client_state_mask()); + return -EBUSY; + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata)); + // Fill in the user_metadata_ptr in address space of the local process. + if (out_meta->user_metadata_size) { + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_ptr = 0; + } + + uint64_t fence_state = fence_state_->load(std::memory_order_acquire); + // If there is an acquire fence from producer, we need to return it. + // The producer state bit mask is kFirstClientBitMask for now. + if (fence_state & BufferHubDefs::kFirstClientBitMask) { + *out_fence = shared_acquire_fence_.Duplicate(); + } + + // Set the consumer bit unique to this consumer. + BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, client_state_mask()); + return 0; +} + +int ConsumerBuffer::Acquire(LocalHandle* ready_fence) { + return Acquire(ready_fence, nullptr, 0); +} + +int ConsumerBuffer::Acquire(LocalHandle* ready_fence, void* meta, + size_t user_metadata_size) { + ATRACE_NAME("ConsumerBuffer::Acquire"); + + if (const int error = CheckMetadata(user_metadata_size)) + return error; + + DvrNativeBufferMetadata canonical_meta; + if (const int error = LocalAcquire(&canonical_meta, ready_fence)) + return error; + + if (meta && user_metadata_size) { + void* metadata_src = + reinterpret_cast<void*>(canonical_meta.user_metadata_ptr); + if (metadata_src) { + memcpy(meta, metadata_src, user_metadata_size); + } else { + ALOGW("ConsumerBuffer::Acquire: no user-defined metadata."); + } + } + + auto status = InvokeRemoteMethod<BufferHubRPC::ConsumerAcquire>(); + if (!status) + return -status.error(); + return 0; +} + +int ConsumerBuffer::AcquireAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence) { + ATRACE_NAME("ConsumerBuffer::AcquireAsync"); + + if (const int error = LocalAcquire(out_meta, out_fence)) + return error; + + auto status = SendImpulse(BufferHubRPC::ConsumerAcquire::Opcode); + if (!status) + return -status.error(); + return 0; +} + +int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // Check invalid state transition. + uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsBufferAcquired(buffer_state)) { + ALOGE("ConsumerBuffer::LocalRelease: not acquired id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // On release, only the user requested metadata is copied back into the shared + // memory for metadata. Since there are multiple consumers, it doesn't make + // sense to send the canonical metadata back to the producer. However, one of + // the consumer can still choose to write up to user_metadata_size bytes of + // data into user_metadata_ptr. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the release fence through the shared epoll fd. Note that during + // releasing the producer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(release_fence, shared_release_fence_)) + return error; + + // For release operation, the client don't need to change the state as it's + // bufferhubd's job to flip the produer bit once all consumers are released. + return 0; +} + +int ConsumerBuffer::Release(const LocalHandle& release_fence) { + ATRACE_NAME("ConsumerBuffer::Release"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalRelease(&meta, release_fence)) + return error; + + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ConsumerRelease>( + BorrowedFence(release_fence.Borrow()))); +} + +int ConsumerBuffer::ReleaseAsync() { + DvrNativeBufferMetadata meta; + return ReleaseAsync(&meta, LocalHandle()); +} + +int ConsumerBuffer::ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence) { + ATRACE_NAME("ConsumerBuffer::ReleaseAsync"); + + if (const int error = LocalRelease(meta, release_fence)) + return error; + + return ReturnStatusOrError( + SendImpulse(BufferHubRPC::ConsumerRelease::Opcode)); +} + +int ConsumerBuffer::Discard() { return Release(LocalHandle()); } + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhub/detached_buffer.cpp b/libs/vr/libbufferhub/detached_buffer.cpp deleted file mode 100644 index 6fae16d265..0000000000 --- a/libs/vr/libbufferhub/detached_buffer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include <private/dvr/detached_buffer.h> - -#include <pdx/file_handle.h> -#include <ui/DetachedBufferHandle.h> - -#include <poll.h> - -using android::pdx::LocalChannelHandle; -using android::pdx::LocalHandle; -using android::pdx::Status; - -namespace android { -namespace dvr { - -DetachedBuffer::DetachedBuffer(uint32_t width, uint32_t height, - uint32_t layer_count, uint32_t format, - uint64_t usage, size_t user_metadata_size) { - ATRACE_NAME("DetachedBuffer::DetachedBuffer"); - ALOGD_IF(TRACE, - "DetachedBuffer::DetachedBuffer: width=%u height=%u layer_count=%u, " - "format=%u usage=%" PRIx64 " user_metadata_size=%zu", - width, height, layer_count, format, usage, user_metadata_size); - - auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Create>( - width, height, layer_count, format, usage, user_metadata_size); - if (!status) { - ALOGE( - "DetachedBuffer::DetachedBuffer: Failed to create detached buffer: %s", - status.GetErrorMessage().c_str()); - client_.Close(-status.error()); - } - - const int ret = ImportGraphicBuffer(); - if (ret < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s", - strerror(-ret)); - client_.Close(ret); - } -} - -DetachedBuffer::DetachedBuffer(LocalChannelHandle channel_handle) - : client_(std::move(channel_handle)) { - const int ret = ImportGraphicBuffer(); - if (ret < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import buffer: %s", - strerror(-ret)); - client_.Close(ret); - } -} - -int DetachedBuffer::ImportGraphicBuffer() { - ATRACE_NAME("DetachedBuffer::DetachedBuffer"); - - auto status = client_.InvokeRemoteMethod<DetachedBufferRPC::Import>(); - if (!status) { - ALOGE("DetachedBuffer::DetachedBuffer: Failed to import GraphicBuffer: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - BufferDescription<LocalHandle> buffer_desc = status.take(); - if (buffer_desc.id() < 0) { - ALOGE("DetachedBuffer::DetachedBuffer: Received an invalid id!"); - return -EIO; - } - - // Stash the buffer id to replace the value in id_. - const int buffer_id = buffer_desc.id(); - - // Import the buffer. - IonBuffer ion_buffer; - ALOGD_IF(TRACE, "DetachedBuffer::DetachedBuffer: id=%d.", buffer_id); - - if (const int ret = buffer_desc.ImportBuffer(&ion_buffer)) { - ALOGE("Failed to import GraphicBuffer, error=%d", ret); - return ret; - } - - // If all imports succeed, replace the previous buffer and id. - id_ = buffer_id; - buffer_ = std::move(ion_buffer); - return 0; -} - -int DetachedBuffer::Poll(int timeout_ms) { - ATRACE_NAME("DetachedBuffer::Poll"); - pollfd p = {client_.event_fd(), POLLIN, 0}; - return poll(&p, 1, timeout_ms); -} - -Status<LocalChannelHandle> DetachedBuffer::Promote() { - ATRACE_NAME("DetachedBuffer::Promote"); - ALOGD_IF(TRACE, "DetachedBuffer::Promote: id=%d.", id_); - - auto status_or_handle = - client_.InvokeRemoteMethod<DetachedBufferRPC::Promote>(); - if (status_or_handle.ok()) { - // Invalidate the buffer. - buffer_ = {}; - } else { - ALOGE("DetachedBuffer::Promote: Failed to promote buffer (id=%d): %s.", id_, - status_or_handle.GetErrorMessage().c_str()); - } - return status_or_handle; -} - -sp<GraphicBuffer> DetachedBuffer::TakeGraphicBuffer() { - if (!client_.IsValid() || !buffer_.buffer()) { - ALOGE("DetachedBuffer::TakeGraphicBuffer: Invalid buffer."); - return nullptr; - } - - // Technically this should never happen. - LOG_FATAL_IF( - buffer_.buffer()->isDetachedBuffer(), - "DetachedBuffer::TakeGraphicBuffer: GraphicBuffer is already detached."); - - sp<GraphicBuffer> buffer = std::move(buffer_.buffer()); - buffer->setDetachedBufferHandle( - DetachedBufferHandle::Create(client_.TakeChannelHandle())); - return buffer; -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h new file mode 100644 index 0000000000..31bf79dae3 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/IBufferClient.h @@ -0,0 +1,33 @@ +#ifndef ANDROID_DVR_IBUFFERCLIENT_H +#define ANDROID_DVR_IBUFFERCLIENT_H + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { +namespace dvr { + +// Interface for acessing BufferHubBuffer remotely. +class IBufferClient : public IInterface { + public: + DECLARE_META_INTERFACE(BufferClient); + + // Checks if the buffer node is valid. + virtual bool isValid() = 0; + + // Duplicates the client. Token_out will be set to a new token when succeed, + // and not changed when failed. + virtual status_t duplicate(uint64_t* outToken) = 0; +}; + +// BnInterface for IBufferClient. Should only be created in bufferhub service. +class BnBufferClient : public BnInterface<IBufferClient> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h new file mode 100644 index 0000000000..09feb73f81 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h @@ -0,0 +1,170 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_BASE_H_ +#define ANDROID_DVR_BUFFER_HUB_BASE_H_ + +#include <vector> + +#include <private/dvr/bufferhub_rpc.h> + +namespace android { +namespace dvr { + +// Base class of two types of BufferHub clients: dvr::ProducerBuffer and +// dvr::ConsumerBuffer. +class BufferHubBase : public pdx::Client { + public: + using LocalHandle = pdx::LocalHandle; + using LocalChannelHandle = pdx::LocalChannelHandle; + template <typename T> + using Status = pdx::Status<T>; + + // Create a new consumer channel that is attached to the producer. Returns + // a file descriptor for the new channel or a negative error code. + Status<LocalChannelHandle> CreateConsumer(); + + // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). + int Poll(int timeout_ms); + + // Locks the area specified by (x, y, width, height) for a specific usage. If + // the usage is software then |addr| will be updated to point to the address + // of the buffer in virtual memory. The caller should only access/modify the + // pixels in the specified area. anything else is undefined behavior. + int Lock(int usage, int x, int y, int width, int height, void** addr); + + // Must be called after Lock() when the caller has finished changing the + // buffer. + int Unlock(); + + // Gets a blob buffer that was created with ProducerBuffer::CreateBlob. + // Locking and Unlocking is handled internally. There's no need to Unlock + // after calling this method. + int GetBlobReadWritePointer(size_t size, void** addr); + + // Returns a dup'd file descriptor for accessing the blob shared memory. The + // caller takes ownership of the file descriptor and must close it or pass on + // ownership. Some GPU API extensions can take file descriptors to bind shared + // memory gralloc buffers to GPU buffer objects. + LocalHandle GetBlobFd() const { + // Current GPU vendor puts the buffer allocation in one FD. If we change GPU + // vendors and this is the wrong fd, late-latching and EDS will very clearly + // stop working and we will need to correct this. The alternative is to use + // a GL context in the pose service to allocate this buffer or to use the + // ION API directly instead of gralloc. + return LocalHandle(dup(native_handle()->data[0])); + } + + // Get up to |max_fds_count| file descriptors for accessing the blob shared + // memory. |fds_count| will contain the actual number of file descriptors. + void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; + + using Client::event_fd; + + Status<int> GetEventMask(int events) { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + std::vector<pdx::ClientChannel::EventSource> GetEventSources() const { + if (auto* client_channel = GetChannel()) { + return client_channel->GetEventSources(); + } else { + return {}; + } + } + + native_handle_t* native_handle() const { + return const_cast<native_handle_t*>(buffer_.handle()); + } + + IonBuffer* buffer() { return &buffer_; } + const IonBuffer* buffer() const { return &buffer_; } + + // Gets ID of the buffer client. All BufferHub clients derived from the same + // buffer in bufferhubd share the same buffer id. + int id() const { return id_; } + + // Gets the channel id of the buffer client. Each BufferHub client has its + // system unique channel id. + int cid() const { return cid_; } + + // Returns the buffer buffer state. + uint64_t buffer_state() { + return buffer_state_->load(std::memory_order_acquire); + }; + + // A state mask which is unique to a buffer hub client among all its siblings + // sharing the same concrete graphic buffer. + uint64_t client_state_mask() const { return client_state_mask_; } + + // The following methods return settings of the first buffer. Currently, + // it is only possible to create multi-buffer BufferHubBases with the same + // settings. + uint32_t width() const { return buffer_.width(); } + uint32_t height() const { return buffer_.height(); } + uint32_t stride() const { return buffer_.stride(); } + uint32_t format() const { return buffer_.format(); } + uint32_t usage() const { return buffer_.usage(); } + uint32_t layer_count() const { return buffer_.layer_count(); } + + uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } + void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } + + protected: + explicit BufferHubBase(LocalChannelHandle channel); + explicit BufferHubBase(const std::string& endpoint_path); + virtual ~BufferHubBase(); + + // Initialization helper. + int ImportBuffer(); + + // Check invalid metadata operation. Returns 0 if requested metadata is valid. + int CheckMetadata(size_t user_metadata_size) const; + + // Send out the new fence by updating the shared fence (shared_release_fence + // for producer and shared_acquire_fence for consumer). Note that during this + // should only be used in LocalPost() or LocalRelease, and the shared fence + // shouldn't be poll'ed by the other end. + int UpdateSharedFence(const LocalHandle& new_fence, + const LocalHandle& shared_fence); + + // IonBuffer that is shared between bufferhubd, producer, and consumers. + size_t metadata_buf_size_{0}; + size_t user_metadata_size_{0}; + BufferHubDefs::MetadataHeader* metadata_header_{nullptr}; + void* user_metadata_ptr_{nullptr}; + std::atomic<uint64_t>* buffer_state_{nullptr}; + std::atomic<uint64_t>* fence_state_{nullptr}; + std::atomic<uint64_t>* active_clients_bit_mask_{nullptr}; + + LocalHandle shared_acquire_fence_; + LocalHandle shared_release_fence_; + + // A local fence fd that holds the ownership of the fence fd on Post (for + // producer) and Release (for consumer). + LocalHandle pending_fence_fd_; + + private: + BufferHubBase(const BufferHubBase&) = delete; + void operator=(const BufferHubBase&) = delete; + + // Global id for the buffer that is consistent across processes. It is meant + // for logging and debugging purposes only and should not be used for lookup + // or any other functional purpose as a security precaution. + int id_; + + // Channel id. + int cid_; + + // Client bit mask which indicates the locations of this client object in the + // buffer_state_. + uint64_t client_state_mask_{0ULL}; + IonBuffer buffer_; + IonBuffer metadata_buffer_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_BASE_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h index 0ea77c85fb..1daeed9b50 100644 --- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h @@ -1,347 +1,10 @@ #ifndef ANDROID_DVR_BUFFER_HUB_CLIENT_H_ #define ANDROID_DVR_BUFFER_HUB_CLIENT_H_ -#include <hardware/gralloc.h> -#include <pdx/channel_handle.h> -#include <pdx/client.h> -#include <pdx/file_handle.h> -#include <pdx/status.h> - -#include <vector> - -#include <private/dvr/ion_buffer.h> - -#include "bufferhub_rpc.h" - -namespace android { -namespace dvr { - -class BufferHubClient : public pdx::Client { - public: - BufferHubClient(); - explicit BufferHubClient(pdx::LocalChannelHandle channel_handle); - - bool IsValid() const; - pdx::LocalChannelHandle TakeChannelHandle(); - - using pdx::Client::Close; - using pdx::Client::GetChannel; - using pdx::Client::InvokeRemoteMethod; - using pdx::Client::IsConnected; - using pdx::Client::event_fd; -}; - -class BufferHubBuffer : public pdx::Client { - public: - using LocalHandle = pdx::LocalHandle; - using LocalChannelHandle = pdx::LocalChannelHandle; - template <typename T> - using Status = pdx::Status<T>; - - // Create a new consumer channel that is attached to the producer. Returns - // a file descriptor for the new channel or a negative error code. - Status<LocalChannelHandle> CreateConsumer(); - - // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). - int Poll(int timeout_ms); - - // Locks the area specified by (x, y, width, height) for a specific usage. If - // the usage is software then |addr| will be updated to point to the address - // of the buffer in virtual memory. The caller should only access/modify the - // pixels in the specified area. anything else is undefined behavior. - int Lock(int usage, int x, int y, int width, int height, void** addr); - - // Must be called after Lock() when the caller has finished changing the - // buffer. - int Unlock(); - - // Gets a blob buffer that was created with BufferProducer::CreateBlob. - // Locking and Unlocking is handled internally. There's no need to Unlock - // after calling this method. - int GetBlobReadWritePointer(size_t size, void** addr); - - // Gets a blob buffer that was created with BufferProducer::CreateBlob. - // Locking and Unlocking is handled internally. There's no need to Unlock - // after calling this method. - int GetBlobReadOnlyPointer(size_t size, void** addr); - - // Returns a dup'd file descriptor for accessing the blob shared memory. The - // caller takes ownership of the file descriptor and must close it or pass on - // ownership. Some GPU API extensions can take file descriptors to bind shared - // memory gralloc buffers to GPU buffer objects. - LocalHandle GetBlobFd() const { - // Current GPU vendor puts the buffer allocation in one FD. If we change GPU - // vendors and this is the wrong fd, late-latching and EDS will very clearly - // stop working and we will need to correct this. The alternative is to use - // a GL context in the pose service to allocate this buffer or to use the - // ION API directly instead of gralloc. - return LocalHandle(dup(native_handle()->data[0])); - } - - // Get up to |max_fds_count| file descriptors for accessing the blob shared - // memory. |fds_count| will contain the actual number of file descriptors. - void GetBlobFds(int* fds, size_t* fds_count, size_t max_fds_count) const; - - using Client::event_fd; - - Status<int> GetEventMask(int events) { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - std::vector<pdx::ClientChannel::EventSource> GetEventSources() const { - if (auto* client_channel = GetChannel()) { - return client_channel->GetEventSources(); - } else { - return {}; - } - } - - native_handle_t* native_handle() const { - return const_cast<native_handle_t*>(buffer_.handle()); - } - - IonBuffer* buffer() { return &buffer_; } - const IonBuffer* buffer() const { return &buffer_; } - - int id() const { return id_; } - - // Returns the buffer buffer state. - uint64_t buffer_state() { return buffer_state_->load(); }; - - // A state mask which is unique to a buffer hub client among all its siblings - // sharing the same concrete graphic buffer. - uint64_t buffer_state_bit() const { return buffer_state_bit_; } - - // The following methods return settings of the first buffer. Currently, - // it is only possible to create multi-buffer BufferHubBuffers with the same - // settings. - uint32_t width() const { return buffer_.width(); } - uint32_t height() const { return buffer_.height(); } - uint32_t stride() const { return buffer_.stride(); } - uint32_t format() const { return buffer_.format(); } - uint32_t usage() const { return buffer_.usage(); } - uint32_t layer_count() const { return buffer_.layer_count(); } - - uint64_t GetQueueIndex() const { return metadata_header_->queue_index; } - void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = index; } - - protected: - explicit BufferHubBuffer(LocalChannelHandle channel); - explicit BufferHubBuffer(const std::string& endpoint_path); - virtual ~BufferHubBuffer(); - - // Initialization helper. - int ImportBuffer(); - - // Check invalid metadata operation. Returns 0 if requested metadata is valid. - int CheckMetadata(size_t user_metadata_size) const; - - // Send out the new fence by updating the shared fence (shared_release_fence - // for producer and shared_acquire_fence for consumer). Note that during this - // should only be used in LocalPost() or LocalRelease, and the shared fence - // shouldn't be poll'ed by the other end. - int UpdateSharedFence(const LocalHandle& new_fence, - const LocalHandle& shared_fence); - - // IonBuffer that is shared between bufferhubd, producer, and consumers. - size_t metadata_buf_size_{0}; - size_t user_metadata_size_{0}; - BufferHubDefs::MetadataHeader* metadata_header_{nullptr}; - void* user_metadata_ptr_{nullptr}; - std::atomic<uint64_t>* buffer_state_{nullptr}; - std::atomic<uint64_t>* fence_state_{nullptr}; - - LocalHandle shared_acquire_fence_; - LocalHandle shared_release_fence_; - - // A local fence fd that holds the ownership of the fence fd on Post (for - // producer) and Release (for consumer). - LocalHandle pending_fence_fd_; - - private: - BufferHubBuffer(const BufferHubBuffer&) = delete; - void operator=(const BufferHubBuffer&) = delete; - - // Global id for the buffer that is consistent across processes. It is meant - // for logging and debugging purposes only and should not be used for lookup - // or any other functional purpose as a security precaution. - int id_; - uint64_t buffer_state_bit_{0ULL}; - IonBuffer buffer_; - IonBuffer metadata_buffer_; -}; - -// This represents a writable buffer. Calling Post notifies all clients and -// makes the buffer read-only. Call Gain to acquire write access. A buffer -// may have many consumers. -// -// The user of BufferProducer is responsible with making sure that the Post() is -// done with the correct metadata type and size. The user is also responsible -// for making sure that remote ends (BufferConsumers) are also using the correct -// metadata when acquiring the buffer. The API guarantees that a Post() with a -// metadata of wrong size will fail. However, it currently does not do any -// type checking. -// The API also assumes that metadata is a serializable type (plain old data). -class BufferProducer : public pdx::ClientBase<BufferProducer, BufferHubBuffer> { - public: - // Imports a bufferhub producer channel, assuming ownership of its handle. - static std::unique_ptr<BufferProducer> Import(LocalChannelHandle channel); - static std::unique_ptr<BufferProducer> Import( - Status<LocalChannelHandle> status); - - // Asynchronously posts a buffer. The fence and metadata are passed to - // consumer via shared fd and shared memory. - int PostAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence); - - // Post this buffer, passing |ready_fence| to the consumers. The bytes in - // |meta| are passed unaltered to the consumers. The producer must not modify - // the buffer until it is re-gained. - // This returns zero or a negative unix error code. - int Post(const LocalHandle& ready_fence, const void* meta, - size_t user_metadata_size); - - template <typename Meta, - typename = typename std::enable_if<std::is_void<Meta>::value>::type> - int Post(const LocalHandle& ready_fence) { - return Post(ready_fence, nullptr, 0); - } - template <typename Meta, typename = typename std::enable_if< - !std::is_void<Meta>::value>::type> - int Post(const LocalHandle& ready_fence, const Meta& meta) { - return Post(ready_fence, &meta, sizeof(meta)); - } - - // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it - // must be waited on before using the buffer. If it is not valid then the - // buffer is free for immediate use. This call will only succeed if the buffer - // is in the released state. - // This returns zero or a negative unix error code. - int Gain(LocalHandle* release_fence); - int GainAsync(); - - // Asynchronously marks a released buffer as gained. This method is similar to - // the synchronous version above, except that it does not wait for BufferHub - // to acknowledge success or failure. Because of the asynchronous nature of - // the underlying message, no error is returned if this method is called when - // the buffer is in an incorrect state. Returns zero if sending the message - // succeeded, or a negative errno code if local error check fails. - int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - - // Detaches a ProducerBuffer from an existing producer/consumer set. Can only - // be called when a producer buffer has exclusive access to the buffer (i.e. - // in the gain'ed state). On the successful return of the IPC call, a new - // LocalChannelHandle representing a detached buffer will be returned and all - // existing producer and consumer channels will be closed. Further IPCs - // towards those channels will return error. - Status<LocalChannelHandle> Detach(); - - private: - friend BASE; - - // Constructors are automatically exposed through BufferProducer::Create(...) - // static template methods inherited from ClientBase, which take the same - // arguments as the constructors. - - // Constructs a buffer with the given geometry and parameters. - BufferProducer(uint32_t width, uint32_t height, uint32_t format, - uint64_t usage, size_t metadata_size = 0); - - // Constructs a blob (flat) buffer with the given usage flags. - BufferProducer(uint64_t usage, size_t size); - - // Imports the given file handle to a producer channel, taking ownership. - explicit BufferProducer(LocalChannelHandle channel); - - // Local state transition helpers. - int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - int LocalPost(const DvrNativeBufferMetadata* meta, - const LocalHandle& ready_fence); -}; - -// This is a connection to a producer buffer, which can be located in another -// application. When that buffer is Post()ed, this fd will be signaled and -// Acquire allows read access. The user is responsible for making sure that -// Acquire is called with the correct metadata structure. The only guarantee the -// API currently provides is that an Acquire() with metadata of the wrong size -// will fail. -class BufferConsumer : public pdx::ClientBase<BufferConsumer, BufferHubBuffer> { - public: - // This call assumes ownership of |fd|. - static std::unique_ptr<BufferConsumer> Import(LocalChannelHandle channel); - static std::unique_ptr<BufferConsumer> Import( - Status<LocalChannelHandle> status); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| will be set to a fence to wait on until the buffer is ready. - // This call will only succeed after the fd is signalled. This call may be - // performed as an alternative to the Acquire() with metadata. In such cases - // the metadata is not read. - // - // This returns zero or negative unix error code. - int Acquire(LocalHandle* ready_fence); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| is set to a fence signaling that the contents of the buffer - // are available. This call will only succeed if the buffer is in the posted - // state. - // Returns zero on success, or a negative errno code otherwise. - int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); - - // Attempt to retrieve a post event from buffer hub. If successful, - // |ready_fence| is set to a fence to wait on until the buffer is ready. This - // call will only succeed after the fd is signaled. This returns zero or a - // negative unix error code. - template <typename Meta> - int Acquire(LocalHandle* ready_fence, Meta* meta) { - return Acquire(ready_fence, meta, sizeof(*meta)); - } - - // Asynchronously acquires a bufer. - int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - - // This should be called after a successful Acquire call. If the fence is - // valid the fence determines the buffer usage, otherwise the buffer is - // released immediately. - // This returns zero or a negative unix error code. - int Release(const LocalHandle& release_fence); - int ReleaseAsync(); - - // Asynchronously releases a buffer. Similar to the synchronous version above, - // except that it does not wait for BufferHub to reply with success or error. - // The fence and metadata are passed to consumer via shared fd and shared - // memory. - int ReleaseAsync(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence); - - // May be called after or instead of Acquire to indicate that the consumer - // does not need to access the buffer this cycle. This returns zero or a - // negative unix error code. - int Discard(); - - // When set, this consumer is no longer notified when this buffer is - // available. The system behaves as if Discard() is immediately called - // whenever the buffer is posted. If ignore is set to true while a buffer is - // pending, it will act as if Discard() was also called. - // This returns zero or a negative unix error code. - int SetIgnore(bool ignore); - - private: - friend BASE; - - explicit BufferConsumer(LocalChannelHandle channel); - - // Local state transition helpers. - int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); - int LocalRelease(const DvrNativeBufferMetadata* meta, - const LocalHandle& release_fence); -}; - -} // namespace dvr -} // namespace android +// TODO(b/116855254): This header is completely deprecated and replaced by +// consumer_buffer.h and producer_buffer.h. Remove this file once all references +// to it has been removed. +#include <private/dvr/consumer_buffer.h> +#include <private/dvr/producer_buffer.h> #endif // ANDROID_DVR_BUFFER_HUB_CLIENT_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h new file mode 100644 index 0000000000..650da97fe3 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h @@ -0,0 +1,219 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_DEFS_H_ +#define ANDROID_DVR_BUFFER_HUB_DEFS_H_ + +#include <dvr/dvr_api.h> +#include <hardware/gralloc.h> +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <pdx/rpc/remote_method.h> +#include <pdx/rpc/serializable.h> +#include <private/dvr/native_handle_wrapper.h> + +#include <atomic> + +namespace android { +namespace dvr { + +namespace BufferHubDefs { + +static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; +static constexpr uint32_t kMetadataUsage = + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; + +// Single producuer multiple (up to 63) consumers ownership signal. +// 64-bit atomic unsigned int. +// +// MSB LSB +// | | +// v v +// [C62|...|C1|C0|P] +// Gain'ed state: [..|0|0|0] -> Exclusively Writable. +// Post'ed state: [..|0|0|1] +// Acquired'ed state: [..|X|X|1] -> At least one bit is set in higher 63 bits +// Released'ed state: [..|X|X|0] -> At least one bit is set in higher 63 bits +static constexpr int kMaxNumberOfClients = 64; +static constexpr uint64_t kFirstClientBitMask = 1ULL; +static constexpr uint64_t kConsumerStateMask = ~kFirstClientBitMask; + +static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state, + uint64_t clear_mask, uint64_t set_mask) { + uint64_t old_state; + uint64_t new_state; + do { + old_state = buffer_state->load(std::memory_order_acquire); + new_state = (old_state & ~clear_mask) | set_mask; + } while (!buffer_state->compare_exchange_weak(old_state, new_state)); +} + +static inline bool IsBufferGained(uint64_t state) { return state == 0; } + +static inline bool IsBufferPosted(uint64_t state, + uint64_t consumer_bit = kConsumerStateMask) { + return (state & kFirstClientBitMask) && !(state & consumer_bit); +} + +static inline bool IsBufferAcquired(uint64_t state) { + return (state & kFirstClientBitMask) && (state & kConsumerStateMask); +} + +static inline bool IsBufferReleased(uint64_t state) { + return !(state & kFirstClientBitMask) && (state & kConsumerStateMask); +} + +static inline uint64_t FindNextAvailableClientStateMask(uint64_t bits) { + return ~bits - (~bits & (~bits - 1)); +} + +static inline uint64_t FindFirstClearedBit() { + return FindNextAvailableClientStateMask(kFirstClientBitMask); +} + +struct __attribute__((packed, aligned(8))) MetadataHeader { + // Internal data format, which can be updated as long as the size, padding and + // field alignment of the struct is consistent within the same ABI. As this + // part is subject for future updates, it's not stable cross Android version, + // so don't have it visible from outside of the Android platform (include Apps + // and vendor HAL). + std::atomic<uint64_t> buffer_state; + std::atomic<uint64_t> fence_state; + std::atomic<uint64_t> active_clients_bit_mask; + uint64_t queue_index; + + // Public data format, which should be updated with caution. See more details + // in dvr_api.h + DvrNativeBufferMetadata metadata; +}; + +static_assert(sizeof(MetadataHeader) == 136, "Unexpected MetadataHeader size"); +static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); + +} // namespace BufferHubDefs + +template <typename FileHandleType> +class BufferTraits { + public: + BufferTraits() = default; + BufferTraits(const native_handle_t* buffer_handle, + const FileHandleType& metadata_handle, int id, + uint64_t client_state_mask, uint64_t metadata_size, + uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, uint32_t stride, + const FileHandleType& acquire_fence_fd, + const FileHandleType& release_fence_fd) + : id_(id), + client_state_mask_(client_state_mask), + metadata_size_(metadata_size), + width_(width), + height_(height), + layer_count_(layer_count), + format_(format), + usage_(usage), + stride_(stride), + buffer_handle_(buffer_handle), + metadata_handle_(metadata_handle.Borrow()), + acquire_fence_fd_(acquire_fence_fd.Borrow()), + release_fence_fd_(release_fence_fd.Borrow()) {} + + BufferTraits(BufferTraits&& other) = default; + BufferTraits& operator=(BufferTraits&& other) = default; + + // ID of the buffer client. All BufferHubBuffer clients derived from the same + // buffer in bufferhubd share the same buffer id. + int id() const { return id_; } + + // State mask of the buffer client. Each BufferHubBuffer client backed by the + // same buffer channel has uniqued state bit among its siblings. For a + // producer buffer the bit must be kFirstClientBitMask; for a consumer the bit + // must be one of the kConsumerStateMask. + uint64_t client_state_mask() const { return client_state_mask_; } + uint64_t metadata_size() const { return metadata_size_; } + + uint32_t width() { return width_; } + uint32_t height() { return height_; } + uint32_t layer_count() { return layer_count_; } + uint32_t format() { return format_; } + uint64_t usage() { return usage_; } + uint32_t stride() { return stride_; } + + const NativeHandleWrapper<FileHandleType>& buffer_handle() const { + return buffer_handle_; + } + + NativeHandleWrapper<FileHandleType> take_buffer_handle() { + return std::move(buffer_handle_); + } + FileHandleType take_metadata_handle() { return std::move(metadata_handle_); } + FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } + FileHandleType take_release_fence() { return std::move(release_fence_fd_); } + + private: + // BufferHub specific traits. + int id_ = -1; + uint64_t client_state_mask_; + uint64_t metadata_size_; + + // Traits for a GraphicBuffer. + uint32_t width_; + uint32_t height_; + uint32_t layer_count_; + uint32_t format_; + uint64_t usage_; + uint32_t stride_; + + // Native handle for the graphic buffer. + NativeHandleWrapper<FileHandleType> buffer_handle_; + + // File handle of an ashmem that holds buffer metadata. + FileHandleType metadata_handle_; + + // Pamameters for shared fences. + FileHandleType acquire_fence_fd_; + FileHandleType release_fence_fd_; + + PDX_SERIALIZABLE_MEMBERS(BufferTraits<FileHandleType>, id_, + client_state_mask_, metadata_size_, stride_, width_, + height_, layer_count_, format_, usage_, + buffer_handle_, metadata_handle_, acquire_fence_fd_, + release_fence_fd_); + + BufferTraits(const BufferTraits&) = delete; + void operator=(const BufferTraits&) = delete; +}; + +struct DetachedBufferRPC { + private: + enum { + kOpDetachedBufferBase = 1000, + + // Allocates a standalone DetachedBuffer not associated with any producer + // consumer set. + kOpCreate, + + // Imports the given channel handle to a DetachedBuffer, taking ownership. + kOpImport, + + // Creates a DetachedBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + kOpDuplicate, + }; + + // Aliases. + using LocalChannelHandle = pdx::LocalChannelHandle; + using LocalHandle = pdx::LocalHandle; + using Void = pdx::rpc::Void; + + public: + PDX_REMOTE_METHOD(Create, kOpCreate, + void(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, + size_t user_metadata_size)); + PDX_REMOTE_METHOD(Import, kOpImport, BufferTraits<LocalHandle>(Void)); + PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void)); + + PDX_REMOTE_API(API, Create, Import, Duplicate); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_DEFS_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h index 04f4fb4c21..5ff4e00a01 100644 --- a/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h +++ b/libs/vr/libbufferhub/include/private/dvr/bufferhub_rpc.h @@ -1,11 +1,11 @@ #ifndef ANDROID_DVR_BUFFERHUB_RPC_H_ #define ANDROID_DVR_BUFFERHUB_RPC_H_ +#include "buffer_hub_defs.h" + #include <cutils/native_handle.h> -#include <sys/types.h> #include <ui/BufferQueueDefs.h> -#include <dvr/dvr_api.h> #include <pdx/channel_handle.h> #include <pdx/file_handle.h> #include <pdx/rpc/remote_method.h> @@ -15,71 +15,6 @@ namespace android { namespace dvr { -namespace BufferHubDefs { - -static constexpr uint32_t kMetadataFormat = HAL_PIXEL_FORMAT_BLOB; -static constexpr uint32_t kMetadataUsage = - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN; - -// Single producuer multiple (up to 63) consumers ownership signal. -// 64-bit atomic unsigned int. -// -// MSB LSB -// | | -// v v -// [P|C62|...|C1|C0] -// Gain'ed state: [0|..|0|0] -> Exclusively Writable. -// Post'ed state: [1|..|0|0] -// Acquired'ed state: [1|..|X|X] -> At least one bit is set in lower 63 bits -// Released'ed state: [0|..|X|X] -> At least one bit is set in lower 63 bits -static constexpr uint64_t kProducerStateBit = 1ULL << 63; -static constexpr uint64_t kConsumerStateMask = (1ULL << 63) - 1; - -static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state, - uint64_t clear_mask, uint64_t set_mask) { - uint64_t old_state; - uint64_t new_state; - do { - old_state = buffer_state->load(); - new_state = (old_state & ~clear_mask) | set_mask; - } while (!buffer_state->compare_exchange_weak(old_state, new_state)); -} - -static inline bool IsBufferGained(uint64_t state) { return state == 0; } - -static inline bool IsBufferPosted(uint64_t state, - uint64_t consumer_bit = kConsumerStateMask) { - return (state & kProducerStateBit) && !(state & consumer_bit); -} - -static inline bool IsBufferAcquired(uint64_t state) { - return (state & kProducerStateBit) && (state & kConsumerStateMask); -} - -static inline bool IsBufferReleased(uint64_t state) { - return !(state & kProducerStateBit) && (state & kConsumerStateMask); -} - -struct __attribute__((packed, aligned(8))) MetadataHeader { - // Internal data format, which can be updated as long as the size, padding and - // field alignment of the struct is consistent within the same ABI. As this - // part is subject for future updates, it's not stable cross Android version, - // so don't have it visible from outside of the Android platform (include Apps - // and vendor HAL). - std::atomic<uint64_t> buffer_state; - std::atomic<uint64_t> fence_state; - uint64_t queue_index; - - // Public data format, which should be updated with caution. See more details - // in dvr_api.h - DvrNativeBufferMetadata metadata; -}; - -static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); -static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); - -} // namespace BufferHubDefs - template <typename FileHandleType> class NativeBufferHandle { public: @@ -164,11 +99,12 @@ class BufferDescription { public: BufferDescription() = default; BufferDescription(const IonBuffer& buffer, const IonBuffer& metadata, int id, - uint64_t buffer_state_bit, + int buffer_cid, uint64_t client_state_mask, const FileHandleType& acquire_fence_fd, const FileHandleType& release_fence_fd) : id_(id), - buffer_state_bit_(buffer_state_bit), + buffer_cid_(buffer_cid), + client_state_mask_(client_state_mask), buffer_(buffer, id), metadata_(metadata, id), acquire_fence_fd_(acquire_fence_fd.Borrow()), @@ -177,14 +113,17 @@ class BufferDescription { BufferDescription(BufferDescription&& other) noexcept = default; BufferDescription& operator=(BufferDescription&& other) noexcept = default; - // ID of the buffer client. All BufferHubBuffer clients derived from the same - // buffer in bufferhubd share the same buffer id. + // ID of the buffer client. All BufferHub clients derived from the same buffer + // in bufferhubd share the same buffer id. int id() const { return id_; } - // State mask of the buffer client. Each BufferHubBuffer client backed by the - // same buffer channel has uniqued state bit among its siblings. For a - // producer buffer the bit must be kProducerStateBit; for a consumer the bit - // must be one of the kConsumerStateMask. - uint64_t buffer_state_bit() const { return buffer_state_bit_; } + + // Channel ID of the buffer client. Each BufferHub client has its system + // unique channel id. + int buffer_cid() const { return buffer_cid_; } + + // State mask of the buffer client. Each BufferHub client backed by the + // same buffer channel has uniqued state bit among its siblings. + uint64_t client_state_mask() const { return client_state_mask_; } FileHandleType take_acquire_fence() { return std::move(acquire_fence_fd_); } FileHandleType take_release_fence() { return std::move(release_fence_fd_); } @@ -193,7 +132,8 @@ class BufferDescription { private: int id_{-1}; - uint64_t buffer_state_bit_{0}; + int buffer_cid_{-1}; + uint64_t client_state_mask_{0}; // Two IonBuffers: one for the graphic buffer and one for metadata. NativeBufferHandle<FileHandleType> buffer_; NativeBufferHandle<FileHandleType> metadata_; @@ -202,8 +142,8 @@ class BufferDescription { FileHandleType acquire_fence_fd_; FileHandleType release_fence_fd_; - PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, - buffer_state_bit_, buffer_, metadata_, + PDX_SERIALIZABLE_MEMBERS(BufferDescription<FileHandleType>, id_, buffer_cid_, + client_state_mask_, buffer_, metadata_, acquire_fence_fd_, release_fence_fd_); BufferDescription(const BufferDescription&) = delete; @@ -372,19 +312,15 @@ struct BufferHubRPC { kOpProducerGain, kOpConsumerAcquire, kOpConsumerRelease, - kOpConsumerSetIgnore, - kOpProducerBufferDetach, kOpConsumerBufferDetach, - kOpDetachedBufferCreate, - kOpDetachedBufferPromote, kOpCreateProducerQueue, kOpCreateConsumerQueue, kOpGetQueueInfo, kOpProducerQueueAllocateBuffers, + kOpProducerQueueInsertBuffer, kOpProducerQueueRemoveBuffer, kOpConsumerQueueImportBuffers, // TODO(b/77153033): Separate all those RPC operations into subclasses. - kOpDetachedBufferBase = 1000, }; // Aliases. @@ -405,9 +341,6 @@ struct BufferHubRPC { PDX_REMOTE_METHOD(ConsumerAcquire, kOpConsumerAcquire, LocalFence(Void)); PDX_REMOTE_METHOD(ConsumerRelease, kOpConsumerRelease, void(LocalFence release_fence)); - PDX_REMOTE_METHOD(ConsumerSetIgnore, kOpConsumerSetIgnore, void(bool ignore)); - PDX_REMOTE_METHOD(ProducerBufferDetach, kOpProducerBufferDetach, - LocalChannelHandle(Void)); // Detaches a ConsumerBuffer from an existing producer/consumer set. Can only // be called when the consumer is the only consumer and it has exclusive @@ -430,31 +363,14 @@ struct BufferHubRPC { std::vector<std::pair<LocalChannelHandle, size_t>>( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count)); + PDX_REMOTE_METHOD(ProducerQueueInsertBuffer, kOpProducerQueueInsertBuffer, + size_t(int buffer_cid)); PDX_REMOTE_METHOD(ProducerQueueRemoveBuffer, kOpProducerQueueRemoveBuffer, void(size_t slot)); PDX_REMOTE_METHOD(ConsumerQueueImportBuffers, kOpConsumerQueueImportBuffers, std::vector<std::pair<LocalChannelHandle, size_t>>(Void)); }; -struct DetachedBufferRPC final : public BufferHubRPC { - private: - enum { - kOpCreate = kOpDetachedBufferBase, - kOpImport, - kOpPromote, - }; - - public: - PDX_REMOTE_METHOD(Create, kOpCreate, - void(uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size)); - PDX_REMOTE_METHOD(Import, kOpImport, BufferDescription<LocalHandle>(Void)); - PDX_REMOTE_METHOD(Promote, kOpPromote, LocalChannelHandle(Void)); - - PDX_REMOTE_API(API, Create, Promote); -}; - } // namespace dvr } // namespace android diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h new file mode 100644 index 0000000000..7349779323 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h @@ -0,0 +1,84 @@ +#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_ +#define ANDROID_DVR_CONSUMER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferConsumer was originally poorly named and gets easily confused with +// IGraphicBufferConsumer. Actually, BufferConsumer is a single buffer that can +// consume (i.e. read) data from a buffer, but it doesn't consume buffer. On +// the other hand, IGraphicBufferConsumer is the consumer end of a BufferQueue +// and it is used to consume buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ConsumerBuffer BufferConsumer; + +// This is a connection to a producer buffer, which can be located in another +// application. When that buffer is Post()ed, this fd will be signaled and +// Acquire allows read access. The user is responsible for making sure that +// Acquire is called with the correct metadata structure. The only guarantee the +// API currently provides is that an Acquire() with metadata of the wrong size +// will fail. +class ConsumerBuffer : public pdx::ClientBase<ConsumerBuffer, BufferHubBase> { + public: + // This call assumes ownership of |fd|. + static std::unique_ptr<ConsumerBuffer> Import(LocalChannelHandle channel); + static std::unique_ptr<ConsumerBuffer> Import( + Status<LocalChannelHandle> status); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| will be set to a fence to wait on until the buffer is ready. + // This call will only succeed after the fd is signalled. This call may be + // performed as an alternative to the Acquire() with metadata. In such cases + // the metadata is not read. + // + // This returns zero or negative unix error code. + int Acquire(LocalHandle* ready_fence); + + // Attempt to retrieve a post event from buffer hub. If successful, + // |ready_fence| is set to a fence signaling that the contents of the buffer + // are available. This call will only succeed if the buffer is in the posted + // state. + // Returns zero on success, or a negative errno code otherwise. + int Acquire(LocalHandle* ready_fence, void* meta, size_t user_metadata_size); + + // Asynchronously acquires a bufer. + int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + + // This should be called after a successful Acquire call. If the fence is + // valid the fence determines the buffer usage, otherwise the buffer is + // released immediately. + // This returns zero or a negative unix error code. + int Release(const LocalHandle& release_fence); + int ReleaseAsync(); + + // Asynchronously releases a buffer. Similar to the synchronous version above, + // except that it does not wait for BufferHub to reply with success or error. + // The fence and metadata are passed to consumer via shared fd and shared + // memory. + int ReleaseAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); + + // May be called after or instead of Acquire to indicate that the consumer + // does not need to access the buffer this cycle. This returns zero or a + // negative unix error code. + int Discard(); + + private: + friend BASE; + + explicit ConsumerBuffer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalAcquire(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence); + int LocalRelease(const DvrNativeBufferMetadata* meta, + const LocalHandle& release_fence); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_CONSUMER_BUFFER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h b/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h deleted file mode 100644 index 6d0b502271..0000000000 --- a/libs/vr/libbufferhub/include/private/dvr/detached_buffer.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ANDROID_DVR_DETACHED_BUFFER_H_ -#define ANDROID_DVR_DETACHED_BUFFER_H_ - -#include <private/dvr/buffer_hub_client.h> - -namespace android { -namespace dvr { - -class DetachedBuffer { - public: - // Allocates a standalone DetachedBuffer not associated with any producer - // consumer set. - static std::unique_ptr<DetachedBuffer> Create(uint32_t width, uint32_t height, - uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size) { - return std::unique_ptr<DetachedBuffer>(new DetachedBuffer( - width, height, layer_count, format, usage, user_metadata_size)); - } - - // Imports the given channel handle to a DetachedBuffer, taking ownership. - static std::unique_ptr<DetachedBuffer> Import( - pdx::LocalChannelHandle channel_handle) { - return std::unique_ptr<DetachedBuffer>( - new DetachedBuffer(std::move(channel_handle))); - } - - DetachedBuffer(const DetachedBuffer&) = delete; - void operator=(const DetachedBuffer&) = delete; - - const sp<GraphicBuffer>& buffer() const { return buffer_.buffer(); } - - int id() const { return id_; } - - // Returns true if the buffer holds an open PDX channels towards bufferhubd. - bool IsConnected() const { return client_.IsValid(); } - - // Returns true if the buffer holds an valid gralloc buffer handle that's - // availble for the client to read from and/or write into. - bool IsValid() const { return buffer_.IsValid(); } - - // Returns the event mask for all the events that are pending on this buffer - // (see sys/poll.h for all possible bits). - pdx::Status<int> GetEventMask(int events) { - if (auto* channel = client_.GetChannel()) { - return channel->GetEventMask(events); - } else { - return pdx::ErrorStatus(EINVAL); - } - } - - // Polls the fd for |timeout_ms| milliseconds (-1 for infinity). - int Poll(int timeout_ms); - - // Promotes a DetachedBuffer to become a ProducerBuffer. Once promoted the - // DetachedBuffer channel will be closed automatically on successful IPC - // return. Further IPCs towards this channel will return error. - pdx::Status<pdx::LocalChannelHandle> Promote(); - - // Takes the underlying graphic buffer out of this DetachedBuffer. This call - // immediately invalidates this DetachedBuffer object and transfers the - // underlying pdx::LocalChannelHandle into the GraphicBuffer. - sp<GraphicBuffer> TakeGraphicBuffer(); - - private: - DetachedBuffer(uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, size_t user_metadata_size); - - DetachedBuffer(pdx::LocalChannelHandle channel_handle); - - int ImportGraphicBuffer(); - - // Global id for the buffer that is consistent across processes. - int id_; - IonBuffer buffer_; - BufferHubClient client_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_DETACHED_BUFFER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h new file mode 100644 index 0000000000..ae70b77110 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/native_handle_wrapper.h @@ -0,0 +1,100 @@ +#ifndef ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ +#define ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ + +#include <cutils/native_handle.h> + +#include <vector> + +namespace android { +namespace dvr { + +// A PDX-friendly wrapper to maintain the life cycle of a native_handle_t +// object. +// +// See https://source.android.com/devices/architecture/hidl/types#handle_t for +// more information about native_handle_t. +template <typename FileHandleType> +class NativeHandleWrapper { + public: + NativeHandleWrapper() = default; + NativeHandleWrapper(NativeHandleWrapper&& other) = default; + NativeHandleWrapper& operator=(NativeHandleWrapper&& other) = default; + + // Create a new NativeHandleWrapper by duplicating the handle. + explicit NativeHandleWrapper(const native_handle_t* handle) { + const int fd_count = handle->numFds; + const int int_count = handle->numInts; + + // Populate the fd and int vectors: native_handle->data[] is an array of fds + // followed by an array of opaque ints. + for (int i = 0; i < fd_count; i++) { + fds_.emplace_back(FileHandleType::AsDuplicate(handle->data[i])); + } + for (int i = 0; i < int_count; i++) { + ints_.push_back(handle->data[fd_count + i]); + } + } + + size_t int_count() const { return ints_.size(); } + size_t fd_count() const { return fds_.size(); } + bool IsValid() const { return ints_.size() != 0 || fds_.size() != 0; } + + // Duplicate a native handle from the wrapper. + native_handle_t* DuplicateHandle() const { + if (!IsValid()) { + return nullptr; + } + + // numFds + numInts ints. + std::vector<FileHandleType> fds; + for (const auto& fd : fds_) { + if (!fd.IsValid()) { + return nullptr; + } + fds.emplace_back(fd.Duplicate()); + } + + return FromFdsAndInts(std::move(fds), ints_); + } + + // Takes the native handle out of the wrapper. + native_handle_t* TakeHandle() { + if (!IsValid()) { + return nullptr; + } + + return FromFdsAndInts(std::move(fds_), std::move(ints_)); + } + + private: + NativeHandleWrapper(const NativeHandleWrapper&) = delete; + void operator=(const NativeHandleWrapper&) = delete; + + static native_handle_t* FromFdsAndInts(std::vector<FileHandleType> fds, + std::vector<int> ints) { + native_handle_t* handle = native_handle_create(fds.size(), ints.size()); + if (!handle) { + ALOGE("NativeHandleWrapper::TakeHandle: Failed to create new handle."); + return nullptr; + } + + // numFds + numInts ints. + for (int i = 0; i < handle->numFds; i++) { + handle->data[i] = fds[i].Release(); + } + memcpy(&handle->data[handle->numFds], ints.data(), + sizeof(int) * handle->numInts); + + return handle; + } + + std::vector<int> ints_; + std::vector<FileHandleType> fds_; + + PDX_SERIALIZABLE_MEMBERS(NativeHandleWrapper<FileHandleType>, ints_, fds_); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_NATIVE_HANDLE_WRAPPER_H_ diff --git a/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h new file mode 100644 index 0000000000..2761416034 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h @@ -0,0 +1,113 @@ +#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_ +#define ANDROID_DVR_PRODUCER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferProducer was originally poorly named and gets easily confused with +// IGraphicBufferProducer. Actually, BufferProducer is a single buffer that can +// produce (i.e. write) data into a buffer, but it doesn't produce buffer. On +// the other hand, IGraphicBufferProducer is the producer end of a BufferQueue +// and it is used to produce buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ProducerBuffer BufferProducer; + +// This represents a writable buffer. Calling Post notifies all clients and +// makes the buffer read-only. Call Gain to acquire write access. A buffer +// may have many consumers. +// +// The user of ProducerBuffer is responsible with making sure that the Post() is +// done with the correct metadata type and size. The user is also responsible +// for making sure that remote ends (BufferConsumers) are also using the correct +// metadata when acquiring the buffer. The API guarantees that a Post() with a +// metadata of wrong size will fail. However, it currently does not do any +// type checking. +// The API also assumes that metadata is a serializable type (plain old data). +class ProducerBuffer : public pdx::ClientBase<ProducerBuffer, BufferHubBase> { + public: + // Imports a bufferhub producer channel, assuming ownership of its handle. + static std::unique_ptr<ProducerBuffer> Import(LocalChannelHandle channel); + static std::unique_ptr<ProducerBuffer> Import( + Status<LocalChannelHandle> status); + + // Asynchronously posts a buffer. The fence and metadata are passed to + // consumer via shared fd and shared memory. + int PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); + + // Post this buffer, passing |ready_fence| to the consumers. The bytes in + // |meta| are passed unaltered to the consumers. The producer must not modify + // the buffer until it is re-gained. + // This returns zero or a negative unix error code. + int Post(const LocalHandle& ready_fence, const void* meta, + size_t user_metadata_size); + + int Post(const LocalHandle& ready_fence) { + return Post(ready_fence, nullptr, 0); + } + + // Attempt to re-gain the buffer for writing. If |release_fence| is valid, it + // must be waited on before using the buffer. If it is not valid then the + // buffer is free for immediate use. This call will succeed if the buffer + // is in the released state, or in posted state and gain_posted_buffer is + // true. + // + // @param release_fence output fence. + // @param gain_posted_buffer whether to gain posted buffer or not. + // @return This returns zero or a negative unix error code. + int Gain(LocalHandle* release_fence, bool gain_posted_buffer = false); + + // Asynchronously marks a released buffer as gained. This method is similar to + // the synchronous version above, except that it does not wait for BufferHub + // to acknowledge success or failure. Because of the asynchronous nature of + // the underlying message, no error is returned if this method is called when + // the buffer is in an incorrect state. Returns zero if sending the message + // succeeded, or a negative errno code if local error check fails. + // TODO(b/112007999): gain_posted_buffer true is only used to prevent + // libdvrtracking from starving when there are non-responding clients. This + // gain_posted_buffer param can be removed once libdvrtracking start to use + // the new AHardwareBuffer API. + int GainAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, + bool gain_posted_buffer = false); + int GainAsync(); + + // Detaches a ProducerBuffer from an existing producer/consumer set. Can only + // be called when a producer buffer has exclusive access to the buffer (i.e. + // in the gain'ed state). On the successful return of the IPC call, a new + // LocalChannelHandle representing a detached buffer will be returned and all + // existing producer and consumer channels will be closed. Further IPCs + // towards those channels will return error. + Status<LocalChannelHandle> Detach(); + + private: + friend BASE; + + // Constructors are automatically exposed through ProducerBuffer::Create(...) + // static template methods inherited from ClientBase, which take the same + // arguments as the constructors. + + // Constructs a buffer with the given geometry and parameters. + ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t metadata_size = 0); + + // Constructs a blob (flat) buffer with the given usage flags. + ProducerBuffer(uint64_t usage, size_t size); + + // Imports the given file handle to a producer channel, taking ownership. + explicit ProducerBuffer(LocalChannelHandle channel); + + // Local state transition helpers. + int LocalGain(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence, + bool gain_posted_buffer = false); + int LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_PRODUCER_BUFFER_H_ diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp new file mode 100644 index 0000000000..f36e1697cb --- /dev/null +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -0,0 +1,252 @@ +#include <private/dvr/producer_buffer.h> + +using android::pdx::LocalChannelHandle; +using android::pdx::LocalHandle; +using android::pdx::Status; + +namespace android { +namespace dvr { + +ProducerBuffer::ProducerBuffer(uint32_t width, uint32_t height, uint32_t format, + uint64_t usage, size_t user_metadata_size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("ProducerBuffer::ProducerBuffer"); + ALOGD_IF(TRACE, + "ProducerBuffer::ProducerBuffer: fd=%d width=%u height=%u format=%u " + "usage=%" PRIx64 " user_metadata_size=%zu", + event_fd(), width, height, format, usage, user_metadata_size); + + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, usage, user_metadata_size); + if (!status) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to create producer buffer: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +ProducerBuffer::ProducerBuffer(uint64_t usage, size_t size) + : BASE(BufferHubRPC::kClientPath) { + ATRACE_NAME("ProducerBuffer::ProducerBuffer"); + ALOGD_IF(TRACE, "ProducerBuffer::ProducerBuffer: usage=%" PRIx64 " size=%zu", + usage, size); + const int width = static_cast<int>(size); + const int height = 1; + const int format = HAL_PIXEL_FORMAT_BLOB; + const size_t user_metadata_size = 0; + + auto status = InvokeRemoteMethod<BufferHubRPC::CreateBuffer>( + width, height, format, usage, user_metadata_size); + if (!status) { + ALOGE("ProducerBuffer::ProducerBuffer: Failed to create blob: %s", + status.GetErrorMessage().c_str()); + Close(-status.error()); + return; + } + + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +ProducerBuffer::ProducerBuffer(LocalChannelHandle channel) + : BASE(std::move(channel)) { + const int ret = ImportBuffer(); + if (ret < 0) { + ALOGE( + "ProducerBuffer::ProducerBuffer: Failed to import producer buffer: %s", + strerror(-ret)); + Close(ret); + } +} + +int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + if (const int error = CheckMetadata(meta->user_metadata_size)) + return error; + + // Check invalid state transition. + uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsBufferGained(buffer_state)) { + ALOGE("ProducerBuffer::LocalPost: not gained, id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // Copy the canonical metadata. + void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata); + memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata)); + // Copy extra user requested metadata. + if (meta->user_metadata_ptr && meta->user_metadata_size) { + void* metadata_src = reinterpret_cast<void*>(meta->user_metadata_ptr); + memcpy(user_metadata_ptr_, metadata_src, meta->user_metadata_size); + } + + // Send out the acquire fence through the shared epoll fd. Note that during + // posting no consumer is not expected to be polling on the fence. + if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_)) + return error; + + // Set the producer bit atomically to transit into posted state. + // The producer state bit mask is kFirstClientBitMask for now. + BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, + BufferHubDefs::kFirstClientBitMask); + return 0; +} + +int ProducerBuffer::Post(const LocalHandle& ready_fence, const void* meta, + size_t user_metadata_size) { + ATRACE_NAME("ProducerBuffer::Post"); + + // Populate cononical metadata for posting. + DvrNativeBufferMetadata canonical_meta; + canonical_meta.user_metadata_ptr = reinterpret_cast<uint64_t>(meta); + canonical_meta.user_metadata_size = user_metadata_size; + + if (const int error = LocalPost(&canonical_meta, ready_fence)) + return error; + + return ReturnStatusOrError(InvokeRemoteMethod<BufferHubRPC::ProducerPost>( + BorrowedFence(ready_fence.Borrow()))); +} + +int ProducerBuffer::PostAsync(const DvrNativeBufferMetadata* meta, + const LocalHandle& ready_fence) { + ATRACE_NAME("ProducerBuffer::PostAsync"); + + if (const int error = LocalPost(meta, ready_fence)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerPost::Opcode)); +} + +int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, + LocalHandle* out_fence, bool gain_posted_buffer) { + uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); + ALOGD_IF(TRACE, "ProducerBuffer::LocalGain: buffer=%d, state=%" PRIx64 ".", + id(), buffer_state); + + if (!out_meta) + return -EINVAL; + + if (BufferHubDefs::IsBufferGained(buffer_state)) { + // We don't want to log error when gaining a newly allocated + // buffer. + ALOGI("ProducerBuffer::LocalGain: already gained id=%d.", id()); + return -EALREADY; + } + if (BufferHubDefs::IsBufferAcquired(buffer_state) || + (BufferHubDefs::IsBufferPosted(buffer_state) && !gain_posted_buffer)) { + ALOGE("ProducerBuffer::LocalGain: not released id=%d state=%" PRIx64 ".", + id(), buffer_state); + return -EBUSY; + } + + // Canonical metadata is undefined on Gain. Except for user_metadata and + // release_fence_mask. Fill in the user_metadata_ptr in address space of the + // local process. + if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { + out_meta->user_metadata_size = + metadata_header_->metadata.user_metadata_size; + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_size = 0; + out_meta->user_metadata_ptr = 0; + } + + uint64_t fence_state = fence_state_->load(std::memory_order_acquire); + // If there is an release fence from consumer, we need to return it. + if (fence_state & BufferHubDefs::kConsumerStateMask) { + *out_fence = shared_release_fence_.Duplicate(); + out_meta->release_fence_mask = + fence_state & BufferHubDefs::kConsumerStateMask; + } + + // Clear out all bits and the buffer is now back to gained state. + buffer_state_->store(0ULL); + return 0; +} + +int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::Gain"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer)) + return error; + + auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); + if (!status) + return -status.error(); + return 0; +} + +int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* release_fence, + bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::GainAsync"); + + if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); +} + +int ProducerBuffer::GainAsync() { + DvrNativeBufferMetadata meta; + LocalHandle fence; + return GainAsync(&meta, &fence); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + LocalChannelHandle channel) { + ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value()); + return ProducerBuffer::Create(std::move(channel)); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +Status<LocalChannelHandle> ProducerBuffer::Detach() { + // TODO(b/112338294) remove after migrate producer buffer to binder + ALOGW("ProducerBuffer::Detach: not supported operation during migration"); + return {}; + + // TODO(b/112338294) Keep here for reference. Remove it after new logic is + // written. + /* uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsBufferGained(buffer_state)) { + // Can only detach a ProducerBuffer when it's in gained state. + ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx64 + ") is not in gained state.", + id(), buffer_state); + return {}; + } + + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>(); + ALOGE_IF(!status, + "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(), + status.GetErrorMessage().c_str()); + return status; */ +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index 9f72c05f0c..20894e3588 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -59,6 +59,9 @@ cc_library_shared { static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } subdirs = ["benchmarks", "tests"] diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp index 5089b8754e..ef1eed6d0a 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp @@ -5,7 +5,7 @@ cc_benchmark { "libbase", "libbinder", "libcutils", - "libdvr", + "libdvr.google", "libgui", "liblog", "libhardware", diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp index 4ca8671f24..b2b4d7a799 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp +++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp @@ -206,7 +206,7 @@ class BinderBufferTransport : public BufferTransport { class DvrApi { public: DvrApi() { - handle_ = dlopen("libdvr.so", RTLD_NOW | RTLD_LOCAL); + handle_ = dlopen("libdvr.google.so", RTLD_NOW | RTLD_LOCAL); CHECK(handle_); auto dvr_get_api = diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp index 8feb1cd803..e1c1aa96c0 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -278,7 +278,7 @@ Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { } Status<void> BufferHubQueue::AddBuffer( - const std::shared_ptr<BufferHubBuffer>& buffer, size_t slot) { + const std::shared_ptr<BufferHubBase>& buffer, size_t slot) { ALOGD_IF(TRACE, "BufferHubQueue::AddBuffer: buffer_id=%d slot=%zu", buffer->id(), slot); @@ -356,8 +356,8 @@ Status<void> BufferHubQueue::Enqueue(Entry entry) { } } -Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, - size_t* slot) { +Status<std::shared_ptr<BufferHubBase>> BufferHubQueue::Dequeue(int timeout, + size_t* slot) { ALOGD_IF(TRACE, "BufferHubQueue::Dequeue: count=%zu, timeout=%d", count(), timeout); @@ -372,7 +372,7 @@ Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, PDX_TRACE_FORMAT("buffer|buffer_id=%d;slot=%zu|", entry.buffer->id(), entry.slot); - std::shared_ptr<BufferHubBuffer> buffer = std::move(entry.buffer); + std::shared_ptr<BufferHubBase> buffer = std::move(entry.buffer); *slot = entry.slot; available_buffers_.pop(); @@ -440,6 +440,10 @@ ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, uint64_t usage, size_t buffer_count) { + if (buffer_count == 0) { + return {std::vector<size_t>()}; + } + if (capacity() + buffer_count > kMaxQueueCapacity) { ALOGE( "ProducerQueue::AllocateBuffers: queue is at capacity: %zu, cannot " @@ -481,10 +485,13 @@ Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( } } - if (buffer_slots.size() == 0) { - // Error out if no buffer is allocated and improted. - ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffers: no buffer allocated."); - ErrorStatus(ENOMEM); + if (buffer_slots.size() != buffer_count) { + // Error out if the count of imported buffer(s) is not correct. + ALOGE( + "ProducerQueue::AllocateBuffers: requested to import %zu " + "buffers, but actually imported %zu buffers.", + buffer_count, buffer_slots.size()); + return ErrorStatus(ENOMEM); } return {std::move(buffer_slots)}; @@ -503,11 +510,6 @@ Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, return status.error_status(); } - if (status.get().size() == 0) { - ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated."); - ErrorStatus(ENOMEM); - } - return {status.get()[0]}; } @@ -524,6 +526,40 @@ Status<void> ProducerQueue::AddBuffer( return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); } +Status<size_t> ProducerQueue::InsertBuffer( + const std::shared_ptr<BufferProducer>& buffer) { + if (buffer == nullptr || + !BufferHubDefs::IsBufferGained(buffer->buffer_state())) { + ALOGE( + "ProducerQueue::InsertBuffer: Can only insert a buffer when it's in " + "gained state."); + return ErrorStatus(EINVAL); + } + + auto status_or_slot = + InvokeRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( + buffer->cid()); + if (!status_or_slot) { + ALOGE( + "ProducerQueue::InsertBuffer: Failed to insert producer buffer: " + "buffer_cid=%d, error: %s.", + buffer->cid(), status_or_slot.GetErrorMessage().c_str()); + return status_or_slot.error_status(); + } + + size_t slot = status_or_slot.get(); + + // Note that we are calling AddBuffer() from the base class to explicitly + // avoid Enqueue() the BufferProducer. + auto status = BufferHubQueue::AddBuffer(buffer, slot); + if (!status) { + ALOGE("ProducerQueue::InsertBuffer: Failed to add buffer: %s.", + status.GetErrorMessage().c_str()); + return status.error_status(); + } + return {slot}; +} + Status<void> ProducerQueue::RemoveBuffer(size_t slot) { auto status = InvokeRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>(slot); diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h index 60e1c4b8a9..c69002debd 100644 --- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h +++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h @@ -31,13 +31,13 @@ namespace dvr { class ConsumerQueue; -// |BufferHubQueue| manages a queue of |BufferHubBuffer|s. Buffers are +// |BufferHubQueue| manages a queue of |BufferHubBase|s. Buffers are // automatically re-requeued when released by the remote side. class BufferHubQueue : public pdx::Client { public: using BufferAvailableCallback = std::function<void()>; using BufferRemovedCallback = - std::function<void(const std::shared_ptr<BufferHubBuffer>&)>; + std::function<void(const std::shared_ptr<BufferHubBase>&)>; virtual ~BufferHubQueue() {} @@ -93,7 +93,7 @@ class BufferHubQueue : public pdx::Client { : -1; } - std::shared_ptr<BufferHubBuffer> GetBuffer(size_t slot) const { + std::shared_ptr<BufferHubBase> GetBuffer(size_t slot) const { return buffers_[slot]; } @@ -142,7 +142,7 @@ class BufferHubQueue : public pdx::Client { // Register a buffer for management by the queue. Used by subclasses to add a // buffer to internal bookkeeping. - pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBuffer>& buffer, + pdx::Status<void> AddBuffer(const std::shared_ptr<BufferHubBase>& buffer, size_t slot); // Called by ProducerQueue::RemoveBuffer and ConsumerQueue::RemoveBuffer only @@ -158,8 +158,8 @@ class BufferHubQueue : public pdx::Client { // block. Specifying a timeout of -1 causes Dequeue() to block indefinitely, // while specifying a timeout equal to zero cause Dequeue() to return // immediately, even if no buffers are available. - pdx::Status<std::shared_ptr<BufferHubBuffer>> Dequeue(int timeout, - size_t* slot); + pdx::Status<std::shared_ptr<BufferHubBase>> Dequeue(int timeout, + size_t* slot); // Waits for buffers to become available and adds them to the available queue. bool WaitForBuffers(int timeout); @@ -172,10 +172,10 @@ class BufferHubQueue : public pdx::Client { // per-buffer data. struct Entry { Entry() : slot(0) {} - Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, size_t in_slot, + Entry(const std::shared_ptr<BufferHubBase>& in_buffer, size_t in_slot, uint64_t in_index) : buffer(in_buffer), slot(in_slot), index(in_index) {} - Entry(const std::shared_ptr<BufferHubBuffer>& in_buffer, + Entry(const std::shared_ptr<BufferHubBase>& in_buffer, std::unique_ptr<uint8_t[]> in_metadata, pdx::LocalHandle in_fence, size_t in_slot) : buffer(in_buffer), @@ -185,7 +185,7 @@ class BufferHubQueue : public pdx::Client { Entry(Entry&&) = default; Entry& operator=(Entry&&) = default; - std::shared_ptr<BufferHubBuffer> buffer; + std::shared_ptr<BufferHubBase> buffer; std::unique_ptr<uint8_t[]> metadata; pdx::LocalHandle fence; size_t slot; @@ -250,7 +250,7 @@ class BufferHubQueue : public pdx::Client { // Tracks the buffers belonging to this queue. Buffers are stored according to // "slot" in this vector. Each slot is a logical id of the buffer within this // queue regardless of its queue position or presence in the ring buffer. - std::array<std::shared_ptr<BufferHubBuffer>, kMaxQueueCapacity> buffers_; + std::array<std::shared_ptr<BufferHubBase>, kMaxQueueCapacity> buffers_; // Buffers and related data that are available for dequeue. std::priority_queue<Entry, std::vector<Entry>, EntryComparator> @@ -331,6 +331,13 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& buffer, size_t slot); + // Inserts a ProducerBuffer into the queue. On success, the method returns the + // |slot| number where the new buffer gets inserted. Note that the buffer + // being inserted should be in Gain'ed state prior to the call and it's + // considered as already Dequeued when the function returns. + pdx::Status<size_t> InsertBuffer( + const std::shared_ptr<BufferProducer>& buffer); + // Remove producer buffer from the queue. pdx::Status<void> RemoveBuffer(size_t slot) override; diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp index 47a27344bd..046df5412f 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp @@ -181,6 +181,40 @@ TEST_F(BufferHubQueueTest, TestProducerConsumer) { } } +TEST_F(BufferHubQueueTest, TestInsertBuffer) { + ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); + + consumer_queue_ = producer_queue_->CreateConsumerQueue(); + ASSERT_TRUE(consumer_queue_ != nullptr); + EXPECT_EQ(producer_queue_->capacity(), 0); + EXPECT_EQ(consumer_queue_->capacity(), 0); + + std::shared_ptr<BufferProducer> p1 = BufferProducer::Create( + kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0); + ASSERT_TRUE(p1 != nullptr); + + // Inserting a posted buffer will fail. + DvrNativeBufferMetadata meta; + EXPECT_EQ(p1->PostAsync(&meta, LocalHandle()), 0); + auto status_or_slot = producer_queue_->InsertBuffer(p1); + EXPECT_FALSE(status_or_slot.ok()); + EXPECT_EQ(status_or_slot.error(), EINVAL); + + // Inserting a gained buffer will succeed. + std::shared_ptr<BufferProducer> p2 = BufferProducer::Create( + kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage); + ASSERT_TRUE(p2 != nullptr); + status_or_slot = producer_queue_->InsertBuffer(p2); + EXPECT_TRUE(status_or_slot.ok()); + // This is the first buffer inserted, should take slot 0. + size_t slot = status_or_slot.get(); + EXPECT_EQ(slot, 0); + + // Wait and expect the consumer to kick up the newly inserted buffer. + WaitAndHandleOnce(consumer_queue_.get(), kTimeoutMs); + EXPECT_EQ(consumer_queue_->capacity(), 1ULL); +} + TEST_F(BufferHubQueueTest, TestRemoveBuffer) { ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{})); DvrNativeBufferMetadata mo; @@ -528,6 +562,30 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { ASSERT_EQ(cs2, ps2); } +TEST_F(BufferHubQueueTest, TestAllocateTwoBuffers) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + ASSERT_EQ(producer_queue_->capacity(), 0); + auto status = producer_queue_->AllocateBuffers( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage, /*buffer_count=*/2); + ASSERT_TRUE(status.ok()); + std::vector<size_t> buffer_slots = status.take(); + ASSERT_EQ(buffer_slots.size(), 2); + ASSERT_EQ(producer_queue_->capacity(), 2); +} + +TEST_F(BufferHubQueueTest, TestAllocateZeroBuffers) { + ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{})); + ASSERT_EQ(producer_queue_->capacity(), 0); + auto status = producer_queue_->AllocateBuffers( + kBufferWidth, kBufferHeight, kBufferLayerCount, kBufferFormat, + kBufferUsage, /*buffer_count=*/0); + ASSERT_TRUE(status.ok()); + std::vector<size_t> buffer_slots = status.take(); + ASSERT_EQ(buffer_slots.size(), 0); + ASSERT_EQ(producer_queue_->capacity(), 0); +} + TEST_F(BufferHubQueueTest, TestUsageSetMask) { const uint32_t set_mask = GRALLOC_USAGE_SW_WRITE_OFTEN; ASSERT_TRUE( diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp index 9c678815cd..8c354fbc18 100644 --- a/libs/vr/libdisplay/Android.bp +++ b/libs/vr/libdisplay/Android.bp @@ -16,8 +16,8 @@ sourceFiles = [ "display_client.cpp", "display_manager_client.cpp", "display_protocol.cpp", - "vsync_client.cpp", "shared_buffer_helpers.cpp", + "vsync_service.cpp", ] localIncludeFiles = [ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h deleted file mode 100644 index 1eeb80e09d..0000000000 --- a/libs/vr/libdisplay/include/private/dvr/vsync_client.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef ANDROID_DVR_VSYNC_CLIENT_H_ -#define ANDROID_DVR_VSYNC_CLIENT_H_ - -#include <stdint.h> - -#include <pdx/client.h> - -struct dvr_vsync_client {}; - -namespace android { -namespace dvr { - -/* - * VSyncClient is a remote interface to the vsync service in displayd. - * This class is used to wait for and retrieve information about the - * display vsync. - */ -class VSyncClient : public pdx::ClientBase<VSyncClient>, - public dvr_vsync_client { - public: - /* - * Wait for the next vsync signal. - * The timestamp (in ns) is written into *ts when ts is non-NULL. - */ - int Wait(int64_t* timestamp_ns); - - /* - * Returns the file descriptor used to communicate with the vsync system - * service or -1 on error. - */ - int GetFd(); - - /* - * Clears the select/poll/epoll event so that subsequent calls to - * these will not signal until the next vsync. - */ - int Acknowledge(); - - /* - * Get the timestamp of the last vsync event in ns. This call has - * the same side effect on events as Acknowledge(), which saves - * an IPC message. - */ - int GetLastTimestamp(int64_t* timestamp_ns); - - /* - * Get vsync scheduling info. - * Get the estimated timestamp of the next GPU lens warp preemption event in - * ns. Also returns the corresponding vsync count that the next lens warp - * operation will target. This call has the same side effect on events as - * Acknowledge(), which saves an IPC message. - */ - int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns, - uint32_t* next_vsync_count); - - private: - friend BASE; - - VSyncClient(); - explicit VSyncClient(long timeout_ms); - - VSyncClient(const VSyncClient&) = delete; - void operator=(const VSyncClient&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_VSYNC_CLIENT_H_ diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h new file mode 100644 index 0000000000..152464abd1 --- /dev/null +++ b/libs/vr/libdisplay/include/private/dvr/vsync_service.h @@ -0,0 +1,65 @@ +#ifndef ANDROID_DVR_VSYNC_SERVICE_H_ +#define ANDROID_DVR_VSYNC_SERVICE_H_ + +#include <binder/IInterface.h> + +namespace android { +namespace dvr { + +class IVsyncCallback : public IInterface { + public: + DECLARE_META_INTERFACE(VsyncCallback) + + enum { + ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION + }; + + virtual status_t onVsync(int64_t vsync_timestamp) = 0; +}; + +class BnVsyncCallback : public BnInterface<IVsyncCallback> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +// Register a callback with IVsyncService to be notified of vsync events and +// timestamps. There's also a shared memory vsync buffer defined in +// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared +// memory buffer that make it preferable in certain situations: +// +// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService +// is always available as long as surface flinger is running. +// +// 2. IVsyncService will make a binder callback when a vsync event occurs. This +// allows the client to not write code to implement periodic "get the latest +// vsync" calls, which is necessary with the vsync shared memory buffer. +// +// 3. The IVsyncService provides the real vsync timestamp reported by hardware +// composer, whereas the vsync shared memory buffer only has predicted vsync +// times. +class IVsyncService : public IInterface { +public: + DECLARE_META_INTERFACE(VsyncService) + + static const char* GetServiceName() { return "vrflinger_vsync"; } + + enum { + REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION, + UNREGISTER_CALLBACK + }; + + virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0; + virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0; +}; + +class BnVsyncService : public BnInterface<IVsyncService> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_VSYNC_SERVICE_H_ diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp deleted file mode 100644 index bc6cf6cabe..0000000000 --- a/libs/vr/libdisplay/vsync_client.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "include/private/dvr/vsync_client.h" - -#include <log/log.h> - -#include <pdx/default_transport/client_channel_factory.h> -#include <private/dvr/display_protocol.h> - -using android::dvr::display::VSyncProtocol; -using android::pdx::Transaction; - -namespace android { -namespace dvr { - -VSyncClient::VSyncClient(long timeout_ms) - : BASE(pdx::default_transport::ClientChannelFactory::Create( - VSyncProtocol::kClientPath), - timeout_ms) {} - -VSyncClient::VSyncClient() - : BASE(pdx::default_transport::ClientChannelFactory::Create( - VSyncProtocol::kClientPath)) {} - -int VSyncClient::Wait(int64_t* timestamp_ns) { - auto status = InvokeRemoteMethod<VSyncProtocol::Wait>(); - if (!status) { - ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - if (timestamp_ns != nullptr) { - *timestamp_ns = status.get(); - } - return 0; -} - -int VSyncClient::GetFd() { return event_fd(); } - -int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) { - auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>(); - if (!status) { - ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - *timestamp_ns = status.get(); - return 0; -} - -int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns, - uint32_t* next_vsync_count) { - if (!vsync_period_ns || !timestamp_ns || !next_vsync_count) - return -EINVAL; - - auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>(); - if (!status) { - ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s", - status.GetErrorMessage().c_str()); - return -status.error(); - } - - *vsync_period_ns = status.get().vsync_period_ns; - *timestamp_ns = status.get().timestamp_ns; - *next_vsync_count = status.get().next_vsync_count; - return 0; -} - -int VSyncClient::Acknowledge() { - auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>(); - ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s", - status.GetErrorMessage().c_str()); - return ReturnStatusOrError(status); -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp new file mode 100644 index 0000000000..43b1196fb3 --- /dev/null +++ b/libs/vr/libdisplay/vsync_service.cpp @@ -0,0 +1,146 @@ +#include "include/private/dvr/vsync_service.h" + +#include <binder/Parcel.h> +#include <log/log.h> + +namespace android { +namespace dvr { + +status_t BnVsyncCallback::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case ON_VSYNC: { + CHECK_INTERFACE(IVsyncCallback, data, reply); + int64_t vsync_timestamp = 0; + status_t result = data.readInt64(&vsync_timestamp); + if (result != NO_ERROR) { + ALOGE("onVsync failed to readInt64: %d", result); + return result; + } + onVsync(vsync_timestamp); + return NO_ERROR; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +class BpVsyncCallback : public BpInterface<IVsyncCallback> { +public: + explicit BpVsyncCallback(const sp<IBinder>& impl) + : BpInterface<IVsyncCallback>(impl) {} + virtual ~BpVsyncCallback() {} + + virtual status_t onVsync(int64_t vsync_timestamp) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncCallback::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("onVsync failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeInt64(vsync_timestamp); + if (result != NO_ERROR) { + ALOGE("onVsync failed to writeInt64: %d", result); + return result; + } + result = remote()->transact( + BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY); + if (result != NO_ERROR) { + ALOGE("onVsync failed to transact: %d", result); + return result; + } + return result; + } +}; + +IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback"); + + +status_t BnVsyncService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case REGISTER_CALLBACK: { + CHECK_INTERFACE(IVsyncService, data, reply); + sp<IBinder> callback; + status_t result = data.readStrongBinder(&callback); + if (result != NO_ERROR) { + ALOGE("registerCallback failed to readStrongBinder: %d", result); + return result; + } + registerCallback(interface_cast<IVsyncCallback>(callback)); + return NO_ERROR; + } + case UNREGISTER_CALLBACK: { + CHECK_INTERFACE(IVsyncService, data, reply); + sp<IBinder> callback; + status_t result = data.readStrongBinder(&callback); + if (result != NO_ERROR) { + ALOGE("unregisterCallback failed to readStrongBinder: %d", result); + return result; + } + unregisterCallback(interface_cast<IVsyncCallback>(callback)); + return NO_ERROR; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +class BpVsyncService : public BpInterface<IVsyncService> { +public: + explicit BpVsyncService(const sp<IBinder>& impl) + : BpInterface<IVsyncService>(impl) {} + virtual ~BpVsyncService() {} + + virtual status_t registerCallback(const sp<IVsyncCallback> callback) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncService::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("registerCallback failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(IInterface::asBinder(callback)); + if (result != NO_ERROR) { + ALOGE("registerCallback failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact( + BnVsyncService::REGISTER_CALLBACK, data, &reply); + if (result != NO_ERROR) { + ALOGE("registerCallback failed to transact: %d", result); + return result; + } + return result; + } + + virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) { + Parcel data, reply; + status_t result = data.writeInterfaceToken( + IVsyncService::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(IInterface::asBinder(callback)); + if (result != NO_ERROR) { + ALOGE("unregisterCallback failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact( + BnVsyncService::UNREGISTER_CALLBACK, data, &reply); + if (result != NO_ERROR) { + ALOGE("unregisterCallback failed to transact: %d", result); + return result; + } + return result; + } +}; + +IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService"); + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 16906f57cd..81a9b2d26a 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -19,7 +19,14 @@ cc_library_headers { vendor_available: true, } +cc_library_headers { + name: "libdvr_private_headers", + export_include_dirs: ["."], + vendor_available: false, +} + cflags = [ + "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"libdvr\"", "-DTRACE=0", "-Wall", @@ -36,7 +43,7 @@ srcs = [ "dvr_performance.cpp", "dvr_pose.cpp", "dvr_surface.cpp", - "dvr_vsync.cpp", + "dvr_tracking.cpp", ] static_libs = [ @@ -66,7 +73,7 @@ shared_libs = [ ] cc_library_shared { - name: "libdvr", + name: "libdvr.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], @@ -81,7 +88,7 @@ cc_library_shared { // restricting function access in the shared lib makes it inconvenient to use in // test code. cc_library_static { - name: "libdvr_static", + name: "libdvr_static.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], diff --git a/libs/vr/libdvr/dvr_api.cpp b/libs/vr/libdvr/dvr_api.cpp index d14f040f12..e099f6a699 100644 --- a/libs/vr/libdvr/dvr_api.cpp +++ b/libs/vr/libdvr/dvr_api.cpp @@ -12,6 +12,7 @@ #include <dvr/dvr_display_manager.h> #include <dvr/dvr_performance.h> #include <dvr/dvr_surface.h> +#include <dvr/dvr_tracking.h> #include <dvr/dvr_vsync.h> // Headers not yet moved into libdvr. diff --git a/libs/vr/libdvr/dvr_buffer_queue.cpp b/libs/vr/libdvr/dvr_buffer_queue.cpp index 571558a5bd..f4c6600469 100644 --- a/libs/vr/libdvr/dvr_buffer_queue.cpp +++ b/libs/vr/libdvr/dvr_buffer_queue.cpp @@ -9,7 +9,7 @@ using namespace android; using android::dvr::BufferConsumer; -using android::dvr::BufferHubBuffer; +using android::dvr::BufferHubBase; using android::dvr::BufferProducer; using android::dvr::ConsumerQueue; using android::dvr::ProducerQueue; @@ -439,7 +439,7 @@ void DvrReadBufferQueue::SetBufferRemovedCallback( consumer_queue_->SetBufferRemovedCallback(nullptr); } else { consumer_queue_->SetBufferRemovedCallback( - [callback, context](const std::shared_ptr<BufferHubBuffer>& buffer) { + [callback, context](const std::shared_ptr<BufferHubBase>& buffer) { // When buffer is removed from the queue, the slot is already invalid. auto read_buffer = std::make_unique<DvrReadBuffer>(); read_buffer->read_buffer = diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h index de8bb96aec..df8125a414 100644 --- a/libs/vr/libdvr/dvr_internal.h +++ b/libs/vr/libdvr/dvr_internal.h @@ -16,8 +16,11 @@ typedef struct DvrWriteBuffer DvrWriteBuffer; namespace android { namespace dvr { -class BufferProducer; -class BufferConsumer; +// TODO(b/116855254): Remove this typedef once rename is complete in libdvr. +// Note that the dvr::BufferProducer and dvr::BufferConsumer were poorly named, +// they should really be named as ProducerBuffer and ConsumerBuffer. +typedef class ProducerBuffer BufferProducer; +typedef class ConsumerBuffer BufferConsumer; class IonBuffer; DvrBuffer* CreateDvrBufferFromIonBuffer( diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp new file mode 100644 index 0000000000..73addc9a0c --- /dev/null +++ b/libs/vr/libdvr/dvr_tracking.cpp @@ -0,0 +1,82 @@ +#include "include/dvr/dvr_tracking.h" + +#include <utils/Errors.h> +#include <utils/Log.h> + +#if !DVR_TRACKING_IMPLEMENTED + +extern "C" { + +// This file provides the stub implementation of dvrTrackingXXX APIs. On +// platforms that implement these APIs, set -DDVR_TRACKING_IMPLEMENTED=1 in the +// build file. +int dvrTrackingCameraCreate(DvrTrackingCamera**) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingCameraDestroy(DvrTrackingCamera*) { + ALOGE("dvrTrackingCameraDestroy is not implemented."); +} + +int dvrTrackingCameraStart(DvrTrackingCamera*, DvrWriteBufferQueue*) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingCameraStop(DvrTrackingCamera*) { + ALOGE("dvrTrackingCameraCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorCreate(DvrTrackingFeatureExtractor**) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor*) { + ALOGE("dvrTrackingFeatureExtractorDestroy is not implemented."); +} + +int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor*, + DvrTrackingFeatureCallback, void*) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor*) { + ALOGE("dvrTrackingFeatureExtractorCreate is not implemented."); + return -ENOSYS; +} + +int dvrTrackingFeatureExtractorProcessBuffer(DvrTrackingFeatureExtractor*, + DvrReadBuffer*, + const DvrTrackingBufferMetadata*, + bool*) { + ALOGE("dvrTrackingFeatureExtractorProcessBuffer is not implemented."); + return -ENOSYS; +} + +int dvrTrackingSensorsCreate(DvrTrackingSensors**, const char*) { + ALOGE("dvrTrackingSensorsCreate is not implemented."); + return -ENOSYS; +} + +void dvrTrackingSensorsDestroy(DvrTrackingSensors*) { + ALOGE("dvrTrackingSensorsDestroy is not implemented."); +} + +int dvrTrackingSensorsStart(DvrTrackingSensors*, DvrTrackingSensorEventCallback, + void*) { + ALOGE("dvrTrackingStart is not implemented."); + return -ENOSYS; +} + +int dvrTrackingSensorsStop(DvrTrackingSensors*) { + ALOGE("dvrTrackingStop is not implemented."); + return -ENOSYS; +} + +} // extern "C" + +#endif // DVR_TRACKING_IMPLEMENTED diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp deleted file mode 100644 index 099240e53a..0000000000 --- a/libs/vr/libdvr/dvr_vsync.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "include/dvr/dvr_vsync.h" - -#include <utils/Log.h> - -#include <private/dvr/vsync_client.h> - -extern "C" { - -struct DvrVSyncClient { - std::unique_ptr<android::dvr::VSyncClient> client; -}; - -int dvrVSyncClientCreate(DvrVSyncClient** client_out) { - auto client = android::dvr::VSyncClient::Create(); - if (!client) { - ALOGE("dvrVSyncClientCreate: Failed to create vsync client!"); - return -EIO; - } - - *client_out = new DvrVSyncClient{std::move(client)}; - return 0; -} - -void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; } - -int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, - int64_t* next_timestamp_ns, - uint32_t* next_vsync_count) { - return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns, - next_vsync_count); -} - -} // extern "C" diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h index 80ffc82920..fef8512266 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api.h +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -10,6 +10,7 @@ #include <dvr/dvr_display_types.h> #include <dvr/dvr_hardware_composer_types.h> #include <dvr/dvr_pose.h> +#include <dvr/dvr_tracking_types.h> #ifdef __cplusplus extern "C" { @@ -50,6 +51,12 @@ typedef int32_t DvrGlobalBufferKey; typedef struct DvrSurfaceAttributeValue DvrSurfaceAttributeValue; typedef struct DvrSurfaceAttribute DvrSurfaceAttribute; +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrTrackingCamera DvrTrackingCamera; +typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; +typedef struct DvrTrackingSensors DvrTrackingSensors; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + // Note: To avoid breaking others during active development, only modify this // struct by appending elements to the end. // If you do feel we should to re-arrange or remove elements, please make a @@ -367,6 +374,38 @@ typedef DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame, typedef int (*DvrPerformanceSetSchedulerPolicyPtr)( pid_t task_id, const char* scheduler_policy); +// dvr_tracking.h +typedef int (*DvrTrackingCameraCreatePtr)(DvrTrackingCamera** out_camera); +typedef void (*DvrTrackingCameraDestroyPtr)(DvrTrackingCamera* camera); +typedef int (*DvrTrackingCameraStartPtr)(DvrTrackingCamera* camera, + DvrWriteBufferQueue* write_queue); +typedef int (*DvrTrackingCameraStopPtr)(DvrTrackingCamera* camera); + +typedef int (*DvrTrackingFeatureExtractorCreatePtr)( + DvrTrackingFeatureExtractor** out_extractor); +typedef void (*DvrTrackingFeatureExtractorDestroyPtr)( + DvrTrackingFeatureExtractor* extractor); +typedef void (*DvrTrackingFeatureCallback)(void* context, + const DvrTrackingFeatures* event); +typedef int (*DvrTrackingFeatureExtractorStartPtr)( + DvrTrackingFeatureExtractor* extractor, + DvrTrackingFeatureCallback callback, void* context); +typedef int (*DvrTrackingFeatureExtractorStopPtr)( + DvrTrackingFeatureExtractor* extractor); +typedef int (*DvrTrackingFeatureExtractorProcessBufferPtr)( + DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, + const DvrTrackingBufferMetadata* metadata, bool* out_skipped); + +typedef void (*DvrTrackingSensorEventCallback)(void* context, + DvrTrackingSensorEvent* event); +typedef int (*DvrTrackingSensorsCreatePtr)(DvrTrackingSensors** out_sensors, + const char* mode); +typedef void (*DvrTrackingSensorsDestroyPtr)(DvrTrackingSensors* sensors); +typedef int (*DvrTrackingSensorsStartPtr)( + DvrTrackingSensors* sensors, DvrTrackingSensorEventCallback callback, + void* context); +typedef int (*DvrTrackingSensorsStopPtr)(DvrTrackingSensors* sensors); + // The buffer metadata that an Android Surface (a.k.a. ANativeWindow) // will populate. A DvrWriteBufferQueue must be created with this metadata iff // ANativeWindow access is needed. Please do not remove, modify, or reorder diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h index f0d8ec6d24..3006b61b81 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h +++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h @@ -85,9 +85,9 @@ DVR_V1_API_ENTRY(ReadBufferQueueSetBufferRemovedCallback); DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents); // V-Sync client -DVR_V1_API_ENTRY(VSyncClientCreate); -DVR_V1_API_ENTRY(VSyncClientDestroy); -DVR_V1_API_ENTRY(VSyncClientGetSchedInfo); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy); +DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo); // Display surface DVR_V1_API_ENTRY(SurfaceCreate); @@ -181,3 +181,20 @@ DVR_V1_API_ENTRY(ReadBufferQueueReleaseBuffer); DVR_V1_API_ENTRY(PoseClientGetDataReader); DVR_V1_API_ENTRY(PoseClientDataCapture); DVR_V1_API_ENTRY(PoseClientDataReaderDestroy); + +// Tracking +DVR_V1_API_ENTRY(TrackingCameraCreate); +DVR_V1_API_ENTRY(TrackingCameraDestroy); +DVR_V1_API_ENTRY(TrackingCameraStart); +DVR_V1_API_ENTRY(TrackingCameraStop); + +DVR_V1_API_ENTRY(TrackingFeatureExtractorCreate); +DVR_V1_API_ENTRY(TrackingFeatureExtractorDestroy); +DVR_V1_API_ENTRY(TrackingFeatureExtractorStart); +DVR_V1_API_ENTRY(TrackingFeatureExtractorStop); +DVR_V1_API_ENTRY(TrackingFeatureExtractorProcessBuffer); + +DVR_V1_API_ENTRY(TrackingSensorsCreate); +DVR_V1_API_ENTRY(TrackingSensorsDestroy); +DVR_V1_API_ENTRY(TrackingSensorsStart); +DVR_V1_API_ENTRY(TrackingSensorsStop); diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h index 943384f802..fe59d1ffba 100644 --- a/libs/vr/libdvr/include/dvr/dvr_deleter.h +++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h @@ -20,7 +20,6 @@ typedef struct DvrSurfaceState DvrSurfaceState; typedef struct DvrSurface DvrSurface; typedef struct DvrHwcClient DvrHwcClient; typedef struct DvrHwcFrame DvrHwcFrame; -typedef struct DvrVSyncClient DvrVSyncClient; void dvrBufferDestroy(DvrBuffer* buffer); void dvrReadBufferDestroy(DvrReadBuffer* read_buffer); @@ -32,7 +31,6 @@ void dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); void dvrSurfaceDestroy(DvrSurface* surface); void dvrHwcClientDestroy(DvrHwcClient* client); void dvrHwcFrameDestroy(DvrHwcFrame* frame); -void dvrVSyncClientDestroy(DvrVSyncClient* client); __END_DECLS @@ -55,7 +53,6 @@ struct DvrObjectDeleter { void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); } void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); } void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); } - void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); } }; // Helper to define unique pointers for DVR object types. @@ -73,7 +70,6 @@ using UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>; using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>; using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>; using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>; -using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>; // TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into // the relevant constructors. diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking.h b/libs/vr/libdvr/include/dvr/dvr_tracking.h new file mode 100644 index 0000000000..5e388f391a --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_tracking.h @@ -0,0 +1,185 @@ +#ifndef ANDROID_DVR_TRACKING_H_ +#define ANDROID_DVR_TRACKING_H_ + +#include <stdint.h> +#include <sys/cdefs.h> + +#include <dvr/dvr_tracking_types.h> + +__BEGIN_DECLS + +typedef struct DvrReadBuffer DvrReadBuffer; +typedef struct DvrTrackingCamera DvrTrackingCamera; +typedef struct DvrTrackingFeatureExtractor DvrTrackingFeatureExtractor; +typedef struct DvrTrackingSensors DvrTrackingSensors; +typedef struct DvrWriteBufferQueue DvrWriteBufferQueue; + +// The callback for DvrTrackingFeatureExtractor that will deliver the feature +// events. This callback is passed to dvrTrackingFeatureExtractorStart. +typedef void (*DvrTrackingFeatureCallback)(void* context, + const DvrTrackingFeatures* event); + +// The callback for DvrTrackingSensors session that will deliver the events. +// This callback is passed to dvrTrackingSensorsStart. +typedef void (*DvrTrackingSensorEventCallback)(void* context, + DvrTrackingSensorEvent* event); + +// Creates a DvrTrackingCamera session. +// +// On creation, the session is not in operating mode. Client code must call +// dvrTrackingCameraStart to bootstrap the underlying camera stack. +// +// There is no plan to expose camera configuration through this API. All camera +// parameters are determined by the system optimized for better tracking +// results. See b/78662281 for detailed deprecation plan of this API and the +// Stage 2 of VR tracking data source refactoring. +// +// @param out_camera The pointer of a DvrTrackingCamera will be filled here if +// the method call succeeds. +// @return Zero on success, or negative error code. +int dvrTrackingCameraCreate(DvrTrackingCamera** out_camera); + +// Destroys a DvrTrackingCamera handle. +// +// @param camera The DvrTrackingCamera of interest. +void dvrTrackingCameraDestroy(DvrTrackingCamera* camera); + +// Starts the DvrTrackingCamera. +// +// On successful return, all DvrReadBufferQueue's associated with the given +// write_queue will start to receive buffers from the camera stack. Note that +// clients of this API should not assume the buffer dimension, format, and/or +// usage of the outcoming buffers, as they are governed by the underlying camera +// logic. Also note that it's the client's responsibility to consume buffers +// from DvrReadBufferQueue on time and return them back to the producer; +// otherwise the camera stack might be blocked. +// +// @param camera The DvrTrackingCamera of interest. +// @param write_queue A DvrWriteBufferQueue that the camera stack can use to +// populate the buffer into. The queue must be empty and the camera stack +// will request buffer allocation with proper buffer dimension, format, and +// usage. Note that the write queue must be created with user_metadata_size +// set to sizeof(DvrTrackingBufferMetadata). On success, the write_queue +// handle will become invalid and the ownership of the queue handle will be +// transferred into the camera; otherwise, the write_queue handle will keep +// untouched and the caller still has the ownership. +// @return Zero on success, or negative error code. +int dvrTrackingCameraStart(DvrTrackingCamera* camera, + DvrWriteBufferQueue* write_queue); + +// Stops the DvrTrackingCamera. +// +// On successful return, the DvrWriteBufferQueue set during +// dvrTrackingCameraStart will stop getting new buffers from the camera stack. +// +// @param camera The DvrTrackingCamera of interest. +// @return Zero on success, or negative error code. +int dvrTrackingCameraStop(DvrTrackingCamera* camera); + +// Creates a DvrTrackingSensors session. +// +// This will initialize but not start device sensors (gyro / accel). Upon +// successfull creation, the clients can call dvrTrackingSensorsStart to start +// receiving sensor events. +// +// @param out_sensors The pointer of a DvrTrackingSensors will be filled here if +// the method call succeeds. +// @param mode The sensor mode. +// mode="ndk": Use the Android NDK. +// mode="direct": Use direct mode sensors (lower latency). +// @return Zero on success, or negative error code. +int dvrTrackingSensorsCreate(DvrTrackingSensors** out_sensors, + const char* mode); + +// Destroys a DvrTrackingSensors session. +// +// @param sensors The DvrTrackingSensors struct to destroy. +void dvrTrackingSensorsDestroy(DvrTrackingSensors* sensors); + +// Starts the tracking sensor session. +// +// This will start the device sensors and start pumping the feature and sensor +// events as they arrive. +// +// @param client A tracking client created by dvrTrackingSensorsCreate. +// @param context A client supplied pointer that will be passed to the callback. +// @param callback A callback that will receive the sensor events on an +// arbitrary thread. +// @return Zero on success, or negative error code. +int dvrTrackingSensorsStart(DvrTrackingSensors* sensors, + DvrTrackingSensorEventCallback callback, + void* context); + +// Stops a DvrTrackingSensors session. +// +// This will stop the device sensors. dvrTrackingSensorsStart can be called to +// restart them again. +// +// @param client A tracking client created by dvrTrackingClientCreate. +// @return Zero on success, or negative error code. +int dvrTrackingSensorsStop(DvrTrackingSensors* sensors); + +// Creates a tracking feature extractor. +// +// This will initialize but not start the feature extraction session. Upon +// successful creation, the client can call dvrTrackingFeatureExtractorStart to +// start receiving features. +// +// @param out_extractor The pointer of a DvrTrackingFeatureExtractor will be +// filled here if the method call succeeds. +int dvrTrackingFeatureExtractorCreate( + DvrTrackingFeatureExtractor** out_extractor); + +// Destroys a tracking feature extractor. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +void dvrTrackingFeatureExtractorDestroy(DvrTrackingFeatureExtractor* extractor); + +// Starts the tracking feature extractor. +// +// This will start the extractor and start pumping the output feature events to +// the registered callback. Note that this method will create one or more +// threads to handle feature processing. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +int dvrTrackingFeatureExtractorStart(DvrTrackingFeatureExtractor* extractor, + DvrTrackingFeatureCallback callback, + void* context); + +// Stops the tracking feature extractor. +// +// This will stop the extractor session and clean up all internal resourcse +// related to this extractor. On succssful return, all internal therad started +// by dvrTrackingFeatureExtractorStart should be stopped. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +int dvrTrackingFeatureExtractorStop(DvrTrackingFeatureExtractor* extractor); + +// Processes one buffer to extract features from. +// +// The buffer will be sent over to DSP for feature extraction. Once the process +// is done, the processing thread will invoke DvrTrackingFeatureCallback with +// newly extracted features. Note that not all buffers will be processed, as the +// underlying DSP can only process buffers at a certain framerate. If a buffer +// needs to be skipped, out_skipped filed will be set to true. Also note that +// for successfully processed stereo buffer, two callbacks (one for each eye) +// will be fired. +// +// @param extractor The DvrTrackingFeatureExtractor to destroy. +// @param buffer The buffer to extract features from. Note that the buffer must +// be in acquired state for the buffer to be processed. Also note that the +// buffer will be released back to its producer on successful return of the +// method. +// @param metadata The metadata associated with the buffer. Should be populated +// by DvrTrackingCamera session as user defined metadata. +// @param out_skipped On successful return, the field will be set to true iff +// the buffer was skipped; and false iff the buffer was processed. This +// field is optional and nullptr can be passed here to ignore the field. +// @return Zero on success, or negative error code. +int dvrTrackingFeatureExtractorProcessBuffer( + DvrTrackingFeatureExtractor* extractor, DvrReadBuffer* buffer, + const DvrTrackingBufferMetadata* metadata, bool* out_skipped); + +__END_DECLS + +#endif // ANDROID_DVR_TRACKING_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_tracking_types.h b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h new file mode 100644 index 0000000000..81310d2303 --- /dev/null +++ b/libs/vr/libdvr/include/dvr/dvr_tracking_types.h @@ -0,0 +1,104 @@ +#ifndef ANDROID_DVR_TRACKING_TYPES_H_ +#define ANDROID_DVR_TRACKING_TYPES_H_ + +#include <stdint.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +typedef struct DvrTrackingBufferMetadata { + // Specifies the source of this image. + uint32_t camera_mask; + // Specifies the memory format of this image. + uint32_t format; + /// The width of the image data. + uint32_t width; + /// The height of the image data. + uint32_t height; + /// The number of bytes per scanline of image data. + uint32_t stride; + /// The frame number of this image. + int32_t frame_number; + /// The timestamp of this image in nanoseconds. Taken in the middle of the + /// exposure interval. + int64_t timestamp_ns; + // This is the timestamp for recording when the system using the HAL + // received the callback. It will not be populated by the HAL. + int64_t callback_timestamp_ns; + /// The exposure duration of this image in nanoseconds. + int64_t exposure_duration_ns; +} DvrTrackingBufferMetadata; + +// Represents a set of features extracted from a camera frame. Note that this +// should be in sync with TangoHalCallbacks defined in tango-hal.h. +typedef struct DvrTrackingFeatures { + // Specifies the source of the features. + uint32_t camera_mask; + + // This is unused. + uint32_t unused; + + // The timestamp in nanoseconds from the image that generated the features. + // Taken in the middle of the exposure interval. + int64_t timestamp_ns; + + // This is the timestamp for recording when the system using the HAL + // received the callback. It will not be populated by the HAL. + int64_t callback_timestamp_ns; + + // The frame number from the image that generated the features. + int64_t frame_number; + + // The number of features. + int count; + + // An array of 2D image points for each feature in the current image. + // This is sub-pixel refined extremum location at the fine resolution. + float (*positions)[2]; + + // The id of these measurements. + int32_t* ids; + + // The feature descriptors. + uint64_t (*descriptors)[8]; + + // Laplacian scores for each feature. + float* scores; + + // Is this feature a minimum or maximum in the Laplacian image. + // 0 if the feature is a maximum, 1 if it is a minimum. + int32_t* is_minimum; + + // This corresponds to the sub-pixel index of the laplacian image + // that the extremum was found. + float* scales; + + // Computed orientation of keypoint as part of FREAK extraction, except + // it's represented in radians and measured anti-clockwise. + float* angles; + + // Edge scores for each feature. + float* edge_scores; +} DvrTrackingFeatures; + +// Represents a sensor event. +typedef struct DvrTrackingSensorEvent { + // The sensor type. + int32_t sensor; + + // Event type. + int32_t type; + + // This is the timestamp recorded from the device. Taken in the middle + // of the integration interval and adjusted for any low pass filtering. + int64_t timestamp_ns; + + // The event data. + float x; + float y; + float z; +} DvrTrackingSensorEvent; + +__END_DECLS + +#endif // ANDROID_DVR_TRACKING_TYPES_H_ diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h index 87fdf31b2b..498bb5cc6e 100644 --- a/libs/vr/libdvr/include/dvr/dvr_vsync.h +++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h @@ -6,8 +6,6 @@ __BEGIN_DECLS -typedef struct DvrVSyncClient DvrVSyncClient; - // Represents a vsync sample. The size of this struct is 32 bytes. typedef struct __attribute__((packed, aligned(16))) DvrVsync { // The timestamp for the last vsync in nanoseconds. @@ -29,19 +27,6 @@ typedef struct __attribute__((packed, aligned(16))) DvrVsync { uint8_t padding[8]; } DvrVsync; -// Creates a new client to the system vsync service. -int dvrVSyncClientCreate(DvrVSyncClient** client_out); - -// Destroys the vsync client. -void dvrVSyncClientDestroy(DvrVSyncClient* client); - -// Get the estimated timestamp of the next GPU lens warp preemption event in/ -// ns. Also returns the corresponding vsync count that the next lens warp -// operation will target. -int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns, - int64_t* next_timestamp_ns, - uint32_t* next_vsync_count); - __END_DECLS #endif // ANDROID_DVR_VSYNC_H_ diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp index 1ae75fbe04..357dffe193 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -12,38 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -shared_libraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", -] - -static_libraries = [ - "libdvr_static", - "libchrome", - "libdvrcommon", - "libdisplay", - "libbroadcastring", -] - cc_test { srcs: [ "dvr_display_manager-test.cpp", "dvr_named_buffer-test.cpp", + "dvr_tracking-test.cpp", ], header_libs: ["libdvr_headers"], - static_libs: static_libraries, - shared_libs: shared_libraries, + static_libs: [ + "libdvr_static.google", + "libchrome", + "libdvrcommon", + "libdisplay", + "libbroadcastring", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "liblog", + "libhardware", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", + ], cflags: [ + "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", "-Wno-missing-field-initializers", @@ -51,4 +49,59 @@ cc_test { "-g", ], name: "dvr_api-test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + +cc_test { + name: "dvr_buffer_queue-test", + + // Includes the dvr_api.h header. Tests should only include "dvr_api.h", + // and shall only get access to |dvrGetApi|, as other symbols are hidden + // from the library. + include_dirs: ["frameworks/native/libs/vr/libdvr/include"], + + srcs: ["dvr_buffer_queue-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", +} + +cc_test { + name: "dvr_display-test", + + include_dirs: [ + "frameworks/native/libs/vr/libdvr/include", + "frameworks/native/libs/nativewindow/include", + ], + + srcs: ["dvr_display-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", } diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk deleted file mode 100644 index 0f3840d52c..0000000000 --- a/libs/vr/libdvr/tests/Android.mk +++ /dev/null @@ -1,73 +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. - -LOCAL_PATH:= $(call my-dir) - -# TODO(b/73133405): Currently, building cc_test against NDK using Android.bp -# doesn't work well. Migrate to use Android.bp once b/73133405 gets fixed. - -include $(CLEAR_VARS) -LOCAL_MODULE:= dvr_buffer_queue-test - -# Includes the dvr_api.h header. Tests should only include "dvr_api.h", -# and shall only get access to |dvrGetApi|, as other symbols are hidden from the -# library. -LOCAL_C_INCLUDES := \ - frameworks/native/libs/vr/libdvr/include \ - -LOCAL_SANITIZE := thread - -LOCAL_SRC_FILES := dvr_buffer_queue-test.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid \ - liblog \ - -LOCAL_CFLAGS := \ - -DTRACE=0 \ - -O2 \ - -g \ - -# DTS Should only link to NDK libraries. -LOCAL_SDK_VERSION := 26 -LOCAL_NDK_STL_VARIANT := c++_static - -include $(BUILD_NATIVE_TEST) - - -include $(CLEAR_VARS) -LOCAL_MODULE:= dvr_display-test - -LOCAL_C_INCLUDES := \ - frameworks/native/libs/vr/libdvr/include \ - frameworks/native/libs/nativewindow/include - -LOCAL_SANITIZE := thread - -LOCAL_SRC_FILES := dvr_display-test.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid \ - liblog - -LOCAL_CFLAGS := \ - -DTRACE=0 \ - -O2 \ - -g - -# DTS Should only link to NDK libraries. -LOCAL_SDK_VERSION := 26 -LOCAL_NDK_STL_VARIANT := c++_static - -include $(BUILD_NATIVE_TEST)
\ No newline at end of file diff --git a/libs/vr/libdvr/tests/dvr_api_test.h b/libs/vr/libdvr/tests/dvr_api_test.h index d8359e78a8..5d2ec285eb 100644 --- a/libs/vr/libdvr/tests/dvr_api_test.h +++ b/libs/vr/libdvr/tests/dvr_api_test.h @@ -14,7 +14,7 @@ class DvrApiTest : public ::testing::Test { // workaround for an Android NDK bug. See more detail: // https://github.com/android-ndk/ndk/issues/360 flags |= RTLD_NODELETE; - platform_handle_ = dlopen("libdvr.so", flags); + platform_handle_ = dlopen("libdvr.google.so", flags); ASSERT_NE(nullptr, platform_handle_) << "Dvr shared library missing."; auto dvr_get_api = reinterpret_cast<decltype(&dvrGetApi)>( diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp new file mode 100644 index 0000000000..3b6d6e1ec4 --- /dev/null +++ b/libs/vr/libdvr/tests/dvr_tracking-test.cpp @@ -0,0 +1,103 @@ +#include <android/log.h> +#include <gtest/gtest.h> + +#include "dvr_api_test.h" + +namespace { + +class DvrTrackingTest : public DvrApiTest {}; + +#if DVR_TRACKING_IMPLEMENTED + +TEST_F(DvrTrackingTest, Implemented) { + ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); + ASSERT_TRUE(api_.TrackingCameraStart != nullptr); + ASSERT_TRUE(api_.TrackingCameraStop != nullptr); + + ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); +} + +TEST_F(DvrTrackingTest, CameraCreateFailsForInvalidInput) { + int ret; + ret = api_.TrackingCameraCreate(nullptr); + EXPECT_EQ(ret, -EINVAL); + + DvrTrackingCamera* camera = reinterpret_cast<DvrTrackingCamera*>(42); + ret = api_.TrackingCameraCreate(&camera); + EXPECT_EQ(ret, -EINVAL); +} + +TEST_F(DvrTrackingTest, CameraCreateDestroy) { + DvrTrackingCamera* camera = nullptr; + int ret = api_.TrackingCameraCreate(&camera); + + EXPECT_EQ(ret, 0); + ASSERT_TRUE(camera != nullptr); + + api_.TrackingCameraDestroy(camera); +} + +TEST_F(DvrTrackingTest, FeatureExtractorCreateFailsForInvalidInput) { + int ret; + ret = api_.TrackingFeatureExtractorCreate(nullptr); + EXPECT_EQ(ret, -EINVAL); + + DvrTrackingFeatureExtractor* camera = + reinterpret_cast<DvrTrackingFeatureExtractor*>(42); + ret = api_.TrackingFeatureExtractorCreate(&camera); + EXPECT_EQ(ret, -EINVAL); +} + +TEST_F(DvrTrackingTest, FeatureExtractorCreateDestroy) { + DvrTrackingFeatureExtractor* camera = nullptr; + int ret = api_.TrackingFeatureExtractorCreate(&camera); + + EXPECT_EQ(ret, 0); + ASSERT_TRUE(camera != nullptr); + + api_.TrackingFeatureExtractorDestroy(camera); +} + +#else // !DVR_TRACKING_IMPLEMENTED + +TEST_F(DvrTrackingTest, NotImplemented) { + ASSERT_TRUE(api_.TrackingCameraCreate != nullptr); + ASSERT_TRUE(api_.TrackingCameraDestroy != nullptr); + ASSERT_TRUE(api_.TrackingCameraStart != nullptr); + ASSERT_TRUE(api_.TrackingCameraStop != nullptr); + + EXPECT_EQ(api_.TrackingCameraCreate(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingCameraStart(nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingCameraStop(nullptr), -ENOSYS); + + ASSERT_TRUE(api_.TrackingFeatureExtractorCreate != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorDestroy != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStart != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorStop != nullptr); + ASSERT_TRUE(api_.TrackingFeatureExtractorProcessBuffer != nullptr); + + EXPECT_EQ(api_.TrackingFeatureExtractorCreate(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorStart(nullptr, nullptr, nullptr), + -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorStop(nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingFeatureExtractorProcessBuffer(nullptr, nullptr, + nullptr, nullptr), + -ENOSYS); + + ASSERT_TRUE(api_.TrackingSensorsCreate != nullptr); + ASSERT_TRUE(api_.TrackingSensorsDestroy != nullptr); + ASSERT_TRUE(api_.TrackingSensorsStart != nullptr); + ASSERT_TRUE(api_.TrackingSensorsStop != nullptr); + + EXPECT_EQ(api_.TrackingSensorsCreate(nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingSensorsStart(nullptr, nullptr, nullptr), -ENOSYS); + EXPECT_EQ(api_.TrackingSensorsStop(nullptr), -ENOSYS); +} + +#endif // DVR_TRACKING_IMPLEMENTED + +} // namespace diff --git a/libs/vr/libpdx/private/pdx/service.h b/libs/vr/libpdx/private/pdx/service.h index 234b24afe4..d38b174184 100644 --- a/libs/vr/libpdx/private/pdx/service.h +++ b/libs/vr/libpdx/private/pdx/service.h @@ -59,9 +59,18 @@ class Channel : public std::enable_shared_from_this<Channel> { virtual ~Channel() {} /* + * Accessors to the pid of the last active client. + */ + pid_t GetActiveProcessId() const { return client_pid_; } + void SetActiveProcessId(pid_t pid) { client_pid_ = pid; } + + /* * Utility to get a shared_ptr reference from the channel context pointer. */ static std::shared_ptr<Channel> GetFromMessageInfo(const MessageInfo& info); + + private: + pid_t client_pid_ = 0; }; /* diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp index 32d40e8371..ecbfdba7c4 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -521,6 +521,9 @@ Status<void> Endpoint::ReceiveMessageForChannel( info.flags = 0; info.service = service_; info.channel = GetChannelState(channel_id); + if (info.channel != nullptr) { + info.channel->SetActiveProcessId(request.cred.pid); + } info.send_len = request.send_len; info.recv_len = request.max_recv_len; info.fd_count = request.file_descriptors.size(); diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index 4dc669b339..07904fb4cf 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -20,7 +20,6 @@ sourceFiles = [ "display_surface.cpp", "hardware_composer.cpp", "vr_flinger.cpp", - "vsync_service.cpp", ] includeFiles = [ "include" ] @@ -40,6 +39,7 @@ sharedLibraries = [ "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", "libbinder", "libbase", "libbufferhubqueue", @@ -64,6 +64,7 @@ sharedLibraries = [ 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", "libdvr_headers", "libsurfaceflinger_headers", ] @@ -92,3 +93,7 @@ cc_library_static { header_libs: headerLibraries, name: "libvrflinger", } + +subdirs = [ + "tests", +] diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h index 3090bd1e7a..6fad58ef6f 100644 --- a/libs/vr/libvrflinger/display_service.h +++ b/libs/vr/libvrflinger/display_service.h @@ -60,11 +60,6 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { void SetDisplayConfigurationUpdateNotifier( DisplayConfigurationUpdateNotifier notifier); - using VSyncCallback = HardwareComposer::VSyncCallback; - void SetVSyncCallback(VSyncCallback callback) { - hardware_composer_.SetVSyncCallback(callback); - } - void GrantDisplayOwnership() { hardware_composer_.Enable(); } void SeizeDisplayOwnership() { hardware_composer_.Disable(); } void OnBootFinished() { hardware_composer_.OnBootFinished(); } diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index b8d5e2ba97..5f6455ca95 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -1,5 +1,6 @@ #include "hardware_composer.h" +#include <binder/IServiceManager.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <fcntl.h> @@ -52,6 +53,10 @@ const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display"; +// 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; @@ -131,6 +136,7 @@ HardwareComposer::~HardwareComposer(void) { UpdatePostThreadState(PostThreadState::Quit, true); if (post_thread_.joinable()) post_thread_.join(); + composer_callback_->SetVsyncService(nullptr); } bool HardwareComposer::Initialize( @@ -147,6 +153,13 @@ bool HardwareComposer::Initialize( 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_, @@ -223,6 +236,7 @@ void HardwareComposer::CreateComposer() { 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() { @@ -242,7 +256,10 @@ void HardwareComposer::OnPostThreadPaused() { // Standalones only create the composer client once and then use SetPowerMode // to control the screen on pause/resume. if (!is_standalone_device_) { - composer_callback_ = nullptr; + if (composer_callback_ != nullptr) { + composer_callback_->SetVsyncService(nullptr); + composer_callback_ = nullptr; + } composer_.reset(nullptr); } else { EnableDisplay(*target_display_, false); @@ -336,7 +353,6 @@ HWC::Error HardwareComposer::Present(hwc2_display_t display) { // According to the documentation, this fence is signaled at the time of // vsync/DMA for physical displays. if (error == HWC::Error::None) { - ATRACE_INT("HardwareComposer: VsyncFence", present_fence); retire_fence_fds_.emplace_back(present_fence); } else { ATRACE_INT("HardwareComposer: PresentResult", error); @@ -775,6 +791,11 @@ void HardwareComposer::PostThread() { 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; @@ -848,6 +869,9 @@ void HardwareComposer::PostThread() { 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) @@ -867,11 +891,6 @@ void HardwareComposer::PostThread() { vsync_ring_->Publish(vsync); } - // Signal all of the vsync clients. Because absolute time is used for the - // wakeup time below, this can take a little time if necessary. - if (vsync_callback_) - vsync_callback_(vsync_timestamp, /*frame_time_estimate*/ 0, vsync_count_); - { // Sleep until shortly before vsync. ATRACE_NAME("sleep"); @@ -1063,8 +1082,45 @@ void HardwareComposer::UpdateLayerConfig() { layers_.size()); } -void HardwareComposer::SetVSyncCallback(VSyncCallback callback) { - vsync_callback_ = callback; +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 NO_ERROR; +} + +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 NO_ERROR; +} + +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( @@ -1123,16 +1179,26 @@ Return<void> HardwareComposer::ComposerCallback::onRefresh( 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) { - TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|", - display, timestamp); display_info->callback_vsync_timestamp = timestamp; } + if (primary_display_.id == display && vsync_service_ != nullptr) { + vsync_service_->OnVsync(timestamp); + } 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_); @@ -1149,6 +1215,7 @@ HardwareComposer::ComposerCallback::GetDisplays() { 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); @@ -1160,7 +1227,6 @@ Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime( if (!event_fd) { // Fall back to returning the last timestamp returned by the vsync // callback. - std::lock_guard<std::mutex> autolock(mutex_); return display_info->callback_vsync_timestamp; } diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h index 539a7fb6d7..94a23376bc 100644 --- a/libs/vr/libvrflinger/hardware_composer.h +++ b/libs/vr/libvrflinger/hardware_composer.h @@ -24,6 +24,7 @@ #include <pdx/rpc/variant.h> #include <private/dvr/buffer_hub_client.h> #include <private/dvr/shared_buffer_helpers.h> +#include <private/dvr/vsync_service.h> #include "acquired_buffer.h" #include "display_surface.h" @@ -300,8 +301,6 @@ class Layer { // will access the state and whether it needs to be synchronized. class HardwareComposer { public: - // Type for vsync callback. - using VSyncCallback = std::function<void(int64_t, int64_t, uint32_t)>; using RequestDisplayCallback = std::function<void(bool)>; HardwareComposer(); @@ -325,8 +324,6 @@ class HardwareComposer { std::string Dump(); - void SetVSyncCallback(VSyncCallback callback); - const DisplayParams& GetPrimaryDisplayParams() const { return primary_display_; } @@ -350,6 +347,18 @@ class HardwareComposer { // 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; @@ -360,6 +369,7 @@ class HardwareComposer { int64_t timestamp) override; bool GotFirstHotplug() { return got_first_hotplug_; } + void SetVsyncService(const sp<VsyncService>& vsync_service); struct Displays { hwc2_display_t primary_display = 0; @@ -385,6 +395,7 @@ class HardwareComposer { 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); @@ -484,9 +495,6 @@ class HardwareComposer { // vector must be sorted by surface_id in ascending order. std::vector<Layer> layers_; - // Handler to hook vsync events outside of this class. - VSyncCallback vsync_callback_; - // The layer posting thread. This thread wakes up a short time before vsync to // hand buffers to hardware composer. std::thread post_thread_; @@ -534,6 +542,9 @@ class HardwareComposer { DvrConfig post_thread_config_; std::mutex shared_config_mutex_; + bool vsync_trace_parity_ = false; + sp<VsyncService> vsync_service_; + static constexpr int kPostThreadInterrupted = 1; HardwareComposer(const HardwareComposer&) = delete; diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp new file mode 100644 index 0000000000..9a1d2e5828 --- /dev/null +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -0,0 +1,40 @@ +shared_libs = [ + "android.hardware.configstore-utils", + "android.hardware.configstore@1.0", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "libhidlbase", + "liblog", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", +] + +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", + ], + cppflags: ["-std=c++1z"], + name: "vrflinger_test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp new file mode 100644 index 0000000000..1d5740f217 --- /dev/null +++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp @@ -0,0 +1,261 @@ +#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); + +// How long to wait for a device that boots to VR to have vr flinger ready. +constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30); + +// 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 != NO_ERROR) { + return std::nullopt; + } + result = IInterface::asBinder(surface_flinger_) + ->transact(kIsVrFlingerActiveTransactionCode, data, &reply); + if (result != NO_ERROR) { + return std::nullopt; + } + bool vr_flinger_active; + result = reply.readBool(&vr_flinger_active); + if (result != NO_ERROR) { + 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 = + getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>( + false); + if (!vr_flinger_enabled) { + return; + } + + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + 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()->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()->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); +} + +// This test runs only on devices that boot to vr. Such a device should boot to +// a state where vr flinger is running, and the test verifies this after a +// delay. +TEST(BootVrFlingerTest, BootsToVrFlinger) { + // Exit if we are not running on a device that boots to vr. + if (!property_get_bool("ro.boot.vr", false)) { + return; + } + + auto surface_flinger_connection = SurfaceFlingerConnection::Create(); + ASSERT_NE(surface_flinger_connection, nullptr); + + // Verify that vr flinger is enabled. + ASSERT_TRUE(surface_flinger_connection->IsAlive()); + auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive(); + ASSERT_TRUE(vr_flinger_active.has_value()); + + bool active_value = vr_flinger_active.value(); + if (!active_value) { + // Try again, but delay up to 30 seconds. + ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true, + kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout), + 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 new file mode 100644 index 0000000000..030bb7b67c --- /dev/null +++ b/libs/vr/libvrflinger/tests/vrflinger_test.filter @@ -0,0 +1,5 @@ +{ + "presubmit": { + "filter": "BootVrFlingerTest.*" + } +} diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp index 26aed4f25f..b57383ad44 100644 --- a/libs/vr/libvrflinger/vr_flinger.cpp +++ b/libs/vr/libvrflinger/vr_flinger.cpp @@ -23,7 +23,6 @@ #include "DisplayHardware/ComposerHal.h" #include "display_manager_service.h" #include "display_service.h" -#include "vsync_service.h" namespace android { namespace dvr { @@ -85,16 +84,6 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, CHECK_ERROR(!service, error, "Failed to create display manager service."); dispatcher_->AddService(service); - service = android::dvr::VSyncService::Create(); - CHECK_ERROR(!service, error, "Failed to create vsync service."); - dispatcher_->AddService(service); - - display_service_->SetVSyncCallback( - std::bind(&android::dvr::VSyncService::VSyncEvent, - std::static_pointer_cast<android::dvr::VSyncService>(service), - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3)); - dispatcher_thread_ = std::thread([this]() { prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0); ALOGI("Entering message loop."); diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp deleted file mode 100644 index b8d8b08b5f..0000000000 --- a/libs/vr/libvrflinger/vsync_service.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include "vsync_service.h" - -#include <hardware/hwcomposer.h> -#include <log/log.h> -#include <poll.h> -#include <sys/prctl.h> -#include <time.h> -#include <utils/Trace.h> - -#include <dvr/dvr_display_types.h> -#include <pdx/default_transport/service_endpoint.h> -#include <private/dvr/clock_ns.h> -#include <private/dvr/display_protocol.h> - -using android::dvr::display::VSyncProtocol; -using android::dvr::display::VSyncSchedInfo; -using android::pdx::Channel; -using android::pdx::Message; -using android::pdx::MessageInfo; -using android::pdx::default_transport::Endpoint; -using android::pdx::rpc::DispatchRemoteMethod; - -namespace android { -namespace dvr { - -VSyncService::VSyncService() - : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)), - last_vsync_(0), - current_vsync_(0), - compositor_time_ns_(0), - current_vsync_count_(0) {} - -VSyncService::~VSyncService() {} - -void VSyncService::VSyncEvent(int64_t timestamp_ns, - int64_t compositor_time_ns, - uint32_t vsync_count) { - ATRACE_NAME("VSyncService::VSyncEvent"); - std::lock_guard<std::mutex> autolock(mutex_); - - last_vsync_ = current_vsync_; - current_vsync_ = timestamp_ns; - compositor_time_ns_ = compositor_time_ns; - current_vsync_count_ = vsync_count; - - NotifyWaiters(); - UpdateClients(); -} - -std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) { - const MessageInfo& info = message.GetInfo(); - - auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid); - AddClient(client); - - return client; -} - -void VSyncService::OnChannelClose(pdx::Message& /*message*/, - const std::shared_ptr<Channel>& channel) { - auto client = std::static_pointer_cast<VSyncChannel>(channel); - if (!client) { - ALOGW("WARNING: VSyncChannel was NULL!!!\n"); - return; - } - - RemoveClient(client); -} - -void VSyncService::AddWaiter(pdx::Message& message) { - std::lock_guard<std::mutex> autolock(mutex_); - std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message)); - waiters_.push_back(std::move(waiter)); -} - -void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) { - std::lock_guard<std::mutex> autolock(mutex_); - clients_.push_back(client); -} - -void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) { - std::lock_guard<std::mutex> autolock(mutex_); - clients_.remove(client); -} - -// Private. Assumes mutex is held. -void VSyncService::NotifyWaiters() { - ATRACE_NAME("VSyncService::NotifyWaiters"); - auto first = waiters_.begin(); - auto last = waiters_.end(); - - while (first != last) { - (*first)->Notify(current_vsync_); - waiters_.erase(first++); - } -} - -// Private. Assumes mutex is held. -void VSyncService::UpdateClients() { - ATRACE_NAME("VSyncService::UpdateClients"); - auto first = clients_.begin(); - auto last = clients_.end(); - - while (first != last) { - (*first)->Signal(); - first++; - } -} - -pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) { - ATRACE_NAME("VSyncService::HandleMessage"); - switch (message.GetOp()) { - case VSyncProtocol::Wait::Opcode: - AddWaiter(message); - return {}; - - case VSyncProtocol::GetLastTimestamp::Opcode: - DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>( - *this, &VSyncService::OnGetLastTimestamp, message); - return {}; - - case VSyncProtocol::GetSchedInfo::Opcode: - DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>( - *this, &VSyncService::OnGetSchedInfo, message); - return {}; - - case VSyncProtocol::Acknowledge::Opcode: - DispatchRemoteMethod<VSyncProtocol::Acknowledge>( - *this, &VSyncService::OnAcknowledge, message); - return {}; - - default: - return Service::HandleMessage(message); - } -} - -pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - - // Getting the timestamp has the side effect of ACKing. - client->Ack(); - return {current_vsync_}; -} - -pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo( - pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - - // Getting the timestamp has the side effect of ACKing. - client->Ack(); - - uint32_t next_vsync_count = current_vsync_count_ + 1; - int64_t current_time = GetSystemClockNs(); - int64_t vsync_period_ns = 0; - int64_t next_warp; - if (current_vsync_ == 0 || last_vsync_ == 0) { - // Handle startup when current_vsync_ or last_vsync_ are 0. - // Normally should not happen because vsync_service is running before - // applications, but in case it does a sane time prevents applications - // from malfunctioning. - vsync_period_ns = 20000000; - next_warp = current_time; - } else { - // TODO(jbates) When we have an accurate reading of the true vsync - // period, use that instead of this estimated value. - vsync_period_ns = current_vsync_ - last_vsync_; - // Clamp the period, because when there are no surfaces the last_vsync_ - // value will get stale. Note this is temporary and goes away as soon - // as we have an accurate vsync period reported by the system. - vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000)); - next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_; - // If the request missed the present window, move up to the next vsync. - if (current_time > next_warp) { - next_warp += vsync_period_ns; - ++next_vsync_count; - } - } - - return {{vsync_period_ns, next_warp, next_vsync_count}}; -} - -pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) { - auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel()); - std::lock_guard<std::mutex> autolock(mutex_); - client->Ack(); - return {}; -} - -void VSyncWaiter::Notify(int64_t timestamp) { - timestamp_ = timestamp; - DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait, - message_); -} - -pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) { - return {timestamp_}; -} - -void VSyncChannel::Ack() { - ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_); - service_.ModifyChannelEvents(cid_, POLLPRI, 0); -} - -void VSyncChannel::Signal() { - ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_); - service_.ModifyChannelEvents(cid_, 0, POLLPRI); -} - -} // namespace dvr -} // namespace android diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h deleted file mode 100644 index 822f02b266..0000000000 --- a/libs/vr/libvrflinger/vsync_service.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ -#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ - -#include <pdx/service.h> - -#include <list> -#include <memory> -#include <mutex> -#include <thread> - -#include "display_service.h" - -namespace android { -namespace dvr { - -// VSyncWaiter encapsulates a client blocked waiting for the next vsync. -// It is used to enqueue the Message to reply to when the next vsync event -// occurs. -class VSyncWaiter { - public: - explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {} - - void Notify(int64_t timestamp); - - private: - pdx::Status<int64_t> OnWait(pdx::Message& message); - - pdx::Message message_; - int64_t timestamp_ = 0; - - VSyncWaiter(const VSyncWaiter&) = delete; - void operator=(const VSyncWaiter&) = delete; -}; - -// VSyncChannel manages the service-side per-client context for each client -// using the service. -class VSyncChannel : public pdx::Channel { - public: - VSyncChannel(pdx::Service& service, int pid, int cid) - : service_(service), pid_(pid), cid_(cid) {} - - void Ack(); - void Signal(); - - private: - pdx::Service& service_; - pid_t pid_; - int cid_; - - VSyncChannel(const VSyncChannel&) = delete; - void operator=(const VSyncChannel&) = delete; -}; - -// VSyncService implements the displayd vsync service over ServiceFS. -class VSyncService : public pdx::ServiceBase<VSyncService> { - public: - ~VSyncService() override; - - pdx::Status<void> HandleMessage(pdx::Message& message) override; - - std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override; - void OnChannelClose(pdx::Message& message, - const std::shared_ptr<pdx::Channel>& channel) override; - - // Called by the hardware composer HAL, or similar, whenever a vsync event - // occurs on the primary display. |compositor_time_ns| is the number of ns - // before the next vsync when the compositor will preempt the GPU to do EDS - // and lens warp. - void VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns, - uint32_t vsync_count); - - private: - friend BASE; - - VSyncService(); - - pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message); - pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message); - pdx::Status<void> OnAcknowledge(pdx::Message& message); - - void NotifierThreadFunction(); - - void AddWaiter(pdx::Message& message); - void NotifyWaiters(); - void UpdateClients(); - - void AddClient(const std::shared_ptr<VSyncChannel>& client); - void RemoveClient(const std::shared_ptr<VSyncChannel>& client); - - int64_t last_vsync_; - int64_t current_vsync_; - int64_t compositor_time_ns_; - uint32_t current_vsync_count_; - - std::mutex mutex_; - - std::list<std::unique_ptr<VSyncWaiter>> waiters_; - std::list<std::shared_ptr<VSyncChannel>> clients_; - - VSyncService(const VSyncService&) = delete; - void operator=(VSyncService&) = delete; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_ diff --git a/libs/vr/libvrsensor/pose_client.cpp b/libs/vr/libvrsensor/pose_client.cpp index 4acc085428..710d75a7b1 100644 --- a/libs/vr/libvrsensor/pose_client.cpp +++ b/libs/vr/libvrsensor/pose_client.cpp @@ -228,7 +228,7 @@ class PoseClient : public pdx::ClientBase<PoseClient> { } constexpr size_t size = DvrVsyncPoseBuffer::kSize * sizeof(DvrPoseAsync); void* addr = nullptr; - int ret = buffer->GetBlobReadOnlyPointer(size, &addr); + int ret = buffer->GetBlobReadWritePointer(size, &addr); if (ret < 0 || !addr) { ALOGE("Pose failed to map ring buffer: ret:%d, addr:%p", ret, addr); return -EIO; diff --git a/libs/vr/public.libraries-google.txt b/libs/vr/public.libraries-google.txt new file mode 100644 index 0000000000..8271b9421f --- /dev/null +++ b/libs/vr/public.libraries-google.txt @@ -0,0 +1 @@ +libdvr.google.so
\ No newline at end of file diff --git a/opengl/include/EGL/Platform.h b/opengl/include/EGL/Platform.h new file mode 100644 index 0000000000..624d31fd8c --- /dev/null +++ b/opengl/include/EGL/Platform.h @@ -0,0 +1,329 @@ +// +// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Platform.h: The public interface ANGLE exposes to the API layer, for +// doing platform-specific tasks like gathering data, or for tracing. + +#ifndef ANGLE_PLATFORM_H +#define ANGLE_PLATFORM_H + +#include <stdint.h> +#include <array> + +#define EGL_PLATFORM_ANGLE_PLATFORM_METHODS_ANGLEX 0x3482 + +#if defined(_WIN32) +# if !defined(LIBANGLE_IMPLEMENTATION) +# define ANGLE_PLATFORM_EXPORT __declspec(dllimport) +# else +# define ANGLE_PLATFORM_EXPORT __declspec(dllexport) +# endif +#elif defined(__GNUC__) || defined(__clang__) +# define ANGLE_PLATFORM_EXPORT __attribute__((visibility ("default"))) +#endif +#if !defined(ANGLE_PLATFORM_EXPORT) +# define ANGLE_PLATFORM_EXPORT +#endif + +#if defined(_WIN32) +# define ANGLE_APIENTRY __stdcall +#else +# define ANGLE_APIENTRY +#endif + +namespace angle +{ +struct WorkaroundsD3D; +using TraceEventHandle = uint64_t; +using EGLDisplayType = void *; +struct PlatformMethods; + +// Use a C-like API to not trigger undefined calling behaviour. +// Avoid using decltype here to work around sanitizer limitations. +// TODO(jmadill): Use decltype here if/when UBSAN is fixed. + +// System -------------------------------------------------------------- + +// Wall clock time in seconds since the epoch. +// TODO(jmadill): investigate using an ANGLE internal time library +using CurrentTimeFunc = double (*)(PlatformMethods *platform); +inline double DefaultCurrentTime(PlatformMethods *platform) +{ + return 0.0; +} + +// Monotonically increasing time in seconds from an arbitrary fixed point in the past. +// This function is expected to return at least millisecond-precision values. For this reason, +// it is recommended that the fixed point be no further in the past than the epoch. +using MonotonicallyIncreasingTimeFunc = double (*)(PlatformMethods *platform); +inline double DefaultMonotonicallyIncreasingTime(PlatformMethods *platform) +{ + return 0.0; +} + +// Logging ------------------------------------------------------------ + +// Log an error message within the platform implementation. +using LogErrorFunc = void (*)(PlatformMethods *platform, const char *errorMessage); +inline void DefaultLogError(PlatformMethods *platform, const char *errorMessage) +{ +} + +// Log a warning message within the platform implementation. +using LogWarningFunc = void (*)(PlatformMethods *platform, const char *warningMessage); +inline void DefaultLogWarning(PlatformMethods *platform, const char *warningMessage) +{ +} + +// Log an info message within the platform implementation. +using LogInfoFunc = void (*)(PlatformMethods *platform, const char *infoMessage); +inline void DefaultLogInfo(PlatformMethods *platform, const char *infoMessage) +{ +} + +// Tracing -------- + +// Get a pointer to the enabled state of the given trace category. The +// embedder can dynamically change the enabled state as trace event +// recording is started and stopped by the application. Only long-lived +// literal strings should be given as the category name. The implementation +// expects the returned pointer to be held permanently in a local static. If +// the unsigned char is non-zero, tracing is enabled. If tracing is enabled, +// addTraceEvent is expected to be called by the trace event macros. +using GetTraceCategoryEnabledFlagFunc = const unsigned char *(*)(PlatformMethods *platform, + const char *categoryName); +inline const unsigned char *DefaultGetTraceCategoryEnabledFlag(PlatformMethods *platform, + const char *categoryName) +{ + return nullptr; +} + +// +// Add a trace event to the platform tracing system. Depending on the actual +// enabled state, this event may be recorded or dropped. +// - phase specifies the type of event: +// - BEGIN ('B'): Marks the beginning of a scoped event. +// - END ('E'): Marks the end of a scoped event. +// - COMPLETE ('X'): Marks the beginning of a scoped event, but doesn't +// need a matching END event. Instead, at the end of the scope, +// updateTraceEventDuration() must be called with the TraceEventHandle +// returned from addTraceEvent(). +// - INSTANT ('I'): Standalone, instantaneous event. +// - START ('S'): Marks the beginning of an asynchronous event (the end +// event can occur in a different scope or thread). The id parameter is +// used to match START/FINISH pairs. +// - FINISH ('F'): Marks the end of an asynchronous event. +// - COUNTER ('C'): Used to trace integer quantities that change over +// time. The argument values are expected to be of type int. +// - METADATA ('M'): Reserved for internal use. +// - categoryEnabled is the pointer returned by getTraceCategoryEnabledFlag. +// - name is the name of the event. Also used to match BEGIN/END and +// START/FINISH pairs. +// - id optionally allows events of the same name to be distinguished from +// each other. For example, to trace the construction and destruction of +// objects, specify the pointer as the id parameter. +// - timestamp should be a time value returned from monotonicallyIncreasingTime. +// - numArgs specifies the number of elements in argNames, argTypes, and +// argValues. +// - argNames is the array of argument names. Use long-lived literal strings +// or specify the COPY flag. +// - argTypes is the array of argument types: +// - BOOL (1): bool +// - UINT (2): unsigned long long +// - INT (3): long long +// - DOUBLE (4): double +// - POINTER (5): void* +// - STRING (6): char* (long-lived null-terminated char* string) +// - COPY_STRING (7): char* (temporary null-terminated char* string) +// - CONVERTABLE (8): WebConvertableToTraceFormat +// - argValues is the array of argument values. Each value is the unsigned +// long long member of a union of all supported types. +// - flags can be 0 or one or more of the following, ORed together: +// - COPY (0x1): treat all strings (name, argNames and argValues of type +// string) as temporary so that they will be copied by addTraceEvent. +// - HAS_ID (0x2): use the id argument to uniquely identify the event for +// matching with other events of the same name. +// - MANGLE_ID (0x4): specify this flag if the id parameter is the value +// of a pointer. +using AddTraceEventFunc = angle::TraceEventHandle (*)(PlatformMethods *platform, + char phase, + const unsigned char *categoryEnabledFlag, + const char *name, + unsigned long long id, + double timestamp, + int numArgs, + const char **argNames, + const unsigned char *argTypes, + const unsigned long long *argValues, + unsigned char flags); +inline angle::TraceEventHandle DefaultAddTraceEvent(PlatformMethods *platform, + char phase, + const unsigned char *categoryEnabledFlag, + const char *name, + unsigned long long id, + double timestamp, + int numArgs, + const char **argNames, + const unsigned char *argTypes, + const unsigned long long *argValues, + unsigned char flags) +{ + return 0; +} + +// Set the duration field of a COMPLETE trace event. +using UpdateTraceEventDurationFunc = void (*)(PlatformMethods *platform, + const unsigned char *categoryEnabledFlag, + const char *name, + angle::TraceEventHandle eventHandle); +inline void DefaultUpdateTraceEventDuration(PlatformMethods *platform, + const unsigned char *categoryEnabledFlag, + const char *name, + angle::TraceEventHandle eventHandle) +{ +} + +// Callbacks for reporting histogram data. +// CustomCounts histogram has exponential bucket sizes, so that min=1, max=1000000, bucketCount=50 +// would do. +using HistogramCustomCountsFunc = void (*)(PlatformMethods *platform, + const char *name, + int sample, + int min, + int max, + int bucketCount); +inline void DefaultHistogramCustomCounts(PlatformMethods *platform, + const char *name, + int sample, + int min, + int max, + int bucketCount) +{ +} +// Enumeration histogram buckets are linear, boundaryValue should be larger than any possible sample +// value. +using HistogramEnumerationFunc = void (*)(PlatformMethods *platform, + const char *name, + int sample, + int boundaryValue); +inline void DefaultHistogramEnumeration(PlatformMethods *platform, + const char *name, + int sample, + int boundaryValue) +{ +} +// Unlike enumeration histograms, sparse histograms only allocate memory for non-empty buckets. +using HistogramSparseFunc = void (*)(PlatformMethods *platform, const char *name, int sample); +inline void DefaultHistogramSparse(PlatformMethods *platform, const char *name, int sample) +{ +} +// Boolean histograms track two-state variables. +using HistogramBooleanFunc = void (*)(PlatformMethods *platform, const char *name, bool sample); +inline void DefaultHistogramBoolean(PlatformMethods *platform, const char *name, bool sample) +{ +} + +// Allows us to programatically override ANGLE's default workarounds for testing purposes. +using OverrideWorkaroundsD3DFunc = void (*)(PlatformMethods *platform, + angle::WorkaroundsD3D *workaroundsD3D); +inline void DefaultOverrideWorkaroundsD3D(PlatformMethods *platform, + angle::WorkaroundsD3D *workaroundsD3D) +{ +} + +// Callback on a successful program link with the program binary. Can be used to store +// shaders to disk. Keys are a 160-bit SHA-1 hash. +using ProgramKeyType = std::array<uint8_t, 20>; +using CacheProgramFunc = void (*)(PlatformMethods *platform, + const ProgramKeyType &key, + size_t programSize, + const uint8_t *programBytes); +inline void DefaultCacheProgram(PlatformMethods *platform, + const ProgramKeyType &key, + size_t programSize, + const uint8_t *programBytes) +{ +} + +// Platform methods are enumerated here once. +#define ANGLE_PLATFORM_OP(OP) \ + OP(currentTime, CurrentTime) \ + OP(monotonicallyIncreasingTime, MonotonicallyIncreasingTime) \ + OP(logError, LogError) \ + OP(logWarning, LogWarning) \ + OP(logInfo, LogInfo) \ + OP(getTraceCategoryEnabledFlag, GetTraceCategoryEnabledFlag) \ + OP(addTraceEvent, AddTraceEvent) \ + OP(updateTraceEventDuration, UpdateTraceEventDuration) \ + OP(histogramCustomCounts, HistogramCustomCounts) \ + OP(histogramEnumeration, HistogramEnumeration) \ + OP(histogramSparse, HistogramSparse) \ + OP(histogramBoolean, HistogramBoolean) \ + OP(overrideWorkaroundsD3D, OverrideWorkaroundsD3D) \ + OP(cacheProgram, CacheProgram) + +#define ANGLE_PLATFORM_METHOD_DEF(Name, CapsName) CapsName##Func Name = Default##CapsName; + +struct ANGLE_PLATFORM_EXPORT PlatformMethods +{ + PlatformMethods() {} + + // User data pointer for any implementation specific members. Put it at the start of the + // platform structure so it doesn't become overwritten if one version of the platform + // adds or removes new members. + void *context = 0; + + ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_DEF); +}; + +#undef ANGLE_PLATFORM_METHOD_DEF + +// Subtract one to account for the context pointer. +constexpr unsigned int g_NumPlatformMethods = (sizeof(PlatformMethods) / sizeof(uintptr_t)) - 1; + +#define ANGLE_PLATFORM_METHOD_STRING(Name) #Name +#define ANGLE_PLATFORM_METHOD_STRING2(Name, CapsName) ANGLE_PLATFORM_METHOD_STRING(Name), + +constexpr const char *const g_PlatformMethodNames[g_NumPlatformMethods] = { + ANGLE_PLATFORM_OP(ANGLE_PLATFORM_METHOD_STRING2)}; + +#undef ANGLE_PLATFORM_METHOD_STRING2 +#undef ANGLE_PLATFORM_METHOD_STRING + +} // namespace angle + +extern "C" { + +// Gets the platform methods on the passed-in EGL display. If the method name signature does not +// match the compiled signature for this ANGLE, false is returned. On success true is returned. +// The application should set any platform methods it cares about on the returned pointer. +// If display is not valid, behaviour is undefined. + +ANGLE_PLATFORM_EXPORT bool ANGLE_APIENTRY ANGLEGetDisplayPlatform(angle::EGLDisplayType display, + const char *const methodNames[], + unsigned int methodNameCount, + void *context, + void *platformMethodsOut); + +// Sets the platform methods back to their defaults. +// If display is not valid, behaviour is undefined. +ANGLE_PLATFORM_EXPORT void ANGLE_APIENTRY ANGLEResetDisplayPlatform(angle::EGLDisplayType display); + +} // extern "C" + +namespace angle +{ +typedef bool(ANGLE_APIENTRY *GetDisplayPlatformFunc)(angle::EGLDisplayType, + const char *const *, + unsigned int, + void *, + void *); +typedef void(ANGLE_APIENTRY *ResetDisplayPlatformFunc)(angle::EGLDisplayType); +} // namespace angle + +// This function is not exported +angle::PlatformMethods *ANGLEPlatformCurrent(); + +#endif // ANGLE_PLATFORM_H diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h index 93a3965cfc..c9e8b7c52c 100644 --- a/opengl/include/EGL/egl.h +++ b/opengl/include/EGL/egl.h @@ -33,12 +33,12 @@ extern "C" { ** used to make the header, and the header can be found at ** http://www.khronos.org/registry/egl ** -** Khronos $Git commit SHA1: a732b061e7 $ on $Git commit date: 2017-06-17 23:27:53 +0100 $ +** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $ */ #include <EGL/eglplatform.h> -/* Generated on date 20170627 */ +/* Generated on date 20180517 */ /* Generated C header for: * API: egl @@ -235,10 +235,66 @@ EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void); EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void); #endif /* EGL_VERSION_1_4 */ -/* This version of Android does not yet support EGL 1.5, but the following - * portion of EGL 1.5 is included in order to support portions of "eglext.h". - */ +#ifndef EGL_VERSION_1_5 +#define EGL_VERSION_1_5 1 +typedef void *EGLSync; typedef intptr_t EGLAttrib; +typedef khronos_utime_nanoseconds_t EGLTime; +typedef void *EGLImage; +#define EGL_CONTEXT_MAJOR_VERSION 0x3098 +#define EGL_CONTEXT_MINOR_VERSION 0x30FB +#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD +#define EGL_NO_RESET_NOTIFICATION 0x31BE +#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0 +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1 +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2 +#define EGL_OPENGL_ES3_BIT 0x00000040 +#define EGL_CL_EVENT_HANDLE 0x309C +#define EGL_SYNC_CL_EVENT 0x30FE +#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF +#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0 +#define EGL_SYNC_TYPE 0x30F7 +#define EGL_SYNC_STATUS 0x30F1 +#define EGL_SYNC_CONDITION 0x30F8 +#define EGL_SIGNALED 0x30F2 +#define EGL_UNSIGNALED 0x30F3 +#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001 +#define EGL_FOREVER 0xFFFFFFFFFFFFFFFFull +#define EGL_TIMEOUT_EXPIRED 0x30F5 +#define EGL_CONDITION_SATISFIED 0x30F6 +#define EGL_NO_SYNC EGL_CAST(EGLSync,0) +#define EGL_SYNC_FENCE 0x30F9 +#define EGL_GL_COLORSPACE 0x309D +#define EGL_GL_COLORSPACE_SRGB 0x3089 +#define EGL_GL_COLORSPACE_LINEAR 0x308A +#define EGL_GL_RENDERBUFFER 0x30B9 +#define EGL_GL_TEXTURE_2D 0x30B1 +#define EGL_GL_TEXTURE_LEVEL 0x30BC +#define EGL_GL_TEXTURE_3D 0x30B2 +#define EGL_GL_TEXTURE_ZOFFSET 0x30BD +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6 +#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7 +#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8 +#define EGL_IMAGE_PRESERVED 0x30D2 +#define EGL_NO_IMAGE EGL_CAST(EGLImage,0) +EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync); +EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout); +EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncAttrib (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value); +EGLAPI EGLImage EGLAPIENTRY eglCreateImage (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglDestroyImage (EGLDisplay dpy, EGLImage image); +EGLAPI EGLDisplay EGLAPIENTRY eglGetPlatformDisplay (EGLenum platform, void *native_display, const EGLAttrib *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list); +EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags); +#endif /* EGL_VERSION_1_5 */ #ifdef __cplusplus } diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 44f4dbc929..ad4ffd7d86 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -33,12 +33,12 @@ extern "C" { ** used to make the header, and the header can be found at ** http://www.khronos.org/registry/egl ** -** Khronos $Git commit SHA1: feaaeb19e1 $ on $Git commit date: 2018-02-26 20:49:02 -0800 $ +** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $ */ #include <EGL/eglplatform.h> -#define EGL_EGLEXT_VERSION 20180228 +#define EGL_EGLEXT_VERSION 20180517 /* Generated C header for: * API: egl @@ -618,6 +618,16 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu #define EGL_EXT_client_extensions 1 #endif /* EGL_EXT_client_extensions */ +#ifndef EGL_EXT_client_sync +#define EGL_EXT_client_sync 1 +#define EGL_SYNC_CLIENT_EXT 0x3364 +#define EGL_SYNC_CLIENT_SIGNAL_EXT 0x3365 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLCLIENTSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglClientSignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); +#endif +#endif /* EGL_EXT_client_sync */ + #ifndef EGL_EXT_compositor #define EGL_EXT_compositor 1 #define EGL_PRIMARY_COMPOSITOR_CONTEXT_EXT 0x3460 @@ -903,6 +913,14 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSu #endif #endif /* EGL_EXT_swap_buffers_with_damage */ +#ifndef EGL_EXT_sync_reuse +#define EGL_EXT_sync_reuse 1 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNSIGNALSYNCEXTPROC) (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglUnsignalSyncEXT (EGLDisplay dpy, EGLSync sync, const EGLAttrib *attrib_list); +#endif +#endif /* EGL_EXT_sync_reuse */ + #ifndef EGL_EXT_yuv_surface #define EGL_EXT_yuv_surface 1 #define EGL_YUV_ORDER_EXT 0x3301 @@ -1147,6 +1165,14 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerGLTextureExternalAttribsNV (EGLDi #define EGL_STREAM_FIFO_SYNCHRONOUS_NV 0x3336 #endif /* EGL_NV_stream_fifo_synchronous */ +#ifndef EGL_NV_stream_flush +#define EGL_NV_stream_flush 1 +typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream); +#endif +#endif /* EGL_NV_stream_flush */ + #ifndef EGL_NV_stream_frame_limits #define EGL_NV_stream_frame_limits 1 #define EGL_PRODUCER_MAX_FRAME_HINT_NV 0x3337 @@ -1285,17 +1311,6 @@ EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV (void); #define EGL_NATIVE_SURFACE_TIZEN 0x32A1 #endif /* EGL_TIZEN_image_native_surface */ -/* This is a private Android extension that does not exist in the EGL registry, - * formerly used to work around a hardware issue on Nexus 4. It is deprecated - * and unimplemented. It has been added to this header manually. */ -#ifndef EGL_ANDROID_image_crop -#define EGL_ANDROID_image_crop 1 -#define EGL_IMAGE_CROP_LEFT_ANDROID 0x3148 -#define EGL_IMAGE_CROP_TOP_ANDROID 0x3149 -#define EGL_IMAGE_CROP_RIGHT_ANDROID 0x314A -#define EGL_IMAGE_CROP_BOTTOM_ANDROID 0x314B -#endif - #ifdef __cplusplus } #endif diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h new file mode 100644 index 0000000000..0556ea1342 --- /dev/null +++ b/opengl/include/EGL/eglext_angle.h @@ -0,0 +1,191 @@ +// +// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// eglext_angle.h: ANGLE modifications to the eglext.h header file. +// Currently we don't include this file directly, we patch eglext.h +// to include it implicitly so it is visible throughout our code. + +#ifndef INCLUDE_EGL_EGLEXT_ANGLE_ +#define INCLUDE_EGL_EGLEXT_ANGLE_ + +// clang-format off + +#ifndef EGL_ANGLE_robust_resource_initialization +#define EGL_ANGLE_robust_resource_initialization 1 +#define EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE 0x3453 +#endif /* EGL_ANGLE_robust_resource_initialization */ + +#ifndef EGL_ANGLE_keyed_mutex +#define EGL_ANGLE_keyed_mutex 1 +#define EGL_DXGI_KEYED_MUTEX_ANGLE 0x33A2 +#endif /* EGL_ANGLE_keyed_mutex */ + +#ifndef EGL_ANGLE_d3d_texture_client_buffer +#define EGL_ANGLE_d3d_texture_client_buffer 1 +#define EGL_D3D_TEXTURE_ANGLE 0x33A3 +#endif /* EGL_ANGLE_d3d_texture_client_buffer */ + +#ifndef EGL_ANGLE_software_display +#define EGL_ANGLE_software_display 1 +#define EGL_SOFTWARE_DISPLAY_ANGLE ((EGLNativeDisplayType)-1) +#endif /* EGL_ANGLE_software_display */ + +#ifndef EGL_ANGLE_direct3d_display +#define EGL_ANGLE_direct3d_display 1 +#define EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2) +#define EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3) +#endif /* EGL_ANGLE_direct3d_display */ + +#ifndef EGL_ANGLE_direct_composition +#define EGL_ANGLE_direct_composition 1 +#define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5 +#endif /* EGL_ANGLE_direct_composition */ + +#ifndef EGL_ANGLE_platform_angle +#define EGL_ANGLE_platform_angle 1 +#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 +#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 +#define EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204 +#define EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205 +#define EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE 0x3206 +#define EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE 0x3451 +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209 +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x345E +#endif /* EGL_ANGLE_platform_angle */ + +#ifndef EGL_ANGLE_platform_angle_d3d +#define EGL_ANGLE_platform_angle_d3d 1 +#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 +#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE 0x320B +#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C +#define EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F +#endif /* EGL_ANGLE_platform_angle_d3d */ + +#ifndef EGL_ANGLE_platform_angle_opengl +#define EGL_ANGLE_platform_angle_opengl 1 +#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D +#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320E +#define EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE 0x3480 +#endif /* EGL_ANGLE_platform_angle_opengl */ + +#ifndef EGL_ANGLE_platform_angle_null +#define EGL_ANGLE_platform_angle_null 1 +#define EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE 0x33AE +#endif /* EGL_ANGLE_platform_angle_null */ + +#ifndef EGL_ANGLE_platform_angle_vulkan +#define EGL_ANGLE_platform_angle_vulkan 1 +#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450 +#endif /* EGL_ANGLE_platform_angle_vulkan */ + +#ifndef EGL_ANGLE_platform_angle_context_virtualization +#define EGL_ANGLE_platform_angle_context_virtualization 1 +#define EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE 0x3481 +#endif /* EGL_ANGLE_platform_angle_context_virtualization */ + +#ifndef EGL_ANGLE_x11_visual +#define EGL_ANGLE_x11_visual +#define EGL_X11_VISUAL_ID_ANGLE 0x33A3 +#endif /* EGL_ANGLE_x11_visual */ + +#ifndef EGL_ANGLE_flexible_surface_compatibility +#define EGL_ANGLE_flexible_surface_compatibility 1 +#define EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE 0x33A6 +#endif /* EGL_ANGLE_flexible_surface_compatibility */ + +#ifndef EGL_ANGLE_surface_orientation +#define EGL_ANGLE_surface_orientation +#define EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE 0x33A7 +#define EGL_SURFACE_ORIENTATION_ANGLE 0x33A8 +#define EGL_SURFACE_ORIENTATION_INVERT_X_ANGLE 0x0001 +#define EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE 0x0002 +#endif /* EGL_ANGLE_surface_orientation */ + +#ifndef EGL_ANGLE_experimental_present_path +#define EGL_ANGLE_experimental_present_path +#define EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE 0x33A4 +#define EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE 0x33A9 +#define EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE 0x33AA +#endif /* EGL_ANGLE_experimental_present_path */ + +#ifndef EGL_ANGLE_stream_producer_d3d_texture +#define EGL_ANGLE_stream_producer_d3d_texture +#define EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE 0x33AB +typedef EGLBoolean(EGLAPIENTRYP PFNEGLCREATESTREAMPRODUCERD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +typedef EGLBoolean(EGLAPIENTRYP PFNEGLSTREAMPOSTD3DTEXTUREANGLEPROC)(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLBoolean EGLAPIENTRY eglCreateStreamProducerD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglStreamPostD3DTextureANGLE(EGLDisplay dpy, EGLStreamKHR stream, void *texture, const EGLAttrib *attrib_list); +#endif +#endif /* EGL_ANGLE_stream_producer_d3d_texture */ + +#ifndef EGL_ANGLE_create_context_webgl_compatibility +#define EGL_ANGLE_create_context_webgl_compatibility 1 +#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x33AC +#endif /* EGL_ANGLE_create_context_webgl_compatibility */ + +#ifndef EGL_ANGLE_display_texture_share_group +#define EGL_ANGLE_display_texture_share_group 1 +#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x33AF +#endif /* EGL_ANGLE_display_texture_share_group */ + +#ifndef EGL_CHROMIUM_create_context_bind_generates_resource +#define EGL_CHROMIUM_create_context_bind_generates_resource 1 +#define EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM 0x33AD +#endif /* EGL_CHROMIUM_create_context_bind_generates_resource */ + +#ifndef EGL_ANGLE_create_context_client_arrays +#define EGL_ANGLE_create_context_client_arrays 1 +#define EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE 0x3452 +#endif /* EGL_ANGLE_create_context_client_arrays */ + +#ifndef EGL_ANGLE_device_creation +#define EGL_ANGLE_device_creation 1 +typedef EGLDeviceEXT(EGLAPIENTRYP PFNEGLCREATEDEVICEANGLEPROC) (EGLint device_type, void *native_device, const EGLAttrib *attrib_list); +typedef EGLBoolean(EGLAPIENTRYP PFNEGLRELEASEDEVICEANGLEPROC) (EGLDeviceEXT device); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE(EGLint device_type, void *native_device, const EGLAttrib *attrib_list); +EGLAPI EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE(EGLDeviceEXT device); +#endif +#endif /* EGL_ANGLE_device_creation */ + +#ifndef EGL_ANGLE_program_cache_control +#define EGL_ANGLE_program_cache_control 1 +#define EGL_PROGRAM_CACHE_SIZE_ANGLE 0x3455 +#define EGL_PROGRAM_CACHE_KEY_LENGTH_ANGLE 0x3456 +#define EGL_PROGRAM_CACHE_RESIZE_ANGLE 0x3457 +#define EGL_PROGRAM_CACHE_TRIM_ANGLE 0x3458 +#define EGL_CONTEXT_PROGRAM_BINARY_CACHE_ENABLED_ANGLE 0x3459 +typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHEGETATTRIBANGLEPROC) (EGLDisplay dpy, EGLenum attrib); +typedef void (EGLAPIENTRYP PFNEGLPROGRAMCACHEQUERYANGLEPROC) (EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize); +typedef void (EGLAPIENTRYP PFNEGPROGRAMCACHELPOPULATEANGLEPROC) (EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize); +typedef EGLint (EGLAPIENTRYP PFNEGLPROGRAMCACHERESIZEANGLEPROC) (EGLDisplay dpy, EGLint limit, EGLenum mode); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI EGLint EGLAPIENTRY eglProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib); +EGLAPI void EGLAPIENTRY eglProgramCacheQueryANGLE(EGLDisplay dpy, EGLint index, void *key, EGLint *keysize, void *binary, EGLint *binarysize); +EGLAPI void EGLAPIENTRY eglProgramCachePopulateANGLE(EGLDisplay dpy, const void *key, EGLint keysize, const void *binary, EGLint binarysize); +EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode); +#endif +#endif /* EGL_ANGLE_program_cache_control */ + +#ifndef EGL_ANGLE_iosurface_client_buffer +#define EGL_ANGLE_iosurface_client_buffer 1 +#define EGL_IOSURFACE_ANGLE 0x3454 +#define EGL_IOSURFACE_PLANE_ANGLE 0x345A +#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B +#define EGL_TEXTURE_TYPE_ANGLE 0x345C +#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D +#endif /* EGL_ANGLE_iosurface_client_buffer */ + +#ifndef EGL_ANGLE_create_context_extensions_enabled +#define EGL_ANGLE_create_context_extensions_enabled 1 +#define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F +#endif /* EGL_ANGLE_create_context_extensions_enabled */ + +// clang-format on + +#endif // INCLUDE_EGL_EGLEXT_ANGLE_ diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h index c77c3338de..0bc2cb9a74 100644 --- a/opengl/include/EGL/eglplatform.h +++ b/opengl/include/EGL/eglplatform.h @@ -77,12 +77,24 @@ typedef HDC EGLNativeDisplayType; typedef HBITMAP EGLNativePixmapType; typedef HWND EGLNativeWindowType; -#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ +#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ typedef int EGLNativeDisplayType; typedef void *EGLNativeWindowType; typedef void *EGLNativePixmapType; +#elif defined(WL_EGL_PLATFORM) + +typedef struct wl_display *EGLNativeDisplayType; +typedef struct wl_egl_pixmap *EGLNativePixmapType; +typedef struct wl_egl_window *EGLNativeWindowType; + +#elif defined(__GBM__) + +typedef struct gbm_device *EGLNativeDisplayType; +typedef struct gbm_bo *EGLNativePixmapType; +typedef void *EGLNativeWindowType; + #elif defined(__ANDROID__) || defined(ANDROID) struct ANativeWindow; @@ -92,7 +104,13 @@ typedef struct ANativeWindow* EGLNativeWindowType; typedef struct egl_native_pixmap_t* EGLNativePixmapType; typedef void* EGLNativeDisplayType; -#elif defined(__unix__) +#elif defined(USE_OZONE) + +typedef intptr_t EGLNativeDisplayType; +typedef intptr_t EGLNativeWindowType; +typedef intptr_t EGLNativePixmapType; + +#elif defined(__unix__) || defined(USE_X11) /* X11 (tentative) */ #include <X11/Xlib.h> @@ -102,6 +120,20 @@ typedef Display *EGLNativeDisplayType; typedef Pixmap EGLNativePixmapType; typedef Window EGLNativeWindowType; +#elif defined(__APPLE__) + +typedef int EGLNativeDisplayType; +typedef void *EGLNativeWindowType; +typedef void *EGLNativePixmapType; + +#elif defined(__HAIKU__) + +#include <kernel/image.h> + +typedef void *EGLNativeDisplayType; +typedef khronos_uintptr_t EGLNativePixmapType; +typedef khronos_uintptr_t EGLNativeWindowType; + #else #error "Platform not recognized" #endif diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index d43c1648be..583aec9db5 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -140,16 +140,22 @@ cc_library_shared { "EGL/egl_cache.cpp", "EGL/egl_display.cpp", "EGL/egl_object.cpp", + "EGL/egl_layers.cpp", "EGL/egl.cpp", "EGL/eglApi.cpp", + "EGL/egl_platform_entries.cpp", "EGL/Loader.cpp", + "EGL/egl_angle_platform.cpp", ], shared_libs: [ "libvndksupport", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libbase", "libhidlbase", "libhidltransport", + "libnativebridge", + "libnativeloader", "libutils", ], static_libs: [ diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index b3752f5bcc..74c4d7d07a 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -79,7 +79,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, } std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); + CacheEntry dummyEntry(dummyKey, nullptr); while (true) { auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); @@ -139,7 +139,7 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, return 0; } std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, NULL); + CacheEntry dummyEntry(dummyKey, nullptr); auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); if (index == mCacheEntries.end() || dummyEntry < *index) { ALOGV("get: no cache entry found for key of size %zu", keySize); @@ -308,7 +308,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { - if (data != NULL && copyData) { + if (data != nullptr && copyData) { memcpy(const_cast<void*>(mData), data, size); } } diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index 1f5d5357e6..e5c5e5bc25 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -97,6 +97,10 @@ public: // int unflatten(void const* buffer, size_t size); + // clear flushes out all contents of the cache then the BlobCache, leaving + // it in an empty state. + void clear() { mCacheEntries.clear(); } + protected: // mMaxTotalSize is the maximum size that all cache entries can occupy. This // includes space for both keys and values. When a call to BlobCache::set diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp index edbaaf090d..cf67cf443b 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -97,7 +97,7 @@ TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0)); } TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { @@ -169,7 +169,7 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { } mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); - ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0)); } TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { @@ -219,7 +219,7 @@ TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { } mBC->set(key, MAX_KEY_SIZE, buf, bufSize); - ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0)); } TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { @@ -237,7 +237,7 @@ TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { int numCached = 0; for (int i = 0; i < 256; i++) { uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { + if (mBC->get(&k, 1, nullptr, 0) == 1) { numCached++; } } @@ -260,7 +260,7 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { int numCached = 0; for (int i = 0; i < maxEntries+1; i++) { uint8_t k = i; - if (mBC->get(&k, 1, NULL, 0) == 1) { + if (mBC->get(&k, 1, nullptr, 0) == 1) { numCached++; } } diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp index 79237159bf..cc42ac7fef 100644 --- a/opengl/libs/EGL/FileBlobCache.cpp +++ b/opengl/libs/EGL/FileBlobCache.cpp @@ -77,7 +77,7 @@ FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxT return; } - uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0)); if (buf == MAP_FAILED) { ALOGE("error mmaping cache file: %s (%d)", strerror(errno), diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 91a34558aa..a150db1b7d 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -33,11 +33,33 @@ #endif #include <vndksupport/linker.h> +#include "egl_platform_entries.h" #include "egl_trace.h" #include "egldefs.h" +#include <EGL/eglext_angle.h> extern "C" { android_namespace_t* android_get_exported_namespace(const char*); + + // TODO(ianelliott@): Get this from an ANGLE header: + typedef enum ANGLEPreference { + ANGLE_NO_PREFERENCE = 0, + ANGLE_PREFER_NATIVE = 1, + ANGLE_PREFER_ANGLE = 2, + } ANGLEPreference; + + // TODO(ianelliott@): Get this from an ANGLE header: + typedef bool (*fpANGLEUseForApplication)(const char* appName, const char* deviceMfr, + const char* deviceModel, ANGLEPreference developerOption, + ANGLEPreference appPreference); + + // TODO(ianelliott@): Get this from an ANGLE header: + typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse); + + // TODO(ianelliott@): Get this from an ANGLE header: + typedef bool (*fpAndroidUseANGLEForApplication)(int fd, long offset, long length, + const char* appName, const char* deviceMfr, + const char* deviceModel); } // ---------------------------------------------------------------------------- @@ -129,7 +151,7 @@ Loader::driver_t::driver_t(void* gles) { dso[0] = gles; for (size_t i=1 ; i<NELEM(dso) ; i++) - dso[i] = 0; + dso[i] = nullptr; } Loader::driver_t::~driver_t() @@ -137,7 +159,7 @@ Loader::driver_t::~driver_t() for (size_t i=0 ; i<NELEM(dso) ; i++) { if (dso[i]) { dlclose(dso[i]); - dso[i] = 0; + dso[i] = nullptr; } } } @@ -163,7 +185,7 @@ int Loader::driver_t::set(void* hnd, int32_t api) // ---------------------------------------------------------------------------- Loader::Loader() - : getProcAddress(NULL) + : getProcAddress(nullptr) { } @@ -221,7 +243,7 @@ void* Loader::open(egl_connection_t* cnx) ATRACE_CALL(); void* dso; - driver_t* hnd = 0; + driver_t* hnd = nullptr; setEmulatorGlesValue(); @@ -253,14 +275,29 @@ void* Loader::open(egl_connection_t* cnx) return (void*)hnd; } -void Loader::close(void* driver) +void Loader::close(egl_connection_t* cnx) { - driver_t* hnd = (driver_t*)driver; + driver_t* hnd = (driver_t*) cnx->dso; delete hnd; + cnx->dso = nullptr; + + if (cnx->featureSo) { + dlclose(cnx->featureSo); + cnx->featureSo = nullptr; + } + + cnx->angleDecided = false; + cnx->useAngle = false; + + if (cnx->vendorEGL) { + dlclose(cnx->vendorEGL); + cnx->vendorEGL = nullptr; + } } void Loader::init_api(void* dso, char const * const * api, + char const * const * ref_api, __eglMustCastToProperFunctionPointerType* curr, getProcAddressType getProcAddress) { @@ -270,13 +307,22 @@ void Loader::init_api(void* dso, char scrap[SIZE]; while (*api) { char const * name = *api; + if (ref_api) { + char const * ref_name = *ref_api; + if (std::strcmp(name, ref_name) != 0) { + *curr++ = nullptr; + ref_api++; + continue; + } + } + __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); } - if (f == NULL) { + if (f == nullptr) { // Try without the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { @@ -286,7 +332,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { // Try with the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if (index>0 && strcmp(name+index, "OES")) { @@ -295,7 +341,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { //ALOGD("%s", name); f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; @@ -314,6 +360,7 @@ void Loader::init_api(void* dso, } *curr++ = f; api++; + if (ref_api) ref_api++; } } @@ -406,9 +453,9 @@ static void* load_system_driver(const char* kind) { } DIR* d = opendir(search); - if (d != NULL) { + if (d != nullptr) { struct dirent* e; - while ((e = readdir(d)) != NULL) { + while ((e = readdir(d)) != nullptr) { if (e->d_type == DT_DIR) { continue; } @@ -434,7 +481,7 @@ static void* load_system_driver(const char* kind) { std::string absolutePath = MatchFile::find(kind); if (absolutePath.empty()) { // this happens often, we don't want to log an error - return 0; + return nullptr; } const char* const driver_absolute_path = absolutePath.c_str(); @@ -444,10 +491,10 @@ static void* load_system_driver(const char* kind) { // sphal namespace. void* dso = do_android_load_sphal_library(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); - if (dso == 0) { + if (dso == nullptr) { const char* err = dlerror(); ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown"); - return 0; + return nullptr; } ALOGD("loaded %s", driver_absolute_path); @@ -455,6 +502,168 @@ static void* load_system_driver(const char* kind) { return dso; } +static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + + std::string name = std::string("lib") + kind + "_angle.so"; + + void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + + if (so) { + ALOGD("dlopen_ext from APK (%s) success at %p", name.c_str(), so); + return so; + } else { + ALOGE("dlopen_ext(\"%s\") failed: %s", name.c_str(), dlerror()); + } + + return nullptr; +} + +static ANGLEPreference getAnglePref(const char* app_pref) { + if (app_pref == nullptr) + return ANGLE_NO_PREFERENCE; + + if (strcmp(app_pref, "angle") == 0) { + return ANGLE_PREFER_ANGLE; + } else if (strcmp(app_pref, "native") == 0) { + return ANGLE_PREFER_NATIVE; + } + return ANGLE_NO_PREFERENCE; +} + +static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) { + // Only attempt to load ANGLE libs + if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0) + return nullptr; + + std::string name; + char prop[PROPERTY_VALUE_MAX]; + + const char* app_name = android::GraphicsEnv::getInstance().getAngleAppName(); + const char* app_pref = android::GraphicsEnv::getInstance().getAngleAppPref(); + bool developer_opt_in = android::GraphicsEnv::getInstance().getAngleDeveloperOptIn(); + const int rules_fd = android::GraphicsEnv::getInstance().getAngleRulesFd(); + const long rules_offset = android::GraphicsEnv::getInstance().getAngleRulesOffset(); + const long rules_length = android::GraphicsEnv::getInstance().getAngleRulesLength(); + + // Determine whether or not to use ANGLE: + ANGLEPreference developer_option = developer_opt_in ? ANGLE_PREFER_ANGLE : ANGLE_NO_PREFERENCE; + bool use_angle = (developer_option == ANGLE_PREFER_ANGLE); + + if (use_angle) { + ALOGV("User set \"Developer Options\" to force the use of ANGLE"); + } else if (cnx->angleDecided) { + use_angle = cnx->useAngle; + } else { + // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily + // load ANGLE and call the updatable opt-in/out logic: + std::string app_name_str = app_name ? app_name : ""; + char manufacturer[PROPERTY_VALUE_MAX]; + char model[PROPERTY_VALUE_MAX]; + property_get("ro.product.manufacturer", manufacturer, "UNSET"); + property_get("ro.product.model", model, "UNSET"); + + cnx->featureSo = load_angle_from_namespace("feature_support", ns); + if (cnx->featureSo) { + ALOGV("loaded ANGLE's opt-in/out logic from namespace"); + bool use_version0_API = false; + bool use_version1_API = false; + fpANGLEGetUtilityAPI ANGLEGetUtilityAPI = + (fpANGLEGetUtilityAPI)dlsym(cnx->featureSo, "ANGLEGetUtilityAPI"); + if (ANGLEGetUtilityAPI) { + unsigned int versionToUse = 1; + if ((ANGLEGetUtilityAPI)(&versionToUse)) { + if (versionToUse == 1) { + use_version1_API = true; + } else { + use_version0_API = true; + } + } + } else { + use_version0_API = true; + ALOGV("Cannot find ANGLEGetUtilityAPI in library"); + } + if (use_version1_API) { + // Use the new version 1 API to determine if the + // application should use the ANGLE or the native driver. + fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication = + (fpAndroidUseANGLEForApplication)dlsym(cnx->featureSo, "AndroidUseANGLEForApplication"); + if (AndroidUseANGLEForApplication) { + use_angle = (AndroidUseANGLEForApplication)(rules_fd, rules_offset, + rules_length, app_name_str.c_str(), + manufacturer, model); + } else { + ALOGW("Cannot find AndroidUseANGLEForApplication in library"); + } + } else if (use_version0_API) { + // Use the old version 0 API to determine if the + // application should use the ANGLE or the native driver. + fpANGLEUseForApplication ANGLEUseForApplication = + (fpANGLEUseForApplication)dlsym(cnx->featureSo, "ANGLEUseForApplication"); + if (ANGLEUseForApplication) { + ANGLEPreference app_preference = + getAnglePref(android::GraphicsEnv::getInstance().getAngleAppPref()); + use_angle = (ANGLEUseForApplication)(app_name_str.c_str(), manufacturer, model, + developer_option, app_preference); + ALOGV("Result of opt-in/out logic is %s", use_angle ? "true" : "false"); + } else { + ALOGW("Cannot find ANGLEUseForApplication in library"); + } + } + } else { + // We weren't able to load and call the updateable opt-in/out logic. + // If we can't load the library, there is no ANGLE available. + use_angle = false; + ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); + } + cnx->angleDecided = true; + } + void* so = nullptr; + if (use_angle) { + so = load_angle_from_namespace(kind, ns); + } + + if (so) { + ALOGV("Loaded ANGLE %s library for %s (instead of native)", + kind, app_name ? app_name : "nullptr"); + property_get("debug.hwui.renderer", prop, "UNSET"); + ALOGV("Skia's renderer set to %s", prop); + cnx->useAngle = true; + + EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.angle.backend", prop, "0"); + switch (atoi(prop)) { + case 1: + ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; + break; + case 2: + ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + break; + default: + break; + } + + cnx->angleBackend = angleBackendDefault; + if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) { + // Find and load vendor libEGL for ANGLE's GL back-end to use. + cnx->vendorEGL = load_system_driver("EGL"); + } + return so; + } else { + ALOGV("Loaded native %s library for %s (instead of ANGLE)", + kind, app_name ? app_name : "nullptr"); + } + + return nullptr; +} + static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = { "ro.hardware.egl", "ro.board.platform", @@ -486,16 +695,22 @@ void *Loader::load_driver(const char* kind, ATRACE_CALL(); void* dso = nullptr; -#ifndef __ANDROID_VNDK__ - android_namespace_t* ns = android_getDriverNamespace(); + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); if (ns) { - dso = load_updated_driver(kind, ns); + dso = load_angle(kind, ns, cnx); + } +#ifndef __ANDROID_VNDK__ + if (!dso) { + android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); + if (ns) { + dso = load_updated_driver(kind, ns); + } } #endif if (!dso) { dso = load_system_driver(kind); if (!dso) - return NULL; + return nullptr; } if (mask & EGL) { @@ -512,11 +727,11 @@ void *Loader::load_driver(const char* kind, char const * name = *api; __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); - if (f == NULL) { - f = (__eglMustCastToProperFunctionPointerType)0; + if (f == nullptr) { + f = (__eglMustCastToProperFunctionPointerType)nullptr; } } *curr++ = f; @@ -525,14 +740,14 @@ void *Loader::load_driver(const char* kind, } if (mask & GLESv1_CM) { - init_api(dso, gl_names, + init_api(dso, gl_names_1, gl_names, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, getProcAddress); } if (mask & GLESv2) { - init_api(dso, gl_names, + init_api(dso, gl_names, nullptr, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index 6a32bb3066..392887d42c 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -29,11 +29,12 @@ struct egl_connection_t; class Loader { typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*); - + enum { EGL = 0x01, GLESv1_CM = 0x02, - GLESv2 = 0x04 + GLESv2 = 0x04, + PLATFORM = 0x08 }; struct driver_t { explicit driver_t(void* gles); @@ -42,25 +43,26 @@ class Loader { int set(void* hnd, int32_t api); void* dso[3]; }; - + getProcAddressType getProcAddress; public: static Loader& getInstance(); ~Loader(); - + void* open(egl_connection_t* cnx); - void close(void* driver); - + void close(egl_connection_t* cnx); + private: Loader(); void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask); static __attribute__((noinline)) - void init_api(void* dso, - char const * const * api, - __eglMustCastToProperFunctionPointerType* curr, - getProcAddressType getProcAddress); + void init_api(void* dso, + char const * const * api, + char const * const * ref_api, + __eglMustCastToProperFunctionPointerType* curr, + getProcAddressType getProcAddress); }; // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index f53cf3f8cc..8870d5f571 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -30,11 +30,10 @@ #include "egl_tls.h" #include "egl_display.h" #include "egl_object.h" +#include "egl_layers.h" #include "CallStack.h" #include "Loader.h" -typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; - // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -89,22 +88,22 @@ static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); egl_display_ptr validate_display(EGLDisplay dpy) { egl_display_ptr dp = get_display(dpy); if (!dp) - return setError(EGL_BAD_DISPLAY, egl_display_ptr(NULL)); + return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr)); if (!dp->isReady()) - return setError(EGL_NOT_INITIALIZED, egl_display_ptr(NULL)); + return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr)); return dp; } egl_display_ptr validate_display_connection(EGLDisplay dpy, egl_connection_t*& cnx) { - cnx = NULL; + cnx = nullptr; egl_display_ptr dp = validate_display(dpy); if (!dp) return dp; cnx = &gEGLImpl; - if (cnx->dso == 0) { - return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL)); + if (cnx->dso == nullptr) { + return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr)); } return dp; } @@ -117,14 +116,14 @@ const GLubyte * egl_get_string_for_current_context(GLenum name) { EGLContext context = egl_tls_t::getContext(); if (context == EGL_NO_CONTEXT) - return NULL; + return nullptr; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction - return NULL; + if (c == nullptr) // this should never happen, by construction + return nullptr; if (name != GL_EXTENSIONS) - return NULL; + return nullptr; return (const GLubyte *)c->gl_extensions.c_str(); } @@ -135,19 +134,19 @@ const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) { EGLContext context = egl_tls_t::getContext(); if (context == EGL_NO_CONTEXT) - return NULL; + return nullptr; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction - return NULL; + if (c == nullptr) // this should never happen, by construction + return nullptr; if (name != GL_EXTENSIONS) - return NULL; + return nullptr; // if index is out of bounds, assume it will be in the default // implementation too, so we don't have to generate a GL error here if (index >= c->tokenized_gl_extensions.size()) - return NULL; + return nullptr; return (const GLubyte *)c->tokenized_gl_extensions[index].c_str(); } @@ -161,12 +160,16 @@ GLint egl_get_num_extensions_for_current_context() { return -1; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction + if (c == nullptr) // this should never happen, by construction return -1; return (GLint)c->tokenized_gl_extensions.size(); } +egl_connection_t* egl_get_connection() { + return &gEGLImpl; +} + // ---------------------------------------------------------------------------- // this mutex protects: @@ -184,7 +187,7 @@ static EGLBoolean egl_init_drivers_locked() { // dynamically load our EGL implementation egl_connection_t* cnx = &gEGLImpl; - if (cnx->dso == 0) { + if (cnx->dso == nullptr) { cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX]; cnx->hooks[egl_connection_t::GLESv2_INDEX] = @@ -192,6 +195,14 @@ static EGLBoolean egl_init_drivers_locked() { cnx->dso = loader.open(cnx); } + // Check to see if any layers are enabled and route functions through them + if (cnx->dso) { + // Layers can be enabled long after the drivers have been loaded. + // They will only be initialized once. + LayerLoader& layer_loader(LayerLoader::getInstance()); + layer_loader.InitLayers(cnx); + } + return cnx->dso ? EGL_TRUE : EGL_FALSE; } @@ -249,12 +260,22 @@ void setGlThreadSpecific(gl_hooks_t const *value) { char const * const gl_names[] = { #include "../entries.in" - NULL + nullptr +}; + +char const * const gl_names_1[] = { + #include "../entries_gles1.in" + nullptr }; char const * const egl_names[] = { #include "egl_entries.in" - NULL + nullptr +}; + +char const * const platform_names[] = { + #include "platform_entries.in" + nullptr }; #undef GL_ENTRY diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 36deedcb85..29a966d348 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -1,5 +1,5 @@ /* - ** Copyright 2007, The Android Open Source Project + ** Copyright 2018, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -16,1170 +16,225 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <ctype.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <string.h> - -#include <hardware/gralloc1.h> - #include <EGL/egl.h> #include <EGL/eglext.h> -#include <android/hardware_buffer.h> -#include <private/android/AHardwareBufferHelpers.h> - -#include <cutils/compiler.h> -#include <cutils/properties.h> -#include <log/log.h> - -#include <condition_variable> -#include <deque> -#include <mutex> -#include <unordered_map> -#include <string> -#include <thread> - #include "../egl_impl.h" -#include "egl_display.h" -#include "egl_object.h" +#include "egl_layers.h" +#include "egl_platform_entries.h" #include "egl_tls.h" #include "egl_trace.h" using namespace android; -// ---------------------------------------------------------------------------- - namespace android { -using nsecs_t = int64_t; - -struct extention_map_t { - const char* name; - __eglMustCastToProperFunctionPointerType address; -}; - -/* - * This is the list of EGL extensions exposed to applications. - * - * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL - * wrapper and are always available. - * - * The rest (gExtensionString) depend on support in the EGL driver, and are - * only available if the driver supports them. However, some of these must be - * supported because they are used by the Android system itself; these are - * listed as mandatory below and are required by the CDD. The system *assumes* - * the mandatory extensions are present and may not function properly if some - * are missing. - * - * NOTE: Both strings MUST have a single space as the last character. - */ - -extern char const * const gBuiltinExtensionString; -extern char const * const gExtensionString; - -// clang-format off -// Extensions implemented by the EGL wrapper. -char const * const gBuiltinExtensionString = - "EGL_KHR_get_all_proc_addresses " - "EGL_ANDROID_presentation_time " - "EGL_KHR_swap_buffers_with_damage " - "EGL_ANDROID_get_native_client_buffer " - "EGL_ANDROID_front_buffer_auto_refresh " - "EGL_ANDROID_get_frame_timestamps " - "EGL_EXT_surface_SMPTE2086_metadata " - "EGL_EXT_surface_CTA861_3_metadata " - ; - -// Whitelist of extensions exposed to applications if implemented in the vendor driver. -char const * const gExtensionString = - "EGL_KHR_image " // mandatory - "EGL_KHR_image_base " // mandatory - "EGL_EXT_image_gl_colorspace " - "EGL_KHR_image_pixmap " - "EGL_KHR_lock_surface " - "EGL_KHR_gl_colorspace " - "EGL_KHR_gl_texture_2D_image " - "EGL_KHR_gl_texture_3D_image " - "EGL_KHR_gl_texture_cubemap_image " - "EGL_KHR_gl_renderbuffer_image " - "EGL_KHR_reusable_sync " - "EGL_KHR_fence_sync " - "EGL_KHR_create_context " - "EGL_KHR_config_attribs " - "EGL_KHR_surfaceless_context " - "EGL_KHR_stream " - "EGL_KHR_stream_fifo " - "EGL_KHR_stream_producer_eglsurface " - "EGL_KHR_stream_consumer_gltexture " - "EGL_KHR_stream_cross_process_fd " - "EGL_EXT_create_context_robustness " - "EGL_NV_system_time " - "EGL_ANDROID_image_native_buffer " // mandatory - "EGL_KHR_wait_sync " // strongly recommended - "EGL_ANDROID_recordable " // mandatory - "EGL_KHR_partial_update " // strongly recommended - "EGL_EXT_pixel_format_float " - "EGL_EXT_buffer_age " // strongly recommended with partial_update - "EGL_KHR_create_context_no_error " - "EGL_KHR_mutable_render_buffer " - "EGL_EXT_yuv_surface " - "EGL_EXT_protected_content " - "EGL_IMG_context_priority " - "EGL_KHR_no_config_context " - ; -// clang-format on - -// extensions not exposed to applications but used by the ANDROID system -// "EGL_ANDROID_blob_cache " // strongly recommended -// "EGL_IMG_hibernate_process " // optional -// "EGL_ANDROID_native_fence_sync " // strongly recommended -// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 -// "EGL_ANDROID_image_crop " // optional - -/* - * EGL Extensions entry-points exposed to 3rd party applications - * (keep in sync with gExtensionString above) - * - */ -static const extention_map_t sExtensionMap[] = { - // EGL_KHR_lock_surface - { "eglLockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, - { "eglUnlockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, - - // EGL_KHR_image, EGL_KHR_image_base - { "eglCreateImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, - { "eglDestroyImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, - - // EGL_KHR_reusable_sync, EGL_KHR_fence_sync - { "eglCreateSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, - { "eglDestroySyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, - { "eglClientWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, - { "eglSignalSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, - { "eglGetSyncAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, - - // EGL_NV_system_time - { "eglGetSystemTimeFrequencyNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, - { "eglGetSystemTimeNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, - - // EGL_KHR_wait_sync - { "eglWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, - - // EGL_ANDROID_presentation_time - { "eglPresentationTimeANDROID", - (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, - - // EGL_KHR_swap_buffers_with_damage - { "eglSwapBuffersWithDamageKHR", - (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, - - // EGL_ANDROID_get_native_client_buffer - { "eglGetNativeClientBufferANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, - - // EGL_KHR_partial_update - { "eglSetDamageRegionKHR", - (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, - - { "eglCreateStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, - { "eglDestroyStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, - { "eglStreamAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, - { "eglQueryStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, - { "eglQueryStreamu64KHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, - { "eglQueryStreamTimeKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, - { "eglCreateStreamProducerSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, - { "eglStreamConsumerGLTextureExternalKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, - { "eglStreamConsumerAcquireKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, - { "eglStreamConsumerReleaseKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, - { "eglGetStreamFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, - { "eglCreateStreamFromFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, - - // EGL_ANDROID_get_frame_timestamps - { "eglGetNextFrameIdANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, - { "eglGetCompositorTimingANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, - { "eglGetCompositorTimingSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, - { "eglGetFrameTimestampsANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, - { "eglGetFrameTimestampSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, - - // EGL_ANDROID_native_fence_sync - { "eglDupNativeFenceFDANDROID", - (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, -}; - -/* - * These extensions entry-points should not be exposed to applications. - * They're used internally by the Android EGL layer. - */ -#define FILTER_EXTENSIONS(procname) \ - (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ - !strcmp((procname), "eglHibernateProcessIMG") || \ - !strcmp((procname), "eglAwakenProcessIMG")) - -// accesses protected by sExtensionMapMutex -static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; - -static int sGLExtentionSlot = 0; -static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; - -static void(*findProcAddress(const char* name, - const extention_map_t* map, size_t n))() { - for (uint32_t i=0 ; i<n ; i++) { - if (!strcmp(name, map[i].name)) { - return map[i].address; - } - } - return NULL; -} - -// ---------------------------------------------------------------------------- - -extern void setGLHooksThreadSpecific(gl_hooks_t const *value); extern EGLBoolean egl_init_drivers(); -extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; -extern gl_hooks_t gHooksTrace; - -} // namespace android; +} // namespace android -// ---------------------------------------------------------------------------- - -static inline void clearError() { egl_tls_t::clearError(); } -static inline EGLContext getContext() { return egl_tls_t::getContext(); } - -// ---------------------------------------------------------------------------- +static inline void clearError() { + egl_tls_t::clearError(); +} -EGLDisplay eglGetDisplay(EGLNativeDisplayType display) -{ +EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { ATRACE_CALL(); clearError(); - uintptr_t index = reinterpret_cast<uintptr_t>(display); - if (index >= NUM_DISPLAYS) { - return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); - } - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); } - EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display); - return dpy; + // Call down the chain, which usually points directly to the impl + // but may also be routed through layers + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetDisplay(display); } -// ---------------------------------------------------------------------------- -// Initialization -// ---------------------------------------------------------------------------- - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ +EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { + ATRACE_CALL(); clearError(); - egl_display_ptr dp = get_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - EGLBoolean res = dp->initialize(major, minor); + if (egl_init_drivers() == EGL_FALSE) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } - return res; + // Call down the chain, which usually points directly to the impl + // but may also be routed through layers + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list); } -EGLBoolean eglTerminate(EGLDisplay dpy) -{ - // NOTE: don't unload the drivers b/c some APIs can be called - // after eglTerminate() has been called. eglTerminate() only - // terminates an EGLDisplay, not a EGL itself. - +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { clearError(); - egl_display_ptr dp = get_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - EGLBoolean res = dp->terminate(); - - return res; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglInitialize(dpy, major, minor); } -// ---------------------------------------------------------------------------- -// configuration -// ---------------------------------------------------------------------------- - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config) -{ +EGLBoolean eglTerminate(EGLDisplay dpy) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - if (num_config==0) { - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - - EGLBoolean res = EGL_FALSE; - *num_config = 0; - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - res = cnx->egl.eglGetConfigs( - dp->disp.dpy, configs, config_size, num_config); - } - - return res; + return cnx->platform.eglTerminate(dpy); } -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ +EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, + EGLint* num_config) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - if (num_config==0) { - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - - EGLBoolean res = EGL_FALSE; - *num_config = 0; - - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - if (attrib_list) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.force_msaa", value, "false"); - - if (!strcmp(value, "true")) { - size_t attribCount = 0; - EGLint attrib = attrib_list[0]; - - // Only enable MSAA if the context is OpenGL ES 2.0 and - // if no caveat is requested - const EGLint *attribRendererable = NULL; - const EGLint *attribCaveat = NULL; - - // Count the number of attributes and look for - // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT - while (attrib != EGL_NONE) { - attrib = attrib_list[attribCount]; - switch (attrib) { - case EGL_RENDERABLE_TYPE: - attribRendererable = &attrib_list[attribCount]; - break; - case EGL_CONFIG_CAVEAT: - attribCaveat = &attrib_list[attribCount]; - break; - default: - break; - } - attribCount++; - } - - if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && - (!attribCaveat || attribCaveat[1] != EGL_NONE)) { - - // Insert 2 extra attributes to force-enable MSAA 4x - EGLint aaAttribs[attribCount + 4]; - aaAttribs[0] = EGL_SAMPLE_BUFFERS; - aaAttribs[1] = 1; - aaAttribs[2] = EGL_SAMPLES; - aaAttribs[3] = 4; - - memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); - - EGLint numConfigAA; - EGLBoolean resAA = cnx->egl.eglChooseConfig( - dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); - - if (resAA == EGL_TRUE && numConfigAA > 0) { - ALOGD("Enabling MSAA 4x"); - *num_config = numConfigAA; - return resAA; - } - } - } - } - - res = cnx->egl.eglChooseConfig( - dp->disp.dpy, attrib_list, configs, config_size, num_config); - } - return res; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config); } -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, + EGLint config_size, EGLint* num_config) { clearError(); - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (!dp) return EGL_FALSE; - - return cnx->egl.eglGetConfigAttrib( - dp->disp.dpy, config, attribute, value); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config); } -// ---------------------------------------------------------------------------- -// surfaces -// ---------------------------------------------------------------------------- +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) { + clearError(); -// Translates EGL color spaces to Android data spaces. -static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { - if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { - return HAL_DATASPACE_UNKNOWN; - } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { - return HAL_DATASPACE_SRGB; - } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { - return HAL_DATASPACE_DISPLAY_P3; - } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { - return HAL_DATASPACE_DISPLAY_P3_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { - return HAL_DATASPACE_V0_SCRGB; - } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { - return HAL_DATASPACE_V0_SCRGB_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { - return HAL_DATASPACE_BT2020_LINEAR; - } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { - return HAL_DATASPACE_BT2020_PQ; - } - return HAL_DATASPACE_UNKNOWN; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value); } -// Get the colorspace value that should be reported from queries. When the colorspace -// is unknown (no attribute passed), default to reporting LINEAR. -static EGLint getReportedColorSpace(EGLint colorspace) { - return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace; -} +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + clearError(); -// Returns a list of color spaces understood by the vendor EGL driver. -static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { - std::vector<EGLint> colorSpaces; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list); +} - // sRGB and linear are always supported when color space support is present. - colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR); - colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR); +EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + clearError(); - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_display_p3")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_scrgb")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_scrgb_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_bt2020_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_bt2020_pq")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT); - } - if (findExtension(dp->disp.queryString.extensions, - "EGL_EXT_gl_colorspace_display_p3_linear")) { - colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); - } - return colorSpaces; -} - -// Cleans up color space related parameters that the driver does not understand. -// If there is no color space attribute in attrib_list, colorSpace is left -// unmodified. -static EGLBoolean processAttributes(egl_display_ptr dp, NativeWindowType window, - const EGLint* attrib_list, EGLint* colorSpace, - std::vector<EGLint>* strippedAttribList) { - for (const EGLint* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { - bool copyAttribute = true; - if (attr[0] == EGL_GL_COLORSPACE_KHR) { - switch (attr[1]) { - case EGL_GL_COLORSPACE_LINEAR_KHR: - case EGL_GL_COLORSPACE_SRGB_KHR: - case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: - case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: - case EGL_GL_COLORSPACE_SCRGB_EXT: - case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: - case EGL_GL_COLORSPACE_BT2020_PQ_EXT: - case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT: - // Fail immediately if the driver doesn't have color space support at all. - if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - break; - default: - // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_* - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - } - *colorSpace = attr[1]; - - // Strip the attribute if the driver doesn't understand it. - copyAttribute = false; - std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp); - for (auto driverColorSpace : driverColorSpaces) { - if (attr[1] == driverColorSpace) { - copyAttribute = true; - break; - } - } - - // If the driver doesn't understand it, we should map sRGB-encoded P3 to - // sRGB rather than just dropping the colorspace on the floor. - // For this format, the driver is expected to apply the sRGB - // transfer function during framebuffer operations. - if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { - strippedAttribList->push_back(attr[0]); - strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR); - } - } - if (copyAttribute) { - strippedAttribList->push_back(attr[0]); - strippedAttribList->push_back(attr[1]); - } - } - // Terminate the attribute list. - strippedAttribList->push_back(EGL_NONE); - - // If the passed color space has wide color gamut, check whether the target native window - // supports wide color. - const bool colorSpaceIsNarrow = - *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR || - *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || - *colorSpace == EGL_UNKNOWN; - if (window && !colorSpaceIsNarrow) { - bool windowSupportsWideColor = true; - // Ordinarily we'd put a call to native_window_get_wide_color_support - // at the beginning of the function so that we'll have the - // result when needed elsewhere in the function. - // However, because eglCreateWindowSurface is called by SurfaceFlinger and - // SurfaceFlinger is required to answer the call below we would - // end up in a deadlock situation. By moving the call to only happen - // if the application has specifically asked for wide-color we avoid - // the deadlock with SurfaceFlinger since it will not ask for a - // wide-color surface. - int err = native_window_get_wide_color_support(window, &windowSupportsWideColor); - - if (err) { - ALOGE("processAttributes: invalid window (win=%p) " - "failed (%#x) (already connected to another API?)", - window, err); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE); - } - if (!windowSupportsWideColor) { - // Application has asked for a wide-color colorspace but - // wide-color support isn't available on the display the window is on. - return setError(EGL_BAD_MATCH, EGL_FALSE); - } - } - return true; -} - -// Gets the native pixel format corrsponding to the passed EGLConfig. -void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, - android_pixel_format* 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 - // modify the EGLconfig's format before setting the native window's - // format. - - EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType); - - EGLint a = 0; - EGLint r, g, b; - r = g = b = 0; - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); - cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); - EGLint colorDepth = r + g + b; - - // Today, the driver only understands sRGB and linear on 888X - // formats. Strip other colorspaces from the attribute list and - // only use them to set the dataspace via - // native_window_set_buffers_dataspace - // if pixel format is RGBX 8888 - // TBD: Can test for future extensions that indicate that driver - // handles requested color space and we can let it through. - // allow SRGB and LINEAR. All others need to be stripped. - // else if 565, 4444 - // TBD: Can we assume these are supported if 8888 is? - // else if FP16 or 1010102 - // strip colorspace from attribs. - // endif - if (a == 0) { - if (colorDepth <= 16) { - *format = HAL_PIXEL_FORMAT_RGB_565; - } else { - if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { - if (colorDepth > 24) { - *format = HAL_PIXEL_FORMAT_RGBA_1010102; - } else { - *format = HAL_PIXEL_FORMAT_RGBX_8888; - } - } else { - *format = HAL_PIXEL_FORMAT_RGBA_FP16; - } - } - } else { - if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { - if (colorDepth > 24) { - *format = HAL_PIXEL_FORMAT_RGBA_1010102; - } else { - *format = HAL_PIXEL_FORMAT_RGBA_8888; - } - } else { - *format = HAL_PIXEL_FORMAT_RGBA_FP16; - } - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list); } -EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { - android_smpte2086_metadata smpteMetadata; - if (s->getSmpte2086Metadata(smpteMetadata)) { - int err = - native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata); - s->resetSmpte2086Metadata(); - if (err != 0) { - ALOGE("error setting native window smpte2086 metadata: %s (%d)", - strerror(-err), err); - return EGL_FALSE; - } - } - android_cta861_3_metadata cta8613Metadata; - if (s->getCta8613Metadata(cta8613Metadata)) { - int err = - native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata); - s->resetCta8613Metadata(); - if (err != 0) { - ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", - strerror(-err), err); - return EGL_FALSE; - } - } - return EGL_TRUE; -} - -EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, - NativeWindowType window, - const EGLint *attrib_list) -{ - const EGLint *origAttribList = attrib_list; - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - if (!window) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - int value = 0; - window->query(window, NATIVE_WINDOW_IS_VALID, &value); - if (!value) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); - if (result < 0) { - ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " - "failed (%#x) (already connected to another API?)", - window, result); - return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - } - - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // now select correct colorspace and dataspace based on user's attribute list - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, window, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - { - int err = native_window_set_buffers_format(window, 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); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - } - - android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); - // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. HAL_DATASPACE_UNKNOWN - // is the default value, but it may have changed at this point. - int err = native_window_set_buffers_data_space(window, dataSpace); - if (err != 0) { - ALOGE("error setting native window pixel dataSpace: %s (%d)", - strerror(-err), err); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - // the EGL spec requires that a new EGLSurface default to swap interval - // 1, so explicitly set that on the window here. - ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window); - anw->setSwapInterval(anw, 1); - - EGLSurface surface = cnx->egl.eglCreateWindowSurface( - iDpy, config, window, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, window, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - - // EGLSurface creation failed - native_window_set_buffers_format(window, 0); - native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); - } - return EGL_NO_SURFACE; -} - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // now select a corresponding sRGB format if needed - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - EGLSurface surface = cnx->egl.eglCreatePixmapSurface( - dp->disp.dpy, config, pixmap, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, NULL, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - } - return EGL_NO_SURFACE; -} - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // Select correct colorspace based on user's attribute list - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, - &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); - - EGLSurface surface = cnx->egl.eglCreatePbufferSurface( - dp->disp.dpy, config, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = - new egl_surface_t(dp.get(), config, NULL, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } - } - return EGL_NO_SURFACE; +EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, + const EGLint* attrib_list) { + clearError(); + + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); } -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) -{ +EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, + const EGLAttrib* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { + clearError(); - egl_surface_t * const s = get_surface(surface); - EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); - if (result == EGL_TRUE) { - _s.terminate(); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePbufferSurface(dpy, config, attrib_list); } -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySurface(dpy, surface); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->getColorSpaceAttribute(attribute, value)) { - return EGL_TRUE; - } else if (s->getSmpte2086Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->getCta8613Attribute(attribute, value)) { - return EGL_TRUE; - } - return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQuerySurface(dpy, surface, attribute, value); } void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) { ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return; - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + cnx->platform.eglBeginFrame(dpy, surface); } -// ---------------------------------------------------------------------------- -// Contexts -// ---------------------------------------------------------------------------- +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list, + const EGLint* attrib_list) { + clearError(); -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext share_list, const EGLint *attrib_list) -{ - clearError(); - - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - if (share_list != EGL_NO_CONTEXT) { - if (!ContextRef(dp.get(), share_list).get()) { - return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); - } - egl_context_t* const c = get_context(share_list); - share_list = c->context; - } - EGLContext context = cnx->egl.eglCreateContext( - dp->disp.dpy, config, share_list, attrib_list); - if (context != EGL_NO_CONTEXT) { - // figure out if it's a GLESv1 or GLESv2 - int version = 0; - if (attrib_list) { - while (*attrib_list != EGL_NONE) { - GLint attr = *attrib_list++; - GLint value = *attrib_list++; - if (attr == EGL_CONTEXT_CLIENT_VERSION) { - if (value == 1) { - version = egl_connection_t::GLESv1_INDEX; - } else if (value == 2 || value == 3) { - version = egl_connection_t::GLESv2_INDEX; - } - } - }; - } - egl_context_t* c = new egl_context_t(dpy, context, config, cnx, - version); - return c; - } - } - return EGL_NO_CONTEXT; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list); } -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) -{ +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) - return EGL_FALSE; - - ContextRef _c(dp.get(), ctx); - if (!_c.get()) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - egl_context_t * const c = get_context(ctx); - EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); - if (result == EGL_TRUE) { - _c.terminate(); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroyContext(dpy, ctx); } -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ +EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { clearError(); - egl_display_ptr dp = validate_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not - // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is - // a valid but uninitialized display. - if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || - (draw != EGL_NO_SURFACE) ) { - if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } - - // get a reference to the object passed in - ContextRef _c(dp.get(), ctx); - SurfaceRef _d(dp.get(), draw); - SurfaceRef _r(dp.get(), read); - - // validate the context (if not EGL_NO_CONTEXT) - if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { - // EGL_NO_CONTEXT is valid - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - } - - // these are the underlying implementation's object - EGLContext impl_ctx = EGL_NO_CONTEXT; - EGLSurface impl_draw = EGL_NO_SURFACE; - EGLSurface impl_read = EGL_NO_SURFACE; - - // these are our objects structs passed in - egl_context_t * c = NULL; - egl_surface_t const * d = NULL; - egl_surface_t const * r = NULL; - - // these are the current objects structs - egl_context_t * cur_c = get_context(getContext()); - - if (ctx != EGL_NO_CONTEXT) { - c = get_context(ctx); - impl_ctx = c->context; - } else { - // no context given, use the implementation of the current context - if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { - // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); - return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE); - } - if (cur_c == NULL) { - // no current context - // not an error, there is just no current context. - return EGL_TRUE; - } - } - - // retrieve the underlying implementation's draw EGLSurface - if (draw != EGL_NO_SURFACE) { - if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - d = get_surface(draw); - impl_draw = d->surface; - } - - // retrieve the underlying implementation's read EGLSurface - if (read != EGL_NO_SURFACE) { - if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - r = get_surface(read); - impl_read = r->surface; - } - - - EGLBoolean result = dp->makeCurrent(c, cur_c, - draw, read, ctx, - impl_draw, impl_read, impl_ctx); - - if (result == EGL_TRUE) { - if (c) { - setGLHooksThreadSpecific(c->cnx->hooks[c->version]); - egl_tls_t::setContext(ctx); - _c.acquire(); - _r.acquire(); - _d.acquire(); - } else { - setGLHooksThreadSpecific(&gHooksNoContext); - egl_tls_t::setContext(EGL_NO_CONTEXT); - } - } else { - // this will ALOGE the error - egl_connection_t* const cnx = &gEGLImpl; - result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE); - } - return result; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx); } - -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - ContextRef _c(dp.get(), ctx); - if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - egl_context_t * const c = get_context(ctx); - return c->cnx->egl.eglQueryContext( - dp->disp.dpy, c->context, attribute, value); - + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryContext(dpy, ctx, attribute, value); } -EGLContext eglGetCurrentContext(void) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_CONTEXT. - +EGLContext eglGetCurrentContext(void) { clearError(); - EGLContext ctx = getContext(); - return ctx; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentContext(); } -EGLSurface eglGetCurrentSurface(EGLint readdraw) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_SURFACE. - +EGLSurface eglGetCurrentSurface(EGLint readdraw) { clearError(); - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); - switch (readdraw) { - case EGL_READ: return c->read; - case EGL_DRAW: return c->draw; - default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); - } - } - return EGL_NO_SURFACE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentSurface(readdraw); } -EGLDisplay eglGetCurrentDisplay(void) -{ - // could be called before eglInitialize(), but we wouldn't have a context - // then, and this function would correctly return EGL_NO_DISPLAY. - +EGLDisplay eglGetCurrentDisplay(void) { clearError(); - EGLContext ctx = getContext(); - if (ctx) { - egl_context_t const * const c = get_context(ctx); - if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); - return c->dpy; - } - return EGL_NO_DISPLAY; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentDisplay(); } -EGLBoolean eglWaitGL(void) -{ +EGLBoolean eglWaitGL(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - return cnx->egl.eglWaitGL(); + return cnx->platform.eglWaitGL(); } -EGLBoolean eglWaitNative(EGLint engine) -{ +EGLBoolean eglWaitNative(EGLint engine) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - return cnx->egl.eglWaitNative(engine); + return cnx->platform.eglWaitNative(engine); } -EGLint eglGetError(void) -{ - EGLint err = EGL_SUCCESS; +EGLint eglGetError(void) { egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - err = cnx->egl.eglGetError(); - } - if (err == EGL_SUCCESS) { - err = egl_tls_t::getError(); - } - return err; -} - -static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( - const char* procname) { - const egl_connection_t* cnx = &gEGLImpl; - void* proc = NULL; - - proc = dlsym(cnx->libEgl, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - proc = dlsym(cnx->libGles2, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - proc = dlsym(cnx->libGles1, procname); - if (proc) return (__eglMustCastToProperFunctionPointerType)proc; - - return NULL; + return cnx->platform.eglGetError(); } -__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) -{ +__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) { // eglGetProcAddress() could be the very first function called // in which case we must make sure we've initialized ourselves, this // happens the first time egl_get_display() is called. @@ -1188,431 +243,98 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); - return NULL; - } - - if (FILTER_EXTENSIONS(procname)) { - return NULL; - } - - __eglMustCastToProperFunctionPointerType addr; - addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap)); - if (addr) return addr; - - addr = findBuiltinWrapper(procname); - if (addr) return addr; - - // this protects accesses to sGLExtentionMap and sGLExtentionSlot - pthread_mutex_lock(&sExtensionMapMutex); - - /* - * Since eglGetProcAddress() is not associated to anything, it needs - * to return a function pointer that "works" regardless of what - * the current context is. - * - * For this reason, we return a "forwarder", a small stub that takes - * care of calling the function associated with the context - * currently bound. - * - * We first look for extensions we've already resolved, if we're seeing - * this extension for the first time, we go through all our - * implementations and call eglGetProcAddress() and record the - * result in the appropriate implementation hooks and return the - * address of the forwarder corresponding to that hook set. - * - */ - - const std::string name(procname); - - auto& extentionMap = sGLExtentionMap; - auto pos = extentionMap.find(name); - addr = (pos != extentionMap.end()) ? pos->second : nullptr; - const int slot = sGLExtentionSlot; - - ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, - "no more slots for eglGetProcAddress(\"%s\")", - procname); - - if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { - bool found = false; - - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetProcAddress) { - // Extensions are independent of the bound context - addr = - cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = - cnx->egl.eglGetProcAddress(procname); - if (addr) found = true; - } - - if (found) { - addr = gExtensionForwarders[slot]; - extentionMap[name] = addr; - sGLExtentionSlot++; - } - } - - pthread_mutex_unlock(&sExtensionMapMutex); - return addr; -} - -class FrameCompletionThread { -public: - - static void queueSync(EGLSyncKHR sync) { - static FrameCompletionThread thread; - - char name[64]; - - std::lock_guard<std::mutex> lock(thread.mMutex); - snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued); - ATRACE_NAME(name); - - thread.mQueue.push_back(sync); - thread.mCondition.notify_one(); - thread.mFramesQueued++; - ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size())); - } - -private: - - FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { - std::thread thread(&FrameCompletionThread::loop, this); - thread.detach(); - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-noreturn" - void loop() { - while (true) { - threadLoop(); - } - } -#pragma clang diagnostic pop - - void threadLoop() { - EGLSyncKHR sync; - uint32_t frameNum; - { - std::unique_lock<std::mutex> lock(mMutex); - while (mQueue.empty()) { - mCondition.wait(lock); - } - sync = mQueue[0]; - frameNum = mFramesCompleted; - } - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - { - char name[64]; - snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum); - ATRACE_NAME(name); - - EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); - if (result == EGL_FALSE) { - ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("FrameCompletion: timeout waiting for fence"); - } - eglDestroySyncKHR(dpy, sync); - } - { - std::lock_guard<std::mutex> lock(mMutex); - mQueue.pop_front(); - mFramesCompleted++; - ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size())); - } + return nullptr; } - uint32_t mFramesQueued; - uint32_t mFramesCompleted; - std::deque<EGLSyncKHR> mQueue; - std::condition_variable mCondition; - std::mutex mMutex; -}; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetProcAddress(procname); +} -EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, - EGLint *rects, EGLint n_rects) -{ +EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects, + EGLint n_rects) { ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), draw); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t* const s = get_surface(draw); - - if (CC_UNLIKELY(dp->traceGpuCompletion)) { - EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); - if (sync != EGL_NO_SYNC_KHR) { - FrameCompletionThread::queueSync(sync); - } - } - - if (CC_UNLIKELY(dp->finishOnSwap)) { - uint32_t pixel; - egl_context_t * const c = get_context( egl_tls_t::getContext() ); - if (c) { - // glReadPixels() ensures that the frame is complete - s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, - GL_RGBA,GL_UNSIGNED_BYTE,&pixel); - } - } - - if (!sendSurfaceMetadata(s)) { - native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL); - return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE); - } - - if (n_rects == 0) { - return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); - } - - std::vector<android_native_rect_t> androidRects((size_t)n_rects); - for (int r = 0; r < n_rects; ++r) { - int offset = r * 4; - int x = rects[offset]; - int y = rects[offset + 1]; - int width = rects[offset + 2]; - int height = rects[offset + 3]; - android_native_rect_t androidRect; - androidRect.left = x; - androidRect.top = y + height; - androidRect.right = x + width; - androidRect.bottom = y; - androidRects.push_back(androidRect); - } - native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size()); - - if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { - return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, - rects, n_rects); - } else { - return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); - } -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) -{ - return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects); } -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target) -{ +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + ATRACE_CALL(); clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffers(dpy, surface); } -const char* eglQueryString(EGLDisplay dpy, EGLint name) -{ +EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) { clearError(); - // Generate an error quietly when client extensions (as defined by - // EGL_EXT_client_extensions) are queried. We do not want to rely on - // validate_display to generate the error as validate_display would log - // the error, which can be misleading. - // - // If we want to support EGL_EXT_client_extensions later, we can return - // the client extension string here instead. - if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) - return setErrorQuiet(EGL_BAD_DISPLAY, (const char*)0); - - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) NULL; - - switch (name) { - case EGL_VENDOR: - return dp->getVendorString(); - case EGL_VERSION: - return dp->getVersionString(); - case EGL_EXTENSIONS: - return dp->getExtensionString(); - case EGL_CLIENT_APIS: - return dp->getClientApiString(); - default: - break; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCopyBuffers(dpy, surface, target); } -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) -{ +const char* eglQueryString(EGLDisplay dpy, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) NULL; - - switch (name) { - case EGL_VENDOR: - return dp->disp.queryString.vendor; - case EGL_VERSION: - return dp->disp.queryString.version; - case EGL_EXTENSIONS: - return dp->disp.queryString.extensions; - case EGL_CLIENT_APIS: - return dp->disp.queryString.clientApi; - default: - break; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryString(dpy, name); } -// ---------------------------------------------------------------------------- -// EGL 1.1 -// ---------------------------------------------------------------------------- - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) -{ +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t * const s = get_surface(surface); - - if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { - if (!s->getNativeWindow()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - } - int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - if (attribute == EGL_TIMESTAMPS_ANDROID) { - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); - return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - if (s->setSmpte2086Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->setCta8613Attribute(attribute, value)) { - return EGL_TRUE; - } else if (s->cnx->egl.eglSurfaceAttrib) { - return s->cnx->egl.eglSurfaceAttrib( - dp->disp.dpy, s->surface, attribute, value); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryStringImplementationANDROID(dpy, name); } -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglBindTexImage) { - return s->cnx->egl.eglBindTexImage( - dp->disp.dpy, s->surface, buffer); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value); } -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglReleaseTexImage) { - return s->cnx->egl.eglReleaseTexImage( - dp->disp.dpy, s->surface, buffer); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglBindTexImage(dpy, surface, buffer); } -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) -{ +EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean res = EGL_TRUE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglSwapInterval) { - res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); - } - - return res; + return cnx->platform.eglReleaseTexImage(dpy, surface, buffer); } +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { + clearError(); -// ---------------------------------------------------------------------------- -// EGL 1.2 -// ---------------------------------------------------------------------------- + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapInterval(dpy, interval); +} -EGLBoolean eglWaitClient(void) -{ +EGLBoolean eglWaitClient(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - - EGLBoolean res; - if (cnx->egl.eglWaitClient) { - res = cnx->egl.eglWaitClient(); - } else { - res = cnx->egl.eglWaitGL(); - } - return res; + return cnx->platform.eglWaitClient(); } -EGLBoolean eglBindAPI(EGLenum api) -{ +EGLBoolean eglBindAPI(EGLenum api) { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } - // bind this API on all EGLs - EGLBoolean res = EGL_TRUE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglBindAPI) { - res = cnx->egl.eglBindAPI(api); - } - return res; + return cnx->platform.eglBindAPI(api); } -EGLenum eglQueryAPI(void) -{ +EGLenum eglQueryAPI(void) { clearError(); if (egl_init_drivers() == EGL_FALSE) { @@ -1620,824 +342,324 @@ EGLenum eglQueryAPI(void) } egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryAPI) { - return cnx->egl.eglQueryAPI(); - } - - // or, it can only be OpenGL ES - return EGL_OPENGL_ES_API; + return cnx->platform.eglQueryAPI(); } -EGLBoolean eglReleaseThread(void) -{ +EGLBoolean eglReleaseThread(void) { clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglReleaseThread) { - cnx->egl.eglReleaseThread(); - } - - // If there is context bound to the thread, release it - egl_display_t::loseCurrent(get_context(getContext())); - - egl_tls_t::clearTLS(); - return EGL_TRUE; + return cnx->platform.eglReleaseThread(); } -EGLSurface eglCreatePbufferFromClientBuffer( - EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, - EGLConfig config, const EGLint *attrib_list) -{ +EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint* attrib_list) { clearError(); - egl_connection_t* cnx = NULL; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (!dp) return EGL_FALSE; - if (cnx->egl.eglCreatePbufferFromClientBuffer) { - return cnx->egl.eglCreatePbufferFromClientBuffer( - dp->disp.dpy, buftype, buffer, config, attrib_list); - } - return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePbufferFromClientBuffer(dpy, buftype, buffer, config, + attrib_list); } -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 3 -// ---------------------------------------------------------------------------- - -EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, - const EGLint *attrib_list) -{ +EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglLockSurfaceKHR) { - return s->cnx->egl.eglLockSurfaceKHR( - dp->disp.dpy, s->surface, attrib_list); - } - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglUnlockSurfaceKHR(dpy, surface); } -EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) -{ +EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + clearError(); - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglUnlockSurfaceKHR) { - return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); - } - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateImage(dpy, ctx, target, buffer, attrib_list); } -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint *attrib_list) -{ - clearError(); - - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_IMAGE_KHR; - - ContextRef _c(dp.get(), ctx); - egl_context_t * const c = _c.get(); - - // Temporary hack: eglImageCreateKHR should accept EGL_GL_COLORSPACE_LINEAR_KHR, - // EGL_GL_COLORSPACE_SRGB_KHR and EGL_GL_COLORSPACE_DEFAULT_EXT if - // EGL_EXT_image_gl_colorspace is supported, but some drivers don't like - // the DEFAULT value and generate an error. - std::vector<EGLint> strippedAttribList; - for (const EGLint *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { - if (attr[0] == EGL_GL_COLORSPACE_KHR && - dp->haveExtension("EGL_EXT_image_gl_colorspace")) { - if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR && - attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) { - continue; - } - } - strippedAttribList.push_back(attr[0]); - strippedAttribList.push_back(attr[1]); - } - strippedAttribList.push_back(EGL_NONE); +EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) { + clearError(); - EGLImageKHR result = EGL_NO_IMAGE_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateImageKHR) { - result = cnx->egl.eglCreateImageKHR( - dp->disp.dpy, - c ? c->context : EGL_NO_CONTEXT, - target, buffer, strippedAttribList.data()); - } - return result; + return cnx->platform.eglDestroyImageKHR(dpy, img); } -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) -{ +EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroyImageKHR) { - result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img); - } - return result; + return cnx->platform.eglDestroyImage(dpy, img); } // ---------------------------------------------------------------------------- // EGL_EGLEXT_VERSION 5 // ---------------------------------------------------------------------------- - -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) -{ +EGLSyncKHR eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_SYNC_KHR; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateSync(dpy, type, attrib_list); +} + +EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + clearError(); - EGLSyncKHR result = EGL_NO_SYNC_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateSyncKHR) { - result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list); - } - return result; + return cnx->platform.eglCreateSyncKHR(dpy, type, attrib_list); } -EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySync(dpy, sync); +} + +EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroySyncKHR) { - result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync); - } - return result; + return cnx->platform.eglDestroySyncKHR(dpy, sync); } EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglSignalSyncKHR) { - result = cnx->egl.eglSignalSyncKHR( - dp->disp.dpy, sync, mode); - } - return result; + return cnx->platform.eglSignalSyncKHR(dpy, sync, mode); } -EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, - EGLint flags, EGLTimeKHR timeout) -{ +EGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLint result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) { - result = cnx->egl.eglClientWaitSyncKHR( - dp->disp.dpy, sync, flags, timeout); - } - return result; + return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout); } -EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, - EGLint attribute, EGLint *value) -{ +EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) { - result = cnx->egl.eglGetSyncAttribKHR( - dp->disp.dpy, sync, attribute, value); - } - return result; + return cnx->platform.eglClientWaitSyncKHR(dpy, sync, flags, timeout); } -EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list) -{ +EGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_STREAM_KHR; - - EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamKHR) { - result = cnx->egl.eglCreateStreamKHR( - dp->disp.dpy, attrib_list); - } - return result; + return cnx->platform.eglGetSyncAttrib(dpy, sync, attribute, value); } -EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) -{ +EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { - result = cnx->egl.eglDestroyStreamKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglGetSyncAttribKHR(dpy, sync, attribute, value); } -EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint value) -{ +EGLStreamKHR eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamAttribKHR) { - result = cnx->egl.eglStreamAttribKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglCreateStreamKHR(dpy, attrib_list); } -EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint *value) -{ +EGLBoolean eglDestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamKHR) { - result = cnx->egl.eglQueryStreamKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglDestroyStreamKHR(dpy, stream); } -EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLuint64KHR *value) -{ +EGLBoolean eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { - result = cnx->egl.eglQueryStreamu64KHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglStreamAttribKHR(dpy, stream, attribute, value); } -EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLTimeKHR *value) -{ +EGLBoolean eglQueryStreamKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { - result = cnx->egl.eglQueryStreamTimeKHR( - dp->disp.dpy, stream, attribute, value); - } - return result; + return cnx->platform.eglQueryStreamKHR(dpy, stream, attribute, value); } -EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, - EGLStreamKHR stream, const EGLint *attrib_list) -{ +EGLBoolean eglQueryStreamu64KHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLuint64KHR* value) { clearError(); - egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_SURFACE; - egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { - EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( - dp->disp.dpy, config, stream, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL, surface, - EGL_GL_COLORSPACE_LINEAR_KHR, cnx); - return s; - } - } - return EGL_NO_SURFACE; + return cnx->platform.eglQueryStreamu64KHR(dpy, stream, attribute, value); } -EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLBoolean eglQueryStreamTimeKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLTimeKHR* value) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { - result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglQueryStreamTimeKHR(dpy, stream, attribute, value); } -EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLSurface eglCreateStreamProducerSurfaceKHR(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, + const EGLint* attrib_list) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { - result = cnx->egl.eglStreamConsumerAcquireKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglCreateStreamProducerSurfaceKHR(dpy, config, stream, attrib_list); } -EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, - EGLStreamKHR stream) -{ +EGLBoolean eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { - result = cnx->egl.eglStreamConsumerReleaseKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglStreamConsumerGLTextureExternalKHR(dpy, stream); } -EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR( - EGLDisplay dpy, EGLStreamKHR stream) -{ +EGLBoolean eglStreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; - - EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { - result = cnx->egl.eglGetStreamFileDescriptorKHR( - dp->disp.dpy, stream); - } - return result; + return cnx->platform.eglStreamConsumerAcquireKHR(dpy, stream); } -EGLStreamKHR eglCreateStreamFromFileDescriptorKHR( - EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) -{ +EGLBoolean eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_STREAM_KHR; - - EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { - result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( - dp->disp.dpy, file_descriptor); - } - return result; + return cnx->platform.eglStreamConsumerReleaseKHR(dpy, stream); } -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 15 -// ---------------------------------------------------------------------------- - -EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - EGLint result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglWaitSyncKHR) { - result = cnx->egl.eglWaitSyncKHR(dp->disp.dpy, sync, flags); - } - return result; + return cnx->platform.eglGetStreamFileDescriptorKHR(dpy, stream); } -// ---------------------------------------------------------------------------- -// ANDROID extensions -// ---------------------------------------------------------------------------- - -EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy, + EGLNativeFileDescriptorKHR file_descriptor) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor); +} - EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; +EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + clearError(); egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { - result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); - } - return result; + return cnx->platform.eglWaitSyncKHR(dpy, sync, flags); } -EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, - EGLnsecsANDROID time) -{ +EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) { clearError(); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglWaitSync(dpy, sync, flags); +} - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return EGL_FALSE; - } +EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync); +} - egl_surface_t const * const s = get_surface(surface); - native_window_set_buffers_timestamp(s->getNativeWindow(), time); +EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) { + clearError(); - return EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time); } -EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) { +EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) { clearError(); - // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus - // this function cannot be implemented when this libEGL is built for - // vendors. -#ifndef __ANDROID_VNDK__ - if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); - return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); -#else - return setError(EGL_BAD_PARAMETER, (EGLClientBuffer)0); -#endif + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetNativeClientBufferANDROID(buffer); } -// ---------------------------------------------------------------------------- -// NVIDIA extensions -// ---------------------------------------------------------------------------- -EGLuint64NV eglGetSystemTimeFrequencyNV() -{ +EGLuint64NV eglGetSystemTimeFrequencyNV() { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } - EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; - - if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { - return cnx->egl.eglGetSystemTimeFrequencyNV(); - } - - return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); + return cnx->platform.eglGetSystemTimeFrequencyNV(); } -EGLuint64NV eglGetSystemTimeNV() -{ +EGLuint64NV eglGetSystemTimeNV() { clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } - EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; - - if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { - return cnx->egl.eglGetSystemTimeNV(); - } - - return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); + return cnx->platform.eglGetSystemTimeNV(); } -// ---------------------------------------------------------------------------- -// Partial update extension -// ---------------------------------------------------------------------------- -EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, - EGLint *rects, EGLint n_rects) -{ +EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint* rects, + EGLint n_rects) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - setError(EGL_BAD_DISPLAY, EGL_FALSE); - return EGL_FALSE; - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - return EGL_FALSE; - } - - egl_surface_t const * const s = get_surface(surface); - if (s->cnx->egl.eglSetDamageRegionKHR) { - return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, - rects, n_rects); - } - - return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSetDamageRegionKHR(dpy, surface, rects, n_rects); } -EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR *frameId) { +EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - uint64_t nextFrameId = 0; - int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId); - - if (ret != 0) { - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetNextFrameId: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } - - *frameId = nextFrameId; - return EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetNextFrameIdANDROID(dpy, surface, frameId); } -EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, - EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) -{ +EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, + const EGLint* names, EGLnsecsANDROID* values) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - nsecs_t* compositeDeadline = nullptr; - nsecs_t* compositeInterval = nullptr; - nsecs_t* compositeToPresentLatency = nullptr; - - for (int i = 0; i < numTimestamps; i++) { - switch (names[i]) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - compositeDeadline = &values[i]; - break; - case EGL_COMPOSITE_INTERVAL_ANDROID: - compositeInterval = &values[i]; - break; - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - compositeToPresentLatency = &values[i]; - break; - default: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - } - - int ret = native_window_get_compositor_timing(s->getNativeWindow(), - compositeDeadline, compositeInterval, compositeToPresentLatency); - - switch (ret) { - case 0: - return EGL_TRUE; - case -ENOSYS: - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - default: - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetCompositorTiming: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCompositorTimingANDROID(dpy, surface, numTimestamps, names, values); } -EGLBoolean eglGetCompositorTimingSupportedANDROID( - EGLDisplay dpy, EGLSurface surface, EGLint name) -{ +EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - ANativeWindow* window = s->getNativeWindow(); - if (!window) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - switch (name) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - case EGL_COMPOSITE_INTERVAL_ANDROID: - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - return EGL_TRUE; - default: - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCompositorTimingSupportedANDROID(dpy, surface, name); } -EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, - EGLnsecsANDROID *values) -{ +EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, + EGLint numTimestamps, const EGLint* timestamps, + EGLnsecsANDROID* values) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - if (!s->getNativeWindow()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - nsecs_t* requestedPresentTime = nullptr; - nsecs_t* acquireTime = nullptr; - nsecs_t* latchTime = nullptr; - nsecs_t* firstRefreshStartTime = nullptr; - nsecs_t* gpuCompositionDoneTime = nullptr; - nsecs_t* lastRefreshStartTime = nullptr; - nsecs_t* displayPresentTime = nullptr; - nsecs_t* dequeueReadyTime = nullptr; - nsecs_t* releaseTime = nullptr; - - for (int i = 0; i < numTimestamps; i++) { - switch (timestamps[i]) { - case EGL_REQUESTED_PRESENT_TIME_ANDROID: - requestedPresentTime = &values[i]; - break; - case EGL_RENDERING_COMPLETE_TIME_ANDROID: - acquireTime = &values[i]; - break; - case EGL_COMPOSITION_LATCH_TIME_ANDROID: - latchTime = &values[i]; - break; - case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: - firstRefreshStartTime = &values[i]; - break; - case EGL_LAST_COMPOSITION_START_TIME_ANDROID: - lastRefreshStartTime = &values[i]; - break; - case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: - gpuCompositionDoneTime = &values[i]; - break; - case EGL_DISPLAY_PRESENT_TIME_ANDROID: - displayPresentTime = &values[i]; - break; - case EGL_DEQUEUE_READY_TIME_ANDROID: - dequeueReadyTime = &values[i]; - break; - case EGL_READS_DONE_TIME_ANDROID: - releaseTime = &values[i]; - break; - default: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - } - } - - int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, - requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, - lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, - dequeueReadyTime, releaseTime); - - switch (ret) { - case 0: - return EGL_TRUE; - case -ENOENT: - return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); - case -ENOSYS: - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - case -EINVAL: - return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); - default: - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetFrameTimestamps: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetFrameTimestampsANDROID(dpy, surface, frameId, numTimestamps, + timestamps, values); } -EGLBoolean eglGetFrameTimestampSupportedANDROID( - EGLDisplay dpy, EGLSurface surface, EGLint timestamp) -{ +EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint timestamp) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - } - - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - egl_surface_t const * const s = get_surface(surface); - - ANativeWindow* window = s->getNativeWindow(); - if (!window) { - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - } - - switch (timestamp) { - case EGL_COMPOSITE_DEADLINE_ANDROID: - case EGL_COMPOSITE_INTERVAL_ANDROID: - case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: - case EGL_REQUESTED_PRESENT_TIME_ANDROID: - case EGL_RENDERING_COMPLETE_TIME_ANDROID: - case EGL_COMPOSITION_LATCH_TIME_ANDROID: - case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: - case EGL_LAST_COMPOSITION_START_TIME_ANDROID: - case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: - case EGL_DEQUEUE_READY_TIME_ANDROID: - case EGL_READS_DONE_TIME_ANDROID: - return EGL_TRUE; - case EGL_DISPLAY_PRESENT_TIME_ANDROID: { - int value = 0; - window->query(window, - NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); - return value == 0 ? EGL_FALSE : EGL_TRUE; - } - default: - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetFrameTimestampSupportedANDROID(dpy, surface, timestamp); } diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp new file mode 100644 index 0000000000..7e0175ac74 --- /dev/null +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#if defined(__ANDROID__) + +#include "egl_angle_platform.h" +#include <time.h> + +#include <log/log.h> + +namespace angle { + +GetDisplayPlatformFunc AnglePlatformImpl::angleGetDisplayPlatform = nullptr; +ResetDisplayPlatformFunc AnglePlatformImpl::angleResetDisplayPlatform = nullptr; +// Initialize start time +time_t AnglePlatformImpl::startTime = time(nullptr); + +void AnglePlatformImpl::assignAnglePlatformMethods(PlatformMethods* platformMethods) { + platformMethods->addTraceEvent = addTraceEvent; + platformMethods->getTraceCategoryEnabledFlag = getTraceCategoryEnabledFlag; + platformMethods->monotonicallyIncreasingTime = monotonicallyIncreasingTime; + platformMethods->logError = logError; + platformMethods->logWarning = logWarning; + platformMethods->logInfo = logInfo; +} + +const unsigned char* AnglePlatformImpl::getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/, + const char* /*categoryName*/) { + // Returning ptr to 'g' (non-zero) to ALWAYS enable tracing initially. + // This ptr is what will be passed into "category_group_enabled" of addTraceEvent + static const unsigned char traceEnabled = 'g'; + return &traceEnabled; +} + +double AnglePlatformImpl::monotonicallyIncreasingTime(PlatformMethods* /*platform*/) { + return difftime(time(nullptr), startTime); +} + +void AnglePlatformImpl::logError(PlatformMethods* /*platform*/, const char* errorMessage) { + ALOGE("ANGLE Error:%s", errorMessage); +} + +void AnglePlatformImpl::logWarning(PlatformMethods* /*platform*/, const char* warningMessage) { + ALOGW("ANGLE Warn:%s", warningMessage); +} + +void AnglePlatformImpl::logInfo(PlatformMethods* /*platform*/, const char* infoMessage) { + ALOGD("ANGLE Info:%s", infoMessage); +} + +TraceEventHandle AnglePlatformImpl::addTraceEvent( + PlatformMethods* /**platform*/, char phase, const unsigned char* /*category_group_enabled*/, + const char* name, unsigned long long /*id*/, double /*timestamp*/, int /*num_args*/, + const char** /*arg_names*/, const unsigned char* /*arg_types*/, + const unsigned long long* /*arg_values*/, unsigned char /*flags*/) { + switch (phase) { + case 'B': { + ATRACE_BEGIN(name); + break; + } + case 'E': { + ATRACE_END(); + break; + } + case 'I': { + ATRACE_NAME(name); + break; + } + default: + // Could handle other event types here + break; + } + // Return any non-zero handle to avoid assert in ANGLE + TraceEventHandle result = 1.0; + return result; +} + +}; // namespace angle + +#endif // __ANDROID__ diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h new file mode 100644 index 0000000000..7c6c8ed135 --- /dev/null +++ b/opengl/libs/EGL/egl_angle_platform.h @@ -0,0 +1,58 @@ +/* + * 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 + +#if defined(__ANDROID__) + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include <EGL/Platform.h> +#pragma GCC diagnostic pop + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "egl_trace.h" + +namespace angle { + +class AnglePlatformImpl { +public: + static void assignAnglePlatformMethods(PlatformMethods* platformMethods); + static GetDisplayPlatformFunc angleGetDisplayPlatform; + static ResetDisplayPlatformFunc angleResetDisplayPlatform; + +private: + static time_t startTime; + static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/, + const char* /*categoryName*/); + static double monotonicallyIncreasingTime(PlatformMethods* /*platform*/); + static void logError(PlatformMethods* /*platform*/, const char* errorMessage); + static void logWarning(PlatformMethods* /*platform*/, const char* warningMessage); + static void logInfo(PlatformMethods* /*platform*/, const char* infoMessage); + static TraceEventHandle addTraceEvent(PlatformMethods* /**platform*/, char phase, + const unsigned char* /*category_group_enabled*/, + const char* name, unsigned long long /*id*/, + double /*timestamp*/, int /*num_args*/, + const char** /*arg_names*/, + const unsigned char* /*arg_types*/, + const unsigned long long* /*arg_values*/, + unsigned char /*flags*/); +}; + +}; // namespace angle + +#endif // __ANDROID__ diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index ec548f3121..bcf496164b 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -95,7 +95,7 @@ void egl_cache_t::initialize(egl_display_t *display) { reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( cnx->egl.eglGetProcAddress( "eglSetBlobCacheFuncsANDROID")); - if (eglSetBlobCacheFuncsANDROID == NULL) { + if (eglSetBlobCacheFuncsANDROID == nullptr) { ALOGE("EGL_ANDROID_blob_cache advertised, " "but unable to get eglSetBlobCacheFuncsANDROID"); return; @@ -119,7 +119,7 @@ void egl_cache_t::terminate() { if (mBlobCache) { mBlobCache->writeToFile(); } - mBlobCache = NULL; + mBlobCache = nullptr; } void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 2aec249aa5..7cf58b4eee 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -21,14 +21,19 @@ #include "../egl_impl.h" +#include <EGL/eglext_angle.h> #include <private/EGL/display.h> +#include <cutils/properties.h> +#include "Loader.h" +#include "egl_angle_platform.h" #include "egl_cache.h" #include "egl_object.h" #include "egl_tls.h" -#include "egl_trace.h" -#include "Loader.h" -#include <cutils/properties.h> + +#include <android/dlext.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> @@ -41,7 +46,8 @@ namespace android { // ---------------------------------------------------------------------------- static char const * const sVendorString = "Android"; -static char const * const sVersionString = "1.4 Android META-EGL"; +static char const* const sVersionString14 = "1.4 Android META-EGL"; +static char const* const sVersionString15 = "1.5 Android META-EGL"; static char const * const sClientApiString = "OpenGL_ES"; extern char const * const gBuiltinExtensionString; @@ -114,15 +120,128 @@ bool egl_display_t::getObject(egl_object_t* object) const { return false; } -EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp) { +EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp, + const EGLAttrib* attrib_list) { if (uintptr_t(disp) >= NUM_DISPLAYS) - return NULL; + return nullptr; + + return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list); +} + +static bool addAnglePlatformAttributes(egl_connection_t* const cnx, + std::vector<EGLAttrib>& attrs) { + intptr_t vendorEGL = (intptr_t)cnx->vendorEGL; + + attrs.reserve(4 * 2); + + attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); + attrs.push_back(cnx->angleBackend); + + switch (cnx->angleBackend) { + case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: + ALOGV("%s: Requesting Vulkan ANGLE back-end", __FUNCTION__); + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.angle.validation", prop, "0"); + attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE); + attrs.push_back(atoi(prop)); + break; + case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: + ALOGV("%s: Requesting Default ANGLE back-end", __FUNCTION__); + break; + case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE: + ALOGV("%s: Requesting OpenGL ES ANGLE back-end", __FUNCTION__); + // NOTE: This is only valid if the backend is OpenGL + attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE); + attrs.push_back(vendorEGL); + break; + default: + ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend); + break; + } + attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE); + attrs.push_back(EGL_FALSE); + + return true; +} + +// Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace +bool initializeAnglePlatform(EGLDisplay dpy) { + // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + void* so = android_dlopen_ext("libEGL_angle.so", RTLD_LOCAL | RTLD_NOW, &dlextinfo); + angle::AnglePlatformImpl::angleGetDisplayPlatform = + reinterpret_cast<angle::GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform")); + + if (!angle::AnglePlatformImpl::angleGetDisplayPlatform) { + ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!"); + return false; + } - return sDisplay[uintptr_t(disp)].getDisplay(disp); + angle::AnglePlatformImpl::angleResetDisplayPlatform = + reinterpret_cast<angle::ResetDisplayPlatformFunc>( + eglGetProcAddress("ANGLEResetDisplayPlatform")); + + angle::PlatformMethods* platformMethods = nullptr; + if (!((angle::AnglePlatformImpl::angleGetDisplayPlatform)(dpy, angle::g_PlatformMethodNames, + angle::g_NumPlatformMethods, nullptr, + &platformMethods))) { + ALOGE("ANGLEGetDisplayPlatform call failed!"); + return false; + } + if (platformMethods) { + angle::AnglePlatformImpl::assignAnglePlatformMethods(platformMethods); + } else { + ALOGE("In initializeAnglePlatform() platformMethods struct ptr is NULL. Not assigning " + "tracing function ptrs!"); + } + return true; } -EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { +static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx, + const EGLAttrib* attrib_list, EGLint* error) { + EGLDisplay dpy = EGL_NO_DISPLAY; + *error = EGL_NONE; + + if (cnx->egl.eglGetPlatformDisplay) { + std::vector<EGLAttrib> attrs; + if (attrib_list) { + for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) { + attrs.push_back(attr[0]); + attrs.push_back(attr[1]); + } + } + + if (!addAnglePlatformAttributes(cnx, attrs)) { + ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display); + *error = EGL_BAD_PARAMETER; + return EGL_NO_DISPLAY; + } + attrs.push_back(EGL_NONE); + + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, + reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), + attrs.data()); + if (dpy == EGL_NO_DISPLAY) { + ALOGE("eglGetPlatformDisplay failed!"); + } else { + if (!initializeAnglePlatform(dpy)) { + ALOGE("initializeAnglePlatform failed!"); + } + } + } else { + ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE", + display); + } + return dpy; +} + +EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { std::lock_guard<std::mutex> _l(lock); ATRACE_CALL(); @@ -131,11 +250,32 @@ EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) { - EGLDisplay dpy = cnx->egl.eglGetDisplay(display); + EGLDisplay dpy = EGL_NO_DISPLAY; + + if (cnx->useAngle) { + EGLint error; + dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error); + if (error != EGL_NONE) { + return setError(error, dpy); + } + } + if (dpy == EGL_NO_DISPLAY) { + // NOTE: eglGetPlatformDisplay with a empty attribute list + // behaves the same as eglGetDisplay + if (cnx->egl.eglGetPlatformDisplay) { + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display, + attrib_list); + } else { + if (attrib_list) { + ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored"); + } + dpy = cnx->egl.eglGetDisplay(display); + } + } + disp.dpy = dpy; if (dpy == EGL_NO_DISPLAY) { - loader.close(cnx->dso); - cnx->dso = NULL; + loader.close(cnx); } } @@ -148,13 +288,20 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { std::unique_lock<std::mutex> _l(refLock); refs++; if (refs > 1) { - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // We don't know what to report until we know what the + // driver supports. Make sure we are initialized before + // returning the version info. while(!eglIsInitialized) { refCond.wait(_l); } + egl_connection_t* const cnx = &gEGLImpl; + + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; return EGL_TRUE; } while(eglIsInitialized) { @@ -201,7 +348,52 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { // the query strings are per-display mVendorString = sVendorString; - mVersionString = sVersionString; + mVersionString.clear(); + cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0); + if ((cnx->major == 1) && (cnx->minor == 5)) { + mVersionString = sVersionString15; + cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0); + } else if ((cnx->major == 1) && (cnx->minor == 4)) { + mVersionString = sVersionString14; + // Extensions needed for an EGL 1.4 implementation to be + // able to support EGL 1.5 functionality + std::vector<const char*> egl15extensions = { + "EGL_EXT_client_extensions", + // "EGL_EXT_platform_base", // implemented by EGL runtime + "EGL_KHR_image_base", + "EGL_KHR_fence_sync", + "EGL_KHR_wait_sync", + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_gl_colorspace", + "EGL_ANDROID_native_fence_sync", + }; + bool extensionsFound = true; + for (const auto& name : egl15extensions) { + extensionsFound &= findExtension(disp.queryString.extensions, name); + ALOGV("Extension %s: %s", name, + findExtension(disp.queryString.extensions, name) ? "Found" : "Missing"); + } + // NOTE: From the spec: + // Creation of fence sync objects requires support from the bound + // client API, and will not succeed unless the client API satisfies: + // client API is OpenGL ES, and either the OpenGL ES version is 3.0 + // or greater, or the GL_OES_EGL_sync extension is supported. + // We don't have a way to check the GL_EXTENSIONS string at this + // point in the code, assume that GL_OES_EGL_sync is supported + // because EGL_KHR_fence_sync is supported (as verified above). + if (extensionsFound) { + // Have everything needed to emulate EGL 1.5 so report EGL 1.5 + // to the application. + mVersionString = sVersionString15; + cnx->major = 1; + cnx->minor = 5; + } + } + if (mVersionString.empty()) { + ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor); + mVersionString = sVersionString14; + } mClientApiString = sClientApiString; mExtensionString = gBuiltinExtensionString; @@ -240,12 +432,6 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (len) { // NOTE: we could avoid the copy if we had strnstr. const std::string ext(start, len); - // Temporary hack: Adreno 530 driver exposes this extension under the draft - // KHR name, but during Khronos review it was decided to demote it to EXT. - if (ext == "EGL_EXT_image_gl_colorspace" && - findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) { - mExtensionString.append("EGL_EXT_image_gl_colorspace "); - } if (findExtension(disp.queryString.extensions, ext.c_str(), len)) { mExtensionString.append(ext + " "); } @@ -268,10 +454,12 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { traceGpuCompletion = true; } - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; } { // scope for refLock @@ -311,6 +499,10 @@ EGLBoolean egl_display_t::terminate() { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { + // If we're using ANGLE reset any custom DisplayPlatform + if (cnx->useAngle && angle::AnglePlatformImpl::angleResetDisplayPlatform) { + (angle::AnglePlatformImpl::angleResetDisplayPlatform)(disp.dpy); + } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, egl_tls_t::egl_strerror(cnx->egl.eglGetError())); @@ -361,8 +553,8 @@ void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); - SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr); { // scope for the lock std::lock_guard<std::mutex> _l(lock); @@ -387,8 +579,8 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); - SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr); { // scope for the lock std::lock_guard<std::mutex> _l(lock); diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 79a9f082a6..36856b79fe 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -49,6 +49,7 @@ bool findExtension(const char* exts, const char* name, size_t nameLen = 0); class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); + EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list); void loseCurrentImpl(egl_context_t * cur_c); public: @@ -72,7 +73,7 @@ public: bool getObject(egl_object_t* object) const; static egl_display_t* get(EGLDisplay dpy); - static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp); + static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list); EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read, EGLContext ctx, @@ -160,7 +161,7 @@ public: const egl_display_t* get() const { return mDpy; } egl_display_t* get() { return mDpy; } - operator bool() const { return mDpy != NULL; } + operator bool() const { return mDpy != nullptr; } private: egl_display_t* mDpy; diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in index b587a16203..2921d512f1 100644 --- a/opengl/libs/EGL/egl_entries.in +++ b/opengl/libs/EGL/egl_entries.in @@ -44,6 +44,18 @@ EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGL /* EGL 1.4 */ +/* EGL 1.5 */ +EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage) +EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, void *, const EGLAttrib *) +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void *, const EGLAttrib *) +EGL_ENTRY(EGLSyncKHR, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync) +EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSync, EGLint, EGLAttrib *) +EGL_ENTRY(EGLBoolean, eglWaitSync, EGLDisplay, EGLSync, EGLint) + /* EGL_EGLEXT_VERSION 3 */ EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *) @@ -75,6 +87,10 @@ EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR) EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint) +/* EGL_EGLEXT_VERSION 20170627 */ +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurfaceEXT, EGLDisplay, EGLConfig, void *, const EGLint *) + /* ANDROID extensions */ EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint) diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp new file mode 100644 index 0000000000..dd8fbfcb7d --- /dev/null +++ b/opengl/libs/EGL/egl_layers.cpp @@ -0,0 +1,435 @@ +/* + ** Copyright 2018, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "egl_layers.h" + +#include <EGL/egl.h> +#include <android-base/file.h> +#include <android-base/strings.h> +#include <android/dlext.h> +#include <cutils/properties.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> +#include <log/log.h> +#include <nativebridge/native_bridge.h> +#include <nativeloader/native_loader.h> +#include <sys/prctl.h> + +namespace android { + +// GLES Layers +// +// - Layer discovery - +// 1. Check for debug layer list from GraphicsEnv +// 2. If none enabled, check system properties +// +// - Layer initializing - +// TODO: ADD DETAIL ABOUT NEW INTERFACES +// - InitializeLayer (provided by layer, called by loader) +// - GetLayerProcAddress (provided by layer, called by loader) +// - getNextLayerProcAddress (provided by loader, called by layer) +// +// 1. Walk through defs for egl and each gl version +// 2. Call GetLayerProcAddress passing the name and the target hook entry point +// - This tells the layer the next point in the chain it should call +// 3. Replace the hook with the layer's entry point +// - All entryoints will be present, anything unsupported by the driver will +// have gl_unimplemented +// +// - Extension layering - +// Not all functions are known to Android, so libEGL handles extensions. +// They are looked up by applications using eglGetProcAddress +// Layers can look them up with getNextLayerProcAddress + +const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) + + sizeof(gl_hooks_t) / sizeof(char*); + +typedef struct FunctionTable { + EGLFuncPointer x[kFuncCount]; + EGLFuncPointer& operator[](int i) { return x[i]; } +} FunctionTable; + +// TODO: Move these to class +std::unordered_map<std::string, int> func_indices; +// func_indices.reserve(kFuncCount); + +std::unordered_map<int, std::string> func_names; +// func_names.reserve(kFuncCount); + +std::vector<FunctionTable> layer_functions; + +const void* getNextLayerProcAddress(void* layer_id, const char* name) { + // Use layer_id to find funcs for layer below current + // This is the same key provided in InitializeLayer + auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id); + EGLFuncPointer val; + + if (func_indices.find(name) == func_indices.end()) { + // No entry for this function - it is an extension + // call down the GPA chain directly to the impl + ALOGV("getNextLayerProcAddress servicing %s", name); + + // Look up which GPA we should use + int gpaIndex = func_indices["eglGetProcAddress"]; + EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex]; + + ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name); + + // Call it for the requested function + typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*); + PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext); + + val = reinterpret_cast<EGLFuncPointer>(next(name)); + ALOGV("Got back %llu for %s", (unsigned long long)val, name); + + // We should store it now, but to do that, we need to move func_idx to the class so we can + // increment it separately + // TODO: Move func_idx to class and store the result of GPA + return reinterpret_cast<void*>(val); + } + + // int index = func_indices[name]; + // val = (*next_layer_funcs)[index]; + // return reinterpret_cast<void*>(val); + return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]); +} + +void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr, + int& func_idx) { + while (*entries) { + const char* name = *entries; + + // Some names overlap, only fill with initial entry + // This does mean that some indices will not be used + if (func_indices.find(name) == func_indices.end()) { + func_names[func_idx] = name; + func_indices[name] = func_idx; + } + + // Populate layer_functions once with initial value + // These values will arrive in priority order, starting with platform entries + if (functions[func_idx] == nullptr) { + functions[func_idx] = *curr; + } + + entries++; + curr++; + func_idx++; + } +} + +LayerLoader& LayerLoader::getInstance() { + // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl + static LayerLoader layer_loader; + + if (!layer_loader.layers_loaded_) layer_loader.LoadLayers(); + + return layer_loader; +} + +const char kSystemLayerLibraryDir[] = "/data/local/debug/gles"; + +std::string LayerLoader::GetDebugLayers() { + // Layers can be specified at the Java level in GraphicsEnvironemnt + // gpu_debug_layers_gles = layer1:layer2:layerN + std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES(); + + if (debug_layers.empty()) { + // Only check system properties if Java settings are empty + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.gles.layers", prop, ""); + debug_layers = prop; + } + + return debug_layers; +} + +EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name, + EGLFuncPointer next) { + // Walk through our list of LayerSetup functions (they will already be in reverse order) to + // build up a call chain from the driver + + EGLFuncPointer layer_entry = next; + + layer_entry = layer_setup(name, layer_entry); + + if (next != layer_entry) { + ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s", + (unsigned long long)next, (unsigned long long)layer_entry, name); + } + + return layer_entry; +} + +EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) { + if (!layers_loaded_ || layer_setup_.empty()) return next; + + ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name, + (unsigned long long)next, current_layer_); + + EGLFuncPointer val = next; + + // Only ApplyLayers for layers that have been setup, not all layers yet + for (unsigned i = 0; i < current_layer_; i++) { + ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name, + (unsigned long long)next); + val = ApplyLayer(layer_setup_[i], name, val); + } + + ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name); + + return val; +} + +void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + + EGLFuncPointer prev = *curr; + + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *curr); + + if (prev != *curr) { + ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } else { + ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not " + "intercept", + (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + EGLFuncPointer prev = *curr; + + // Only apply layers to driver entries if not handled by the platform + if (FindPlatformImplAddr(name) == nullptr) { + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *prev); + + if (prev != *curr) { + ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } + + } else { + ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +bool LayerLoader::Initialized() { + return initialized_; +} + +void LayerLoader::InitLayers(egl_connection_t* cnx) { + if (!layers_loaded_) return; + + if (initialized_) return; + + if (layer_setup_.empty()) { + initialized_ = true; + return; + } + + // Include the driver in layer_functions + layer_functions.resize(layer_setup_.size() + 1); + + // Walk through the initial lists and create layer_functions[0] + int func_idx = 0; + char const* const* entries; + EGLFuncPointer* curr; + + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after platform_names: %i", func_idx); + + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after egl_names: %i", func_idx); + + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after gl_names: %i", func_idx); + + // Walk through each layer's entry points per API, starting just above the driver + for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) { + // Init the layer with a key that points to layer just below it + layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]), + reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>( + getNextLayerProcAddress)); + + // Check functions implemented by the platform + func_idx = 0; + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + LayerPlatformEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // EGL + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // GLES 2+ + // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x + // If it were added in the future, a different layer initialization model would be needed, + // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase + // initialization. + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + } + + // We only want to apply layers once + initialized_ = true; +} + +void LayerLoader::LoadLayers() { + std::string debug_layers = GetDebugLayers(); + + // If no layers are specified, we're done + if (debug_layers.empty()) return; + + // Only enable the system search path for non-user builds + std::string system_path; + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + system_path = kSystemLayerLibraryDir; + } + + ALOGI("Debug layer list: %s", debug_layers.c_str()); + std::vector<std::string> layers = android::base::Split(debug_layers, ":"); + + // Load the layers in reverse order so we start with the driver's entrypoint and work our way up + for (int32_t i = layers.size() - 1; i >= 0; i--) { + // Check each layer path for the layer + std::vector<std::string> paths = + android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(), + ":"); + + if (!system_path.empty()) { + // Prepend the system paths so they override other layers + auto it = paths.begin(); + paths.insert(it, system_path); + } + + bool layer_found = false; + for (uint32_t j = 0; j < paths.size() && !layer_found; j++) { + std::string layer; + + ALOGI("Searching %s for GLES layers", paths[j].c_str()); + + // Realpath will return null for non-existent files + android::base::Realpath(paths[j] + "/" + layers[i], &layer); + + if (!layer.empty()) { + layer_found = true; + ALOGI("GLES layer found: %s", layer.c_str()); + + // Load the layer + // + // TODO: This code is common with Vulkan loader, refactor + // + // Libraries in the system layer library dir can't be loaded into + // the application namespace. That causes compatibility problems, since + // any symbol dependencies will be resolved by system libraries. They + // can't safely use libc++_shared, for example. Which is one reason + // (among several) we only allow them in non-user builds. + void* handle = nullptr; + auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); + if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) { + bool native_bridge = false; + std::string error_message; + handle = OpenNativeLibrary(app_namespace, layer.c_str(), &native_bridge, + &error_message); + if (!handle) { + ALOGE("Failed to load layer %s with error: %s", layer.c_str(), + error_message.c_str()); + return; + } + + } else { + handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL); + } + + if (handle) { + ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle, + layers[i].c_str()); + } else { + // If the layer is found but can't be loaded, try setenforce 0 + const char* dlsym_error = dlerror(); + ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error); + return; + } + + // Find the layer's Initialize function + std::string init_func = "InitializeLayer"; + ALOGV("Looking for entrypoint %s", init_func.c_str()); + + layer_init_func LayerInit = + reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str())); + if (LayerInit) { + ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str()); + layer_init_.push_back(LayerInit); + } else { + ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str()); + return; + } + + // Find the layer's setup function + std::string setup_func = "GetLayerProcAddress"; + ALOGV("Looking for entrypoint %s", setup_func.c_str()); + + layer_setup_func LayerSetup = + reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str())); + if (LayerSetup) { + ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str()); + layer_setup_.push_back(LayerSetup); + } else { + ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str()); + return; + } + } + } + } + // Track this so we only attempt to load these once + layers_loaded_ = true; +} + +} // namespace android diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h new file mode 100644 index 0000000000..e401b448cf --- /dev/null +++ b/opengl/libs/EGL/egl_layers.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_EGL_LAYERS_H +#define ANDROID_EGL_LAYERS_H + +#include <string> +#include <unordered_map> +#include <vector> + +#include <EGL/egldefs.h> + +#include "egl_platform_entries.h" + +typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; + +namespace android { + +class LayerLoader { +public: + static LayerLoader& getInstance(); + ~LayerLoader(){}; + + typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*); + typedef EGLFuncPointer (*layer_init_func)( + const void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address); + typedef EGLFuncPointer (*layer_setup_func)(const char* name, EGLFuncPointer next); + + void LoadLayers(); + void InitLayers(egl_connection_t*); + void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); + void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); + bool Initialized(); + std::string GetDebugLayers(); + + EGLFuncPointer GetGpaNext(unsigned i); + EGLFuncPointer ApplyLayer(layer_setup_func layer_setup, const char* name, EGLFuncPointer next); + EGLFuncPointer ApplyLayers(const char* name, EGLFuncPointer next); + + std::vector<layer_init_func> layer_init_; + std::vector<layer_setup_func> layer_setup_; + +private: + LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){}; + bool layers_loaded_; + bool initialized_; + unsigned current_layer_; +}; + +}; // namespace android + +#endif // ANDROID_EGL_LAYERS_H diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index f879254f76..ff4fe2dd9c 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -81,14 +81,14 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWind } egl_surface_t::~egl_surface_t() { - if (win != NULL) { + if (win != nullptr) { disconnect(); win->decStrong(this); } } void egl_surface_t::disconnect() { - if (win != NULL && connected) { + if (win != nullptr && connected) { native_window_set_buffers_format(win, 0); if (native_window_api_disconnect(win, NATIVE_WINDOW_API_EGL)) { ALOGW("EGLNativeWindowType %p disconnect failed", win); @@ -281,12 +281,12 @@ void egl_surface_t::terminate() { egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx, int version) : egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context), - config(config), read(0), draw(0), cnx(cnx), version(version) { + config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) { } void egl_context_t::onLooseCurrent() { - read = NULL; - draw = NULL; + read = nullptr; + draw = nullptr; } void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 4e1de5cec4..fb2bdf4c51 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -67,7 +67,7 @@ public: public: ~LocalRef(); explicit LocalRef(egl_object_t* rhs); - explicit LocalRef(egl_display_t const* display, T o) : ref(0) { + explicit LocalRef(egl_display_t const* display, T o) : ref(nullptr) { egl_object_t* native = reinterpret_cast<N*>(o); if (o && egl_object_t::get(display, native)) { ref = native; diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp new file mode 100644 index 0000000000..547a6690b0 --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -0,0 +1,2693 @@ +/* + ** Copyright 2007, 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 "egl_platform_entries.h" + +#include <ctype.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> + +#include <hardware/gralloc1.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/eglext_angle.h> + +#include <android/hardware_buffer.h> +#include <android-base/strings.h> +#include <graphicsenv/GraphicsEnv.h> +#include <private/android/AHardwareBufferHelpers.h> + +#include <cutils/compiler.h> +#include <cutils/properties.h> +#include <log/log.h> + +#include <condition_variable> +#include <deque> +#include <mutex> +#include <unordered_map> +#include <string> +#include <thread> + +#include "../egl_impl.h" + +#include "egl_display.h" +#include "egl_object.h" +#include "egl_layers.h" +#include "egl_tls.h" +#include "egl_trace.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +namespace android { + +using nsecs_t = int64_t; + +struct extention_map_t { + const char* name; + __eglMustCastToProperFunctionPointerType address; +}; + +/* + * This is the list of EGL extensions exposed to applications. + * + * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL + * wrapper and are always available. + * + * The rest (gExtensionString) depend on support in the EGL driver, and are + * only available if the driver supports them. However, some of these must be + * supported because they are used by the Android system itself; these are + * listed as mandatory below and are required by the CDD. The system *assumes* + * the mandatory extensions are present and may not function properly if some + * are missing. + * + * NOTE: Both strings MUST have a single space as the last character. + */ + +extern char const * const gBuiltinExtensionString; +extern char const * const gExtensionString; + +// clang-format off +// Extensions implemented by the EGL wrapper. +char const * const gBuiltinExtensionString = + "EGL_KHR_get_all_proc_addresses " + "EGL_ANDROID_presentation_time " + "EGL_KHR_swap_buffers_with_damage " + "EGL_ANDROID_get_native_client_buffer " + "EGL_ANDROID_front_buffer_auto_refresh " + "EGL_ANDROID_get_frame_timestamps " + "EGL_EXT_surface_SMPTE2086_metadata " + "EGL_EXT_surface_CTA861_3_metadata " + ; + +// Whitelist of extensions exposed to applications if implemented in the vendor driver. +char const * const gExtensionString = + "EGL_KHR_image " // mandatory + "EGL_KHR_image_base " // mandatory + "EGL_EXT_image_gl_colorspace " + "EGL_KHR_image_pixmap " + "EGL_KHR_lock_surface " + "EGL_KHR_gl_colorspace " + "EGL_KHR_gl_texture_2D_image " + "EGL_KHR_gl_texture_3D_image " + "EGL_KHR_gl_texture_cubemap_image " + "EGL_KHR_gl_renderbuffer_image " + "EGL_KHR_reusable_sync " + "EGL_KHR_fence_sync " + "EGL_KHR_create_context " + "EGL_KHR_config_attribs " + "EGL_KHR_surfaceless_context " + "EGL_KHR_stream " + "EGL_KHR_stream_fifo " + "EGL_KHR_stream_producer_eglsurface " + "EGL_KHR_stream_consumer_gltexture " + "EGL_KHR_stream_cross_process_fd " + "EGL_EXT_create_context_robustness " + "EGL_NV_system_time " + "EGL_ANDROID_image_native_buffer " // mandatory + "EGL_KHR_wait_sync " // strongly recommended + "EGL_ANDROID_recordable " // mandatory + "EGL_KHR_partial_update " // strongly recommended + "EGL_EXT_pixel_format_float " + "EGL_EXT_buffer_age " // strongly recommended with partial_update + "EGL_KHR_create_context_no_error " + "EGL_KHR_mutable_render_buffer " + "EGL_EXT_yuv_surface " + "EGL_EXT_protected_content " + "EGL_IMG_context_priority " + "EGL_KHR_no_config_context " + ; + +char const * const gClientExtensionString = + "EGL_EXT_client_extensions " + "EGL_KHR_platform_android " + "EGL_ANGLE_platform_angle"; +// clang-format on + +// extensions not exposed to applications but used by the ANDROID system +// "EGL_ANDROID_blob_cache " // strongly recommended +// "EGL_IMG_hibernate_process " // optional +// "EGL_ANDROID_native_fence_sync " // strongly recommended +// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 + +/* + * EGL Extensions entry-points exposed to 3rd party applications + * (keep in sync with gExtensionString above) + * + */ +static const extention_map_t sExtensionMap[] = { + // EGL_KHR_lock_surface + { "eglLockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, + { "eglUnlockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, + + // EGL_KHR_image, EGL_KHR_image_base + { "eglCreateImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, + { "eglDestroyImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, + + // EGL_KHR_reusable_sync, EGL_KHR_fence_sync + { "eglCreateSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, + { "eglDestroySyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, + { "eglClientWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, + { "eglSignalSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, + { "eglGetSyncAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, + + // EGL_NV_system_time + { "eglGetSystemTimeFrequencyNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, + { "eglGetSystemTimeNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, + + // EGL_KHR_wait_sync + { "eglWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, + + // EGL_ANDROID_presentation_time + { "eglPresentationTimeANDROID", + (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, + + // EGL_KHR_swap_buffers_with_damage + { "eglSwapBuffersWithDamageKHR", + (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, + + // EGL_ANDROID_get_native_client_buffer + { "eglGetNativeClientBufferANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, + + // EGL_KHR_partial_update + { "eglSetDamageRegionKHR", + (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, + + { "eglCreateStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, + { "eglDestroyStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, + { "eglStreamAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, + { "eglQueryStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, + { "eglQueryStreamu64KHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, + { "eglQueryStreamTimeKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, + { "eglCreateStreamProducerSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, + { "eglStreamConsumerGLTextureExternalKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, + { "eglStreamConsumerAcquireKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, + { "eglStreamConsumerReleaseKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, + { "eglGetStreamFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, + { "eglCreateStreamFromFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + + // EGL_ANDROID_get_frame_timestamps + { "eglGetNextFrameIdANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, + { "eglGetFrameTimestampsANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglGetFrameTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, + + // EGL_ANDROID_native_fence_sync + { "eglDupNativeFenceFDANDROID", + (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, +}; + +/* + * These extensions entry-points should not be exposed to applications. + * They're used internally by the Android EGL layer. + */ +#define FILTER_EXTENSIONS(procname) \ + (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ + !strcmp((procname), "eglHibernateProcessIMG") || \ + !strcmp((procname), "eglAwakenProcessIMG")) + +// accesses protected by sExtensionMapMutex +static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; + +static int sGLExtentionSlot = 0; +static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; + +static void(*findProcAddress(const char* name, + const extention_map_t* map, size_t n))() { + for (uint32_t i=0 ; i<n ; i++) { + if (!strcmp(name, map[i].name)) { + return map[i].address; + } + } + return nullptr; +} + +// ---------------------------------------------------------------------------- + +extern void setGLHooksThreadSpecific(gl_hooks_t const *value); +extern EGLBoolean egl_init_drivers(); +extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; +extern gl_hooks_t gHooksTrace; + +// ---------------------------------------------------------------------------- + +static inline EGLContext getContext() { return egl_tls_t::getContext(); } + +// ---------------------------------------------------------------------------- + +static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { + if (platform != EGL_PLATFORM_ANDROID_KHR) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + uintptr_t index = reinterpret_cast<uintptr_t>(display); + if (index >= NUM_DISPLAYS) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list); + return dpy; +} + +EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) { + return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr); +} + +EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display, + const EGLAttrib* attrib_list) { + return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display), + attrib_list); +} + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->initialize(major, minor); + + return res; +} + +EGLBoolean eglTerminateImpl(EGLDisplay dpy) +{ + // NOTE: don't unload the drivers b/c some APIs can be called + // after eglTerminate() has been called. eglTerminate() only + // terminates an EGLDisplay, not a EGL itself. + + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->terminate(); + + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + res = cnx->egl.eglGetConfigs( + dp->disp.dpy, configs, config_size, num_config); + } + + return res; +} + +EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + if (attrib_list) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.force_msaa", value, "false"); + + if (!strcmp(value, "true")) { + size_t attribCount = 0; + EGLint attrib = attrib_list[0]; + + // Only enable MSAA if the context is OpenGL ES 2.0 and + // if no caveat is requested + const EGLint *attribRendererable = nullptr; + const EGLint *attribCaveat = nullptr; + + // Count the number of attributes and look for + // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT + while (attrib != EGL_NONE) { + attrib = attrib_list[attribCount]; + switch (attrib) { + case EGL_RENDERABLE_TYPE: + attribRendererable = &attrib_list[attribCount]; + break; + case EGL_CONFIG_CAVEAT: + attribCaveat = &attrib_list[attribCount]; + break; + default: + break; + } + attribCount++; + } + + if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && + (!attribCaveat || attribCaveat[1] != EGL_NONE)) { + + // Insert 2 extra attributes to force-enable MSAA 4x + EGLint aaAttribs[attribCount + 4]; + aaAttribs[0] = EGL_SAMPLE_BUFFERS; + aaAttribs[1] = 1; + aaAttribs[2] = EGL_SAMPLES; + aaAttribs[3] = 4; + + memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); + + EGLint numConfigAA; + EGLBoolean resAA = cnx->egl.eglChooseConfig( + dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); + + if (resAA == EGL_TRUE && numConfigAA > 0) { + ALOGD("Enabling MSAA 4x"); + *num_config = numConfigAA; + return resAA; + } + } + } + } + + res = cnx->egl.eglChooseConfig( + dp->disp.dpy, attrib_list, configs, config_size, num_config); + } + return res; +} + +EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + + return cnx->egl.eglGetConfigAttrib( + dp->disp.dpy, config, attribute, value); +} + +// ---------------------------------------------------------------------------- +// surfaces +// ---------------------------------------------------------------------------- + +// Translates EGL color spaces to Android data spaces. +static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { + return HAL_DATASPACE_UNKNOWN; + } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { + return HAL_DATASPACE_SRGB; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + return HAL_DATASPACE_DISPLAY_P3; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { + return HAL_DATASPACE_DISPLAY_P3_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { + return HAL_DATASPACE_V0_SCRGB; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { + return HAL_DATASPACE_BT2020_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { + return HAL_DATASPACE_BT2020_PQ; + } + return HAL_DATASPACE_UNKNOWN; +} + +// Get the colorspace value that should be reported from queries. When the colorspace +// is unknown (no attribute passed), default to reporting LINEAR. +static EGLint getReportedColorSpace(EGLint colorspace) { + return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace; +} + +// Returns a list of color spaces understood by the vendor EGL driver. +static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { + std::vector<EGLint> colorSpaces; + + // sRGB and linear are always supported when color space support is present. + colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR); + colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR); + + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_pq")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); + } + return colorSpaces; +} + +// Cleans up color space related parameters that the driver does not understand. +// If there is no color space attribute in attrib_list, colorSpace is left +// unmodified. +template <typename AttrType> +static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window, + const AttrType* attrib_list, EGLint* colorSpace, + std::vector<AttrType>* strippedAttribList) { + for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { + bool copyAttribute = true; + if (attr[0] == EGL_GL_COLORSPACE_KHR) { + switch (attr[1]) { + case EGL_GL_COLORSPACE_LINEAR_KHR: + case EGL_GL_COLORSPACE_SRGB_KHR: + case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: + case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: + case EGL_GL_COLORSPACE_SCRGB_EXT: + case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: + case EGL_GL_COLORSPACE_BT2020_PQ_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT: + // Fail immediately if the driver doesn't have color space support at all. + if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + break; + default: + // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_* + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + *colorSpace = static_cast<EGLint>(attr[1]); + + // Strip the attribute if the driver doesn't understand it. + copyAttribute = false; + std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp); + for (auto driverColorSpace : driverColorSpaces) { + if (static_cast<EGLint>(attr[1]) == driverColorSpace) { + copyAttribute = true; + break; + } + } + + // If the driver doesn't understand it, we should map sRGB-encoded P3 to + // sRGB rather than just dropping the colorspace on the floor. + // For this format, the driver is expected to apply the sRGB + // transfer function during framebuffer operations. + if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR); + } + } + if (copyAttribute) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(attr[1]); + } + } + // Terminate the attribute list. + strippedAttribList->push_back(EGL_NONE); + + // If the passed color space has wide color gamut, check whether the target native window + // supports wide color. + const bool colorSpaceIsNarrow = *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR || + *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || *colorSpace == EGL_UNKNOWN; + if (window && !colorSpaceIsNarrow) { + bool windowSupportsWideColor = true; + // Ordinarily we'd put a call to native_window_get_wide_color_support + // at the beginning of the function so that we'll have the + // result when needed elsewhere in the function. + // However, because eglCreateWindowSurface is called by SurfaceFlinger and + // SurfaceFlinger is required to answer the call below we would + // end up in a deadlock situation. By moving the call to only happen + // if the application has specifically asked for wide-color we avoid + // the deadlock with SurfaceFlinger since it will not ask for a + // wide-color surface. + int err = native_window_get_wide_color_support(window, &windowSupportsWideColor); + + if (err) { + ALOGE("processAttributes: invalid window (win=%p) " + "failed (%#x) (already connected to another API?)", + window, err); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + } + if (!windowSupportsWideColor) { + // Application has asked for a wide-color colorspace but + // wide-color support isn't available on the display the window is on. + return setError(EGL_BAD_MATCH, EGL_FALSE); + } + } + return true; +} + +// Note: This only works for existing GLenum's that are all 32bits. +// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this. +void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) { + for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) { + newList.push_back(static_cast<EGLint>(attr[0])); + newList.push_back(static_cast<EGLint>(attr[1])); + } + newList.push_back(EGL_NONE); +} + +// Gets the native pixel format corrsponding to the passed EGLConfig. +void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, + android_pixel_format* 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 + // modify the EGLconfig's format before setting the native window's + // format. + + EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType); + + EGLint a = 0; + EGLint r, g, b; + r = g = b = 0; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); + EGLint colorDepth = r + g + b; + + // Today, the driver only understands sRGB and linear on 888X + // formats. Strip other colorspaces from the attribute list and + // only use them to set the dataspace via + // native_window_set_buffers_dataspace + // if pixel format is RGBX 8888 + // TBD: Can test for future extensions that indicate that driver + // handles requested color space and we can let it through. + // allow SRGB and LINEAR. All others need to be stripped. + // else if 565, 4444 + // TBD: Can we assume these are supported if 8888 is? + // else if FP16 or 1010102 + // strip colorspace from attribs. + // endif + if (a == 0) { + if (colorDepth <= 16) { + *format = HAL_PIXEL_FORMAT_RGB_565; + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBX_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBA_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } +} + +EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { + android_smpte2086_metadata smpteMetadata; + if (s->getSmpte2086Metadata(smpteMetadata)) { + int err = + native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata); + s->resetSmpte2086Metadata(); + if (err != 0) { + ALOGE("error setting native window smpte2086 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + android_cta861_3_metadata cta8613Metadata; + if (s->getCta8613Metadata(cta8613Metadata)) { + int err = + native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata); + s->resetCta8613Metadata(); + if (err != 0) { + ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + return EGL_TRUE; +} + +template <typename AttrType, typename CreateFuncType> +EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config, + ANativeWindow* window, const AttrType* attrib_list, + CreateFuncType createWindowSurfaceFunc) { + const AttrType* origAttribList = attrib_list; + + if (!window) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + int value = 0; + window->query(window, NATIVE_WINDOW_IS_VALID, &value); + if (!value) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + // NOTE: When using Vulkan backend, the Vulkan runtime makes all the + // native_window_* calls, so don't do them here. + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (result < 0) { + ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " + "failed (%#x) (already connected to another API?)", + window, result); + return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + } + } + + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // now select correct colorspace and dataspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<AttrType> strippedAttribList; + if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int err = native_window_set_buffers_format(window, 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); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); + // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. + // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed + // at this point. + err = native_window_set_buffers_data_space(window, dataSpace); + if (err != 0) { + ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + } + + // the EGL spec requires that a new EGLSurface default to swap interval + // 1, so explicitly set that on the window here. + window->setSwapInterval(window, 1); + + EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + + // EGLSurface creation failed + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_buffers_format(window, 0); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, + NativeWindowType window, + const EGLint* attrib_list); +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)( + EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list); + +EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list, + cnx->egl.eglCreateWindowSurface); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreatePlatformWindowSurface) { + return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list, + cnx->egl.eglCreatePlatformWindowSurface); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have " + "eglCreatePlatformWindowSurface"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) { + return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), + convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT); + } else { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, + static_cast<ANativeWindow*>( + native_window), + convertedAttribs.data(), + cnx->egl.eglCreateWindowSurface); + } + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + void* /*native_pixmap*/, + const EGLAttrib* /*attrib_list*/) { + // Per EGL_KHR_platform_android: + // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that + // belongs to the Android platform. Any such call fails and generates + // an EGL_BAD_PARAMETER error. + + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config, + const EGLint* attrib_list) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // Select correct colorspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<EGLint> strippedAttribList; + if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(surface); + EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); + if (result == EGL_TRUE) { + _s.terminate(); + } + return result; +} + +EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint* value) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const* const s = get_surface(surface); + if (s->getColorSpaceAttribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } + return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value); +} + +void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } +} + +// ---------------------------------------------------------------------------- +// Contexts +// ---------------------------------------------------------------------------- + +EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (share_list != EGL_NO_CONTEXT) { + if (!ContextRef(dp.get(), share_list).get()) { + return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); + } + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } + EGLContext context = cnx->egl.eglCreateContext( + dp->disp.dpy, config, share_list, attrib_list); + if (context != EGL_NO_CONTEXT) { + // figure out if it's a GLESv1 or GLESv2 + int version = 0; + if (attrib_list) { + while (*attrib_list != EGL_NONE) { + GLint attr = *attrib_list++; + GLint value = *attrib_list++; + if (attr == EGL_CONTEXT_CLIENT_VERSION) { + if (value == 1) { + version = egl_connection_t::GLESv1_INDEX; + } else if (value == 2 || value == 3) { + version = egl_connection_t::GLESv2_INDEX; + } + } + }; + } + egl_context_t* c = new egl_context_t(dpy, context, config, cnx, + version); + return c; + } + } + return EGL_NO_CONTEXT; +} + +EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) + return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); + if (result == EGL_TRUE) { + _c.terminate(); + } + return result; +} + +EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not + // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is + // a valid but uninitialized display. + if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || + (draw != EGL_NO_SURFACE) ) { + if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + // get a reference to the object passed in + ContextRef _c(dp.get(), ctx); + SurfaceRef _d(dp.get(), draw); + SurfaceRef _r(dp.get(), read); + + // validate the context (if not EGL_NO_CONTEXT) + if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { + // EGL_NO_CONTEXT is valid + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + } + + // these are the underlying implementation's object + EGLContext impl_ctx = EGL_NO_CONTEXT; + EGLSurface impl_draw = EGL_NO_SURFACE; + EGLSurface impl_read = EGL_NO_SURFACE; + + // these are our objects structs passed in + egl_context_t * c = nullptr; + egl_surface_t const * d = nullptr; + egl_surface_t const * r = nullptr; + + // these are the current objects structs + egl_context_t * cur_c = get_context(getContext()); + + if (ctx != EGL_NO_CONTEXT) { + c = get_context(ctx); + impl_ctx = c->context; + } else { + // no context given, use the implementation of the current context + if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { + // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); + return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE); + } + if (cur_c == nullptr) { + // no current context + // not an error, there is just no current context. + return EGL_TRUE; + } + } + + // retrieve the underlying implementation's draw EGLSurface + if (draw != EGL_NO_SURFACE) { + if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + d = get_surface(draw); + impl_draw = d->surface; + } + + // retrieve the underlying implementation's read EGLSurface + if (read != EGL_NO_SURFACE) { + if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + r = get_surface(read); + impl_read = r->surface; + } + + + EGLBoolean result = dp->makeCurrent(c, cur_c, + draw, read, ctx, + impl_draw, impl_read, impl_ctx); + + if (result == EGL_TRUE) { + if (c) { + setGLHooksThreadSpecific(c->cnx->hooks[c->version]); + egl_tls_t::setContext(ctx); + _c.acquire(); + _r.acquire(); + _d.acquire(); + } else { + setGLHooksThreadSpecific(&gHooksNoContext); + egl_tls_t::setContext(EGL_NO_CONTEXT); + } + } else { + // this will ALOGE the error + egl_connection_t* const cnx = &gEGLImpl; + result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE); + } + return result; +} + +EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + return c->cnx->egl.eglQueryContext( + dp->disp.dpy, c->context, attribute, value); + +} + +EGLContext eglGetCurrentContextImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_CONTEXT. + EGLContext ctx = getContext(); + return ctx; +} + +EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_SURFACE. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + switch (readdraw) { + case EGL_READ: return c->read; + case EGL_DRAW: return c->draw; + default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + } + return EGL_NO_SURFACE; +} + +EGLDisplay eglGetCurrentDisplayImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_DISPLAY. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + return c->dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglWaitGLImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitGL(); +} + +EGLBoolean eglWaitNativeImpl(EGLint engine) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitNative(engine); +} + +EGLint eglGetErrorImpl(void) +{ + EGLint err = EGL_SUCCESS; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + err = cnx->egl.eglGetError(); + } + if (err == EGL_SUCCESS) { + err = egl_tls_t::getError(); + } + return err; +} + +static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( + const char* procname) { + const egl_connection_t* cnx = &gEGLImpl; + void* proc = nullptr; + + proc = dlsym(cnx->libEgl, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles2, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles1, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + return nullptr; +} + +__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname) +{ + if (FILTER_EXTENSIONS(procname)) { + return nullptr; + } + + __eglMustCastToProperFunctionPointerType addr; + addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap)); + if (addr) return addr; + + addr = findBuiltinWrapper(procname); + if (addr) return addr; + + // this protects accesses to sGLExtentionMap and sGLExtentionSlot + pthread_mutex_lock(&sExtensionMapMutex); + + /* + * Since eglGetProcAddress() is not associated to anything, it needs + * to return a function pointer that "works" regardless of what + * the current context is. + * + * For this reason, we return a "forwarder", a small stub that takes + * care of calling the function associated with the context + * currently bound. + * + * We first look for extensions we've already resolved, if we're seeing + * this extension for the first time, we go through all our + * implementations and call eglGetProcAddress() and record the + * result in the appropriate implementation hooks and return the + * address of the forwarder corresponding to that hook set. + * + */ + + const std::string name(procname); + + auto& extentionMap = sGLExtentionMap; + auto pos = extentionMap.find(name); + addr = (pos != extentionMap.end()) ? pos->second : nullptr; + const int slot = sGLExtentionSlot; + + ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, + "no more slots for eglGetProcAddress(\"%s\")", + procname); + + egl_connection_t* const cnx = &gEGLImpl; + LayerLoader& layer_loader(LayerLoader::getInstance()); + + if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + + if (cnx->dso && cnx->egl.eglGetProcAddress) { + + // Extensions are independent of the bound context + addr = cnx->egl.eglGetProcAddress(procname); + if (addr) { + + // purposefully track the bottom of the stack in extensionMap + extentionMap[name] = addr; + + // Apply layers + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + sGLExtentionSlot++; + } + } + + } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { + + // We've seen this func before, but we tracked the bottom, so re-apply layers + // More layers might have been enabled + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + } + + pthread_mutex_unlock(&sExtensionMapMutex); + return addr; +} + +class FrameCompletionThread { +public: + + static void queueSync(EGLSyncKHR sync) { + static FrameCompletionThread thread; + + char name[64]; + + std::lock_guard<std::mutex> lock(thread.mMutex); + snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued); + ATRACE_NAME(name); + + thread.mQueue.push_back(sync); + thread.mCondition.notify_one(); + thread.mFramesQueued++; + ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size())); + } + +private: + + FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { + std::thread thread(&FrameCompletionThread::loop, this); + thread.detach(); + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + void loop() { + while (true) { + threadLoop(); + } + } +#pragma clang diagnostic pop + + void threadLoop() { + EGLSyncKHR sync; + uint32_t frameNum; + { + std::unique_lock<std::mutex> lock(mMutex); + while (mQueue.empty()) { + mCondition.wait(lock); + } + sync = mQueue[0]; + frameNum = mFramesCompleted; + } + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + { + char name[64]; + snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum); + ATRACE_NAME(name); + + EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); + if (result == EGL_FALSE) { + ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("FrameCompletion: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, sync); + } + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.pop_front(); + mFramesCompleted++; + ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size())); + } + } + + uint32_t mFramesQueued; + uint32_t mFramesCompleted; + std::deque<EGLSyncKHR> mQueue; + std::condition_variable mCondition; + std::mutex mMutex; +}; + +EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), draw); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(draw); + + if (CC_UNLIKELY(dp->traceGpuCompletion)) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (sync != EGL_NO_SYNC_KHR) { + FrameCompletionThread::queueSync(sync); + } + } + + if (CC_UNLIKELY(dp->finishOnSwap)) { + uint32_t pixel; + egl_context_t * const c = get_context( egl_tls_t::getContext() ); + if (c) { + // glReadPixels() ensures that the frame is complete + s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, + GL_RGBA,GL_UNSIGNED_BYTE,&pixel); + } + } + + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + if (!sendSurfaceMetadata(s)) { + native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE); + } + } + + if (n_rects == 0) { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } + + std::vector<android_native_rect_t> androidRects((size_t)n_rects); + for (int r = 0; r < n_rects; ++r) { + int offset = r * 4; + int x = rects[offset]; + int y = rects[offset + 1]; + int width = rects[offset + 2]; + int height = rects[offset + 3]; + android_native_rect_t androidRect; + androidRect.left = x; + androidRect.top = y + height; + androidRect.right = x + width; + androidRect.bottom = y; + androidRects.push_back(androidRect); + } + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), + androidRects.size()); + } + + if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { + return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } else { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } +} + +EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) +{ + return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0); +} + +EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); +} + +const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) +{ + if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { + // Return list of client extensions + return gClientExtensionString; + } + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->getVendorString(); + case EGL_VERSION: + return dp->getVersionString(); + case EGL_EXTENSIONS: + return dp->getExtensionString(); + case EGL_CLIENT_APIS: + return dp->getClientApiString(); + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->disp.queryString.vendor; + case EGL_VERSION: + return dp->disp.queryString.version; + case EGL_EXTENSIONS: + return dp->disp.queryString.extensions; + case EGL_CLIENT_APIS: + return dp->disp.queryString.clientApi; + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttribImpl( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t * const s = get_surface(surface); + + if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { + if (!s->getNativeWindow()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } + int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (attribute == EGL_TIMESTAMPS_ANDROID) { + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (s->setSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->setCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->cnx->egl.eglSurfaceAttrib) { + return s->cnx->egl.eglSurfaceAttrib( + dp->disp.dpy, s->surface, attribute, value); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglBindTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglBindTexImage) { + return s->cnx->egl.eglBindTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglReleaseTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglReleaseTexImage) { + return s->cnx->egl.eglReleaseTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglSwapInterval) { + res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); + } + + return res; +} + + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglWaitClientImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + EGLBoolean res; + if (cnx->egl.eglWaitClient) { + res = cnx->egl.eglWaitClient(); + } else { + res = cnx->egl.eglWaitGL(); + } + return res; +} + +EGLBoolean eglBindAPIImpl(EGLenum api) +{ + // bind this API on all EGLs + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglBindAPI) { + res = cnx->egl.eglBindAPI(api); + } + return res; +} + +EGLenum eglQueryAPIImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryAPI) { + return cnx->egl.eglQueryAPI(); + } + + // or, it can only be OpenGL ES + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglReleaseThreadImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglReleaseThread) { + cnx->egl.eglReleaseThread(); + } + + // If there is context bound to the thread, release it + egl_display_t::loseCurrent(get_context(getContext())); + + egl_tls_t::clearTLS(); + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBufferImpl( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + if (cnx->egl.eglCreatePbufferFromClientBuffer) { + return cnx->egl.eglCreatePbufferFromClientBuffer( + dp->disp.dpy, buftype, buffer, config, attrib_list); + } + return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 3 +// ---------------------------------------------------------------------------- + +EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, + const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglLockSurfaceKHR) { + return s->cnx->egl.eglLockSurfaceKHR( + dp->disp.dpy, s->surface, attrib_list); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglUnlockSurfaceKHR) { + return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +// Note: EGLImageKHR and EGLImage are the same thing so no need +// to templatize that. +template <typename AttrType, typename FuncType> +EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const AttrType* attrib_list, + FuncType eglCreateImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_IMAGE_KHR; + + ContextRef _c(dp.get(), ctx); + egl_context_t* const c = _c.get(); + + EGLImageKHR result = EGL_NO_IMAGE_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglCreateImageFunc) { + result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer, + attrib_list); + } + return result; +} + +typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, + const EGLAttrib* attrib_list); + +EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + attrib_list, + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateImage) { + return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer, + attrib_list, + cnx->egl.eglCreateImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage"); + return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + convertedAttribs.data(), + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img, + PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && destroyImageFunc) { + result = destroyImageFunc(dp->disp.dpy, img); + } + return result; +} + +EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroyImage) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 5 +// ---------------------------------------------------------------------------- + +// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize +template <typename AttrType, typename FuncType> +EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list, + FuncType eglCreateSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SYNC_KHR; + + egl_connection_t* const cnx = &gEGLImpl; + EGLSyncKHR result = EGL_NO_SYNC_KHR; + if (cnx->dso && eglCreateSyncFunc) { + result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list); + } + return result; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type, + const EGLAttrib* attrib_list); + +EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list, + gEGLImpl.egl.eglCreateSyncKHR); +} + +EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateSync) { + return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list, + cnx->egl.eglCreateSync); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(), + cnx->egl.eglCreateSyncKHR); +} + +EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglDestroySyncFunc) { + result = eglDestroySyncFunc(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) { + return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR); +} + +EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroySync) { + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR); +} + +EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) { + result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode); + } + return result; +} + +EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout, + PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLint result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglClientWaitSyncFunc) { + result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout); + } + return result; +} + +EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglClientWaitSync) { + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +template <typename AttrType, typename FuncType> +EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value, + FuncType eglGetSyncAttribFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglGetSyncAttribFunc) { + result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLAttrib* value); + +EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglGetSyncAttrib) { + return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value, + cnx->egl.eglGetSyncAttrib); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib + EGLint attribValue; + EGLBoolean ret = + eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, + &attribValue, + gEGLImpl.egl + .eglGetSyncAttribKHR); + if (ret) { + *value = static_cast<EGLAttrib>(attribValue); + } + return ret; +} + +EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, + EGLint* value) { + return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value, + gEGLImpl.egl + .eglGetSyncAttribKHR); +} + +EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamKHR) { + result = cnx->egl.eglCreateStreamKHR( + dp->disp.dpy, attrib_list); + } + return result; +} + +EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { + result = cnx->egl.eglDestroyStreamKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamAttribKHR) { + result = cnx->egl.eglStreamAttribKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamKHR) { + result = cnx->egl.eglQueryStreamKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLuint64KHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { + result = cnx->egl.eglQueryStreamu64KHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLTimeKHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { + result = cnx->egl.eglQueryStreamTimeKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config, + EGLStreamKHR stream, const EGLint *attrib_list) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SURFACE; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { + EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( + dp->disp.dpy, config, stream, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + EGL_GL_COLORSPACE_LINEAR_KHR, cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { + result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { + result = cnx->egl.eglStreamConsumerAcquireKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { + result = cnx->egl.eglStreamConsumerReleaseKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl( + EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; + + EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { + result = cnx->egl.eglGetStreamFileDescriptorKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl( + EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { + result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( + dp->disp.dpy, file_descriptor); + } + return result; +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 15 +// ---------------------------------------------------------------------------- + +// Need to template function type because return type is different +template <typename ReturnType, typename FuncType> +ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, + FuncType eglWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + ReturnType result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglWaitSyncFunc) { + result = eglWaitSyncFunc(dp->disp.dpy, sync, flags); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags); + +EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR); +} + +EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglWaitSync) { + return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags, + cnx->egl.eglWaitSync); + } + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return static_cast<EGLBoolean>( + eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR)); +} + +// ---------------------------------------------------------------------------- +// ANDROID extensions +// ---------------------------------------------------------------------------- + +EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + + EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { + result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLnsecsANDROID time) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + native_window_set_buffers_timestamp(s->getNativeWindow(), time); + + return EGL_TRUE; +} + +EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) { + // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus + // this function cannot be implemented when this libEGL is built for + // vendors. +#ifndef __ANDROID_VNDK__ + if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); + return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); +#else + return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); +#endif +} + +// ---------------------------------------------------------------------------- +// NVIDIA extensions +// ---------------------------------------------------------------------------- +EGLuint64NV eglGetSystemTimeFrequencyNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { + return cnx->egl.eglGetSystemTimeFrequencyNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +EGLuint64NV eglGetSystemTimeNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { + return cnx->egl.eglGetSystemTimeNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +// ---------------------------------------------------------------------------- +// Partial update extension +// ---------------------------------------------------------------------------- +EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglSetDamageRegionKHR) { + return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } + + return EGL_FALSE; +} + +EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR *frameId) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + uint64_t nextFrameId = 0; + int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId); + + if (ret != 0) { + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetNextFrameId: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + *frameId = nextFrameId; + return EGL_TRUE; +} + +EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* compositeDeadline = nullptr; + nsecs_t* compositeInterval = nullptr; + nsecs_t* compositeToPresentLatency = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (names[i]) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + compositeDeadline = &values[i]; + break; + case EGL_COMPOSITE_INTERVAL_ANDROID: + compositeInterval = &values[i]; + break; + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + compositeToPresentLatency = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_compositor_timing(s->getNativeWindow(), + compositeDeadline, compositeInterval, compositeToPresentLatency); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (name) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + return EGL_TRUE; + default: + return EGL_FALSE; + } +} + +EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* requestedPresentTime = nullptr; + nsecs_t* acquireTime = nullptr; + nsecs_t* latchTime = nullptr; + nsecs_t* firstRefreshStartTime = nullptr; + nsecs_t* gpuCompositionDoneTime = nullptr; + nsecs_t* lastRefreshStartTime = nullptr; + nsecs_t* displayPresentTime = nullptr; + nsecs_t* dequeueReadyTime = nullptr; + nsecs_t* releaseTime = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (timestamps[i]) { + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + requestedPresentTime = &values[i]; + break; + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + acquireTime = &values[i]; + break; + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + latchTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + firstRefreshStartTime = &values[i]; + break; + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + lastRefreshStartTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + gpuCompositionDoneTime = &values[i]; + break; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: + displayPresentTime = &values[i]; + break; + case EGL_DEQUEUE_READY_TIME_ANDROID: + dequeueReadyTime = &values[i]; + break; + case EGL_READS_DONE_TIME_ANDROID: + releaseTime = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, + requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, + lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, + dequeueReadyTime, releaseTime); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOENT: + return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + case -EINVAL: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetFrameTimestamps: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint timestamp) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (timestamp) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + case EGL_DEQUEUE_READY_TIME_ANDROID: + case EGL_READS_DONE_TIME_ANDROID: + return EGL_TRUE; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: { + int value = 0; + window->query(window, + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); + return value == 0 ? EGL_FALSE : EGL_TRUE; + } + default: + return EGL_FALSE; + } +} + +const GLubyte * glGetStringImpl(GLenum name) { + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetString(name); + } + return ret; +} + +const GLubyte * glGetStringiImpl(GLenum name, GLuint index) { + const GLubyte * ret = egl_get_string_for_current_context(name, index); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetStringi(name, index); + } + return ret; +} + +void glGetBooleanvImpl(GLenum pname, GLboolean * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = num_exts > 0 ? GL_TRUE : GL_FALSE; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetBooleanv(pname, data); +} + +void glGetFloatvImpl(GLenum pname, GLfloat * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLfloat)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetFloatv(pname, data); +} + +void glGetIntegervImpl(GLenum pname, GLint * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetIntegerv(pname, data); +} + +void glGetInteger64vImpl(GLenum pname, GLint64 * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint64)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetInteger64v(pname, data); +} + +struct implementation_map_t { + const char* name; + EGLFuncPointer address; +}; + +static const implementation_map_t sPlatformImplMap[] = { + // clang-format off + { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl }, + { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl }, + { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl }, + { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl }, + { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl }, + { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl }, + { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl }, + { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl }, + { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl }, + { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl }, + { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl }, + { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl }, + { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl }, + { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl }, + { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl }, + { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl }, + { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl }, + { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl }, + { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl }, + { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl }, + { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl }, + { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl }, + { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl }, + { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl }, + { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl }, + { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl }, + { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl }, + { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl }, + { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl }, + { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl }, + { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl }, + { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl }, + { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl }, + { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl }, + { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl }, + { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl }, + { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl }, + { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl }, + { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl }, + { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl }, + { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl }, + { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl }, + { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl }, + { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl }, + { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl }, + { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl }, + { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl }, + { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl }, + { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl }, + { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl }, + { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl }, + { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl }, + { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl }, + { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl }, + { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl }, + { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl }, + { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl }, + { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl }, + { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl }, + { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl }, + { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl }, + { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl }, + { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl }, + { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl }, + { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl }, + { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl }, + { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl }, + { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl }, + { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl }, + { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl }, + { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl }, + { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl }, + { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl }, + { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl }, + { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl }, + { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl }, + { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl }, + { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl }, + { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl }, + { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl }, + { "glGetString", (EGLFuncPointer)&glGetStringImpl }, + { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl }, + { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl }, + { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl }, + { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl }, + { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl }, + // clang-format on +}; + +EGLFuncPointer FindPlatformImplAddr(const char* name) +{ + static const bool DEBUG = false; + + if (name == nullptr) { + ALOGV("FindPlatformImplAddr called with null name"); + return nullptr; + } + + for (int i = 0; i < NELEM(sPlatformImplMap); i++) { + if (sPlatformImplMap[i].name == nullptr) { + ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name); + return nullptr; + } + if (!strcmp(name, sPlatformImplMap[i].name)) { + ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name); + return sPlatformImplMap[i].address; + } + } + + ALOGV("FindPlatformImplAddr did not find an entry for %s", name); + return nullptr; +} +} // namespace android diff --git a/opengl/libs/EGL/egl_platform_entries.h b/opengl/libs/EGL/egl_platform_entries.h new file mode 100644 index 0000000000..85b1db3fae --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef ANDROID_EGLAPI_H +#define ANDROID_EGLAPI_H + +#include <EGL/egl.h> + +typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; + +namespace android { + +EGLint eglGetErrorImpl(); +EGLFuncPointer FindPlatformImplAddr(const char* name); + +}; // namespace android + +#endif // ANDROID_EGLAPI_H + diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp index 8508c5fe9d..aaecb62194 100644 --- a/opengl/libs/EGL/egl_tls.cpp +++ b/opengl/libs/EGL/egl_tls.cpp @@ -21,6 +21,7 @@ #include <cutils/properties.h> #include <log/log.h> #include "CallStack.h" +#include "egl_platform_entries.h" namespace android { @@ -28,7 +29,7 @@ pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED; pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT; egl_tls_t::egl_tls_t() - : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(true) { + : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) { } const char *egl_tls_t::egl_strerror(EGLint err) { @@ -55,13 +56,38 @@ const char *egl_tls_t::egl_strerror(EGLint err) { void egl_tls_t::validateTLSKey() { struct TlsKeyInitializer { - static void create() { - pthread_key_create(&sKey, (void (*)(void*))&eglReleaseThread); - } + static void create() { pthread_key_create(&sKey, destructTLSData); } }; pthread_once(&sOnceKey, TlsKeyInitializer::create); } +void egl_tls_t::destructTLSData(void* data) { + egl_tls_t* tls = static_cast<egl_tls_t*>(data); + if (!tls) return; + + // Several things in the call tree of eglReleaseThread expect to be able to get the current + // thread state directly from TLS. That's a problem because Bionic has already cleared our + // TLS pointer before calling this function (pthread_getspecific(sKey) will return nullptr). + // Instead the data is passed as our parameter. + // + // Ideally we'd refactor this so we have thin wrappers that retrieve thread state from TLS and + // then pass it as a parameter (or 'this' pointer) to functions that do the real work without + // touching TLS. Then from here we could just call those implementation functions with the the + // TLS data we just received as a parameter. + // + // But that's a fairly invasive refactoring, so to do this robustly in the short term we just + // put the data *back* in TLS and call the top-level eglReleaseThread. It and it's call tree + // will retrieve the value from TLS, and then finally clear the TLS data. Bionic explicitly + // tolerates re-setting the value that it's currently trying to destruct (see + // pthread_key_clean_all()). Even if we forgot to clear the restored TLS data, bionic would + // call the destructor again, but eventually gives up and just leaks the data rather than + // enter an infinite loop. + pthread_setspecific(sKey, tls); + eglReleaseThread(); + ALOGE_IF(pthread_getspecific(sKey) != nullptr, + "EGL TLS data still exists after eglReleaseThread"); +} + void egl_tls_t::setErrorEtcImpl( const char* caller, int line, EGLint error, bool quiet) { validateTLSKey(); @@ -92,7 +118,7 @@ bool egl_tls_t::logNoContextCall() { egl_tls_t* egl_tls_t::getTLS() { egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); - if (tls == 0) { + if (tls == nullptr) { tls = new egl_tls_t; pthread_setspecific(sKey, tls); } @@ -103,7 +129,7 @@ void egl_tls_t::clearTLS() { if (sKey != TLS_KEY_NOT_INITIALIZED) { egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); if (tls) { - pthread_setspecific(sKey, 0); + pthread_setspecific(sKey, nullptr); delete tls; } } @@ -112,7 +138,7 @@ void egl_tls_t::clearTLS() { void egl_tls_t::clearError() { // This must clear the error from all the underlying EGL implementations as // well as the EGL wrapper layer. - eglGetError(); + android::eglGetErrorImpl(); } EGLint egl_tls_t::getError() { diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h index 9feae681bd..86a375c002 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -38,6 +38,7 @@ class egl_tls_t { egl_tls_t(); static void validateTLSKey(); + static void destructTLSData(void* data); static void setErrorEtcImpl( const char* caller, int line, EGLint error, bool quiet); diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index 985827655a..cca0053606 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -18,9 +18,13 @@ #define ANDROID_EGLDEFS_H #include "../hooks.h" +#include "egl_platform_entries.h" + +#include <log/log.h> #define VERSION_MAJOR 1 #define VERSION_MINOR 4 +#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) // ---------------------------------------------------------------------------- namespace android { @@ -31,23 +35,54 @@ const unsigned int NUM_DISPLAYS = 1; // ---------------------------------------------------------------------------- +extern char const * const platform_names[]; + +// clang-format off struct egl_connection_t { enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 }; - inline egl_connection_t() : dso(0) { } + inline egl_connection_t() : dso(nullptr) { + + char const* const* entries = platform_names; + EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); + while (*entries) { + const char* name = *entries; + EGLFuncPointer f = FindPlatformImplAddr(name); + + if (f == nullptr) { + // If no entry found, update the lookup table: sPlatformImplMap + ALOGE("No entry found in platform lookup table for %s", name); + } + + *curr++ = f; + entries++; + } + } + void * dso; gl_hooks_t * hooks[2]; EGLint major; EGLint minor; + EGLint driverVersion; egl_t egl; + // Functions implemented or redirected by platform libraries + platform_impl_t platform; + void* libEgl; void* libGles1; void* libGles2; + + bool angleDecided; + bool useAngle; + EGLint angleBackend; + void* vendorEGL; + void* featureSo; }; +// clang-format on // ---------------------------------------------------------------------------- @@ -58,6 +93,7 @@ extern "C" void gl_unimplemented(); extern "C" void gl_noop(); extern char const * const gl_names[]; +extern char const * const gl_names_1[]; extern char const * const egl_names[]; extern egl_connection_t gEGLImpl; diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index f7fde9625f..65f50f54fb 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -301,71 +301,31 @@ extern "C" { } const GLubyte * glGetString(GLenum name) { - const GLubyte * ret = egl_get_string_for_current_context(name); - if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetString(name); - } - return ret; + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetString(name); } const GLubyte * glGetStringi(GLenum name, GLuint index) { - const GLubyte * ret = egl_get_string_for_current_context(name, index); - if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetStringi(name, index); - } - return ret; + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetStringi(name, index); } void glGetBooleanv(GLenum pname, GLboolean * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = num_exts > 0 ? GL_TRUE : GL_FALSE; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetBooleanv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetBooleanv(pname, data); } void glGetFloatv(GLenum pname, GLfloat * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLfloat)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetFloatv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetFloatv(pname, data); } void glGetIntegerv(GLenum pname, GLint * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLint)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetIntegerv(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetIntegerv(pname, data); } void glGetInteger64v(GLenum pname, GLint64 * data) { - if (pname == GL_NUM_EXTENSIONS) { - int num_exts = egl_get_num_extensions_for_current_context(); - if (num_exts >= 0) { - *data = (GLint64)num_exts; - return; - } - } - - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if (_c) _c->glGetInteger64v(pname, data); + egl_connection_t* const cnx = egl_get_connection(); + return cnx->platform.glGetInteger64v(pname, data); } diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index a8855efa57..0af050175b 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -21,15 +21,18 @@ #include <EGL/eglext.h> #include <EGL/eglplatform.h> +#include "EGL/egldefs.h" #include "hooks.h" // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- + EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name); EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index); EGLAPI GLint egl_get_num_extensions_for_current_context(); +EGLAPI egl_connection_t* egl_get_connection(); // ---------------------------------------------------------------------------- }; // namespace android diff --git a/opengl/libs/entries_gles1.in b/opengl/libs/entries_gles1.in new file mode 100644 index 0000000000..c9be593dd9 --- /dev/null +++ b/opengl/libs/entries_gles1.in @@ -0,0 +1,298 @@ +GL_ENTRY(void, glActiveTexture, GLenum texture) +GL_ENTRY(void, glAlphaFunc, GLenum func, GLfloat ref) +GL_ENTRY(void, glAlphaFuncx, GLenum func, GLfixed ref) +GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLfixed ref) +GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) +GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer) +GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer) +GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) +GL_ENTRY(void, glBlendEquationOES, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparateOES, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glBlendFuncSeparateOES, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) +GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) +GL_ENTRY(GLenum, glCheckFramebufferStatusOES, GLenum target) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glClearColorx, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glClearColorxOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glClearDepthf, GLfloat d) +GL_ENTRY(void, glClearDepthfOES, GLclampf depth) +GL_ENTRY(void, glClearDepthx, GLfixed depth) +GL_ENTRY(void, glClearDepthxOES, GLfixed depth) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glClientActiveTexture, GLenum texture) +GL_ENTRY(GLenum, glClientWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glClipPlanef, GLenum p, const GLfloat *eqn) +GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn) +GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation) +GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn) +GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation) +GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) +GL_ENTRY(void, glColor4x, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glColor4xOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCopyTextureLevelsAPPLE, GLuint destinationTexture, GLuint sourceTexture, GLint sourceBaseLevel, GLsizei sourceLevelCount) +GL_ENTRY(void, glCullFace, GLenum mode) +GL_ENTRY(void, glCurrentPaletteMatrixOES, GLuint matrixpaletteindex) +GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) +GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences) +GL_ENTRY(void, glDeleteFramebuffersOES, GLsizei n, const GLuint *framebuffers) +GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint *renderbuffers) +GL_ENTRY(void, glDeleteSyncAPPLE, GLsync sync) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glDepthFunc, GLenum func) +GL_ENTRY(void, glDepthMask, GLboolean flag) +GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f) +GL_ENTRY(void, glDepthRangefOES, GLclampf n, GLclampf f) +GL_ENTRY(void, glDepthRangex, GLfixed n, GLfixed f) +GL_ENTRY(void, glDepthRangexOES, GLfixed n, GLfixed f) +GL_ENTRY(void, glDisable, GLenum cap) +GL_ENTRY(void, glDisableClientState, GLenum array) +GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) +GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices) +GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height) +GL_ENTRY(void, glDrawTexfvOES, const GLfloat *coords) +GL_ENTRY(void, glDrawTexiOES, GLint x, GLint y, GLint z, GLint width, GLint height) +GL_ENTRY(void, glDrawTexivOES, const GLint *coords) +GL_ENTRY(void, glDrawTexsOES, GLshort x, GLshort y, GLshort z, GLshort width, GLshort height) +GL_ENTRY(void, glDrawTexsvOES, const GLshort *coords) +GL_ENTRY(void, glDrawTexxOES, GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height) +GL_ENTRY(void, glDrawTexxvOES, const GLfixed *coords) +GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEnable, GLenum cap) +GL_ENTRY(void, glEnableClientState, GLenum array) +GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask) +GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, void **params) +GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length) +GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms) +GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders) +GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, void *texels) +GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures) +GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program) +GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param) +GL_ENTRY(GLsync, glFenceSyncAPPLE, GLenum condition, GLbitfield flags) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFinishFenceNV, GLuint fence) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length) +GL_ENTRY(void, glFogf, GLenum pname, GLfloat param) +GL_ENTRY(void, glFogfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glFogx, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxv, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glFrontFace, GLenum mode) +GL_ENTRY(void, glFrustumf, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glFrustumfOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glFrustumx, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glFrustumxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) +GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences) +GL_ENTRY(void, glGenFramebuffersOES, GLsizei n, GLuint *framebuffers) +GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint *renderbuffers) +GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) +GL_ENTRY(void, glGenerateMipmapOES, GLenum target) +GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data) +GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params) +GL_ENTRY(void, glGetClipPlanef, GLenum plane, GLfloat *equation) +GL_ENTRY(void, glGetClipPlanefOES, GLenum plane, GLfloat *equation) +GL_ENTRY(void, glGetClipPlanex, GLenum plane, GLfixed *equation) +GL_ENTRY(void, glGetClipPlanexOES, GLenum plane, GLfixed *equation) +GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params) +GL_ENTRY(void, glGetFixedv, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetFixedvOES, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data) +GL_ENTRY(void, glGetFramebufferAttachmentParameterivOES, GLenum target, GLenum attachment, GLenum pname, GLint *params) +GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void) +GL_ENTRY(void, glGetInteger64vAPPLE, GLenum pname, GLint64 *params) +GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data) +GL_ENTRY(void, glGetLightfv, GLenum light, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetLightxv, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetLightxvOES, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) +GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(const GLubyte *, glGetString, GLenum name) +GL_ENTRY(void, glGetSyncivAPPLE, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) +GL_ENTRY(void, glGetTexEnvfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexEnviv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexEnvxv, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexEnvxvOES, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexGenfvOES, GLenum coord, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexGenivOES, GLenum coord, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexGenxvOES, GLenum coord, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glHint, GLenum target, GLenum mode) +GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) +GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) +GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence) +GL_ENTRY(GLboolean, glIsFramebufferOES, GLuint framebuffer) +GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer) +GL_ENTRY(GLboolean, glIsSyncAPPLE, GLsync sync) +GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) +GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxv, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glLightModelxvOES, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glLightf, GLenum light, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightfv, GLenum light, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLightx, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxOES, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxv, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLightxvOES, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLineWidthx, GLfixed width) +GL_ENTRY(void, glLineWidthxOES, GLfixed width) +GL_ENTRY(void, glLoadIdentity, void) +GL_ENTRY(void, glLoadMatrixf, const GLfloat *m) +GL_ENTRY(void, glLoadMatrixx, const GLfixed *m) +GL_ENTRY(void, glLoadMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glLoadPaletteFromModelViewMatrixOES, void) +GL_ENTRY(void, glLogicOp, GLenum opcode) +GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access) +GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) +GL_ENTRY(void, glMaterialf, GLenum face, GLenum pname, GLfloat param) +GL_ENTRY(void, glMaterialfv, GLenum face, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glMaterialx, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxOES, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxv, GLenum face, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glMaterialxvOES, GLenum face, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glMatrixIndexPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glMatrixMode, GLenum mode) +GL_ENTRY(void, glMultMatrixf, const GLfloat *m) +GL_ENTRY(void, glMultMatrixx, const GLfixed *m) +GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) +GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +GL_ENTRY(void, glMultiTexCoord4x, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glMultiTexCoord4xOES, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glNormal3f, GLfloat nx, GLfloat ny, GLfloat nz) +GL_ENTRY(void, glNormal3x, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glNormal3xOES, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glNormalPointer, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glOrthof, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glOrthofOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glOrthox, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glOrthoxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) +GL_ENTRY(void, glPointParameterf, GLenum pname, GLfloat param) +GL_ENTRY(void, glPointParameterfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glPointParameterx, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxv, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointParameterxvOES, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointSize, GLfloat size) +GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glPointSizex, GLfixed size) +GL_ENTRY(void, glPointSizexOES, GLfixed size) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units) +GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units) +GL_ENTRY(void, glPopGroupMarkerEXT, void) +GL_ENTRY(void, glPopMatrix, void) +GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(void, glPushMatrix, void) +GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed *mantissa, GLint *exponent) +GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) +GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) +GL_ENTRY(void, glRenderbufferStorageMultisampleAPPLE, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glResolveMultisampleFramebufferAPPLE, void) +GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glRotatexOES, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragexOES, GLclampx value, GLboolean invert) +GL_ENTRY(void, glScalef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glScalex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glScalexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition) +GL_ENTRY(void, glShadeModel, GLenum mode) +GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilMask, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence) +GL_ENTRY(void, glTexCoordPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glTexEnvf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexEnvfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexEnvi, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexEnviv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexEnvx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnvxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnvxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexEnvxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexGenfOES, GLenum coord, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexGenfvOES, GLenum coord, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param) +GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) +GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer) diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index 81dbe0e34b..63a0e140cc 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -59,6 +59,10 @@ namespace android { #define GL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__); #define EGL_ENTRY(_r, _api, ...) _r (*(_api))(__VA_ARGS__); +struct platform_impl_t { + #include "platform_entries.in" +}; + struct egl_t { #include "EGL/egl_entries.in" }; diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt index fa26e33f39..b2d795745f 100644 --- a/opengl/libs/libEGL.map.txt +++ b/opengl/libs/libEGL.map.txt @@ -3,23 +3,30 @@ LIBEGL { eglBindAPI; eglBindTexImage; eglChooseConfig; + eglClientWaitSync; # introduced=29 eglClientWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglCopyBuffers; eglCreateContext; + eglCreateImage; # introduced=29 eglCreateImageKHR; eglCreateNativeClientBufferANDROID; # introduced=24 eglCreatePbufferFromClientBuffer; eglCreatePbufferSurface; eglCreatePixmapSurface; + eglCreatePlatformPixmapSurface; # introduced=29 + eglCreatePlatformWindowSurface; # introduced=29 eglCreateStreamFromFileDescriptorKHR; # introduced=23 eglCreateStreamKHR; # introduced=23 eglCreateStreamProducerSurfaceKHR; # introduced=23 + eglCreateSync; # introduced=29 eglCreateSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglCreateWindowSurface; eglDestroyContext; + eglDestroyImage; # introduced=29 eglDestroyImageKHR; eglDestroyStreamKHR; # introduced=23 eglDestroySurface; + eglDestroySync; # introduced=29 eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglDupNativeFenceFDANDROID; # vndk eglGetConfigAttrib; @@ -30,8 +37,10 @@ LIBEGL { eglGetDisplay; eglGetError; eglGetNativeClientBufferANDROID; # introduced=26 + eglGetPlatformDisplay; # introduced=29 eglGetProcAddress; eglGetStreamFileDescriptorKHR; # introduced=23 + eglGetSyncAttrib; # introduced=29 eglGetSyncAttribKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 eglGetSystemTimeFrequencyNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21 eglGetSystemTimeNV; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21 @@ -64,6 +73,7 @@ LIBEGL { eglWaitClient; eglWaitGL; eglWaitNative; + eglWaitSync; # introduced=29 eglWaitSyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 local: *; diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in new file mode 100644 index 0000000000..46734112d3 --- /dev/null +++ b/opengl/libs/platform_entries.in @@ -0,0 +1,86 @@ +EGL_ENTRY(EGLDisplay, eglGetDisplay, EGLNativeDisplayType) +EGL_ENTRY(EGLDisplay, eglGetPlatformDisplay, EGLenum, EGLNativeDisplayType, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*) +EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay) +EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*) +EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*) +EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint*) +EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint*) +EGL_ENTRY(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*) +EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint*) +EGL_ENTRY(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, EGLConfig, void*, const EGLAttrib*) +EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint*) +EGL_ENTRY(void, eglBeginFrame, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext) +EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext) +EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint*) +EGL_ENTRY(EGLContext, eglGetCurrentContext, void) +EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint) +EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void) +EGL_ENTRY(EGLBoolean, eglWaitGL, void) +EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint) +EGL_ENTRY(EGLint, eglGetError, void) +EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*) +EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType) +EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint) +EGL_ENTRY(const char*, eglQueryStringImplementationANDROID, EGLDisplay, EGLint) +EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint) +EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint) +EGL_ENTRY(EGLBoolean, eglWaitClient, void) +EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum) +EGL_ENTRY(EGLenum, eglQueryAPI, void) +EGL_ENTRY(EGLBoolean, eglReleaseThread, void) +EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint*) +EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint*) +EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface) +EGL_ENTRY(EGLImage, eglCreateImage, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglDestroyImage, EGLDisplay, EGLImage) +EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR) +EGL_ENTRY(EGLSync, eglCreateSync, EGLDisplay, EGLenum, const EGLAttrib*) +EGL_ENTRY(EGLBoolean, eglDestroySync, EGLDisplay, EGLSync) +EGL_ENTRY(EGLint, eglClientWaitSync, EGLDisplay, EGLSync, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttrib, EGLDisplay, EGLSyncKHR, EGLint, EGLAttrib*) +EGL_ENTRY(EGLSyncKHR, eglCreateSyncKHR, EGLDisplay, EGLenum, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroySyncKHR, EGLDisplay, EGLSyncKHR) +EGL_ENTRY(EGLBoolean, eglSignalSyncKHR, EGLDisplay, EGLSyncKHR, EGLenum) +EGL_ENTRY(EGLint, eglClientWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLTimeKHR) +EGL_ENTRY(EGLBoolean, eglGetSyncAttribKHR, EGLDisplay, EGLSyncKHR, EGLint, EGLint*) +EGL_ENTRY(EGLStreamKHR, eglCreateStreamKHR, EGLDisplay, const EGLint*) +EGL_ENTRY(EGLBoolean, eglDestroyStreamKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamAttribKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint) +EGL_ENTRY(EGLBoolean, eglQueryStreamKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLint*) +EGL_ENTRY(EGLBoolean, eglQueryStreamu64KHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLuint64KHR*) +EGL_ENTRY(EGLBoolean, eglQueryStreamTimeKHR, EGLDisplay, EGLStreamKHR, EGLenum, EGLTimeKHR*) +EGL_ENTRY(EGLSurface, eglCreateStreamProducerSurfaceKHR, EGLDisplay, EGLConfig, EGLStreamKHR, const EGLint*) +EGL_ENTRY(EGLBoolean, eglStreamConsumerGLTextureExternalKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamConsumerAcquireKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLBoolean, eglStreamConsumerReleaseKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, EGLDisplay, EGLStreamKHR) +EGL_ENTRY(EGLStreamKHR, eglCreateStreamFromFileDescriptorKHR, EGLDisplay, EGLNativeFileDescriptorKHR) +EGL_ENTRY(EGLint, eglWaitSync, EGLDisplay, EGLSync, EGLint) +EGL_ENTRY(EGLint, eglWaitSyncKHR, EGLDisplay, EGLSyncKHR, EGLint) +EGL_ENTRY(EGLint, eglDupNativeFenceFDANDROID, EGLDisplay, EGLSyncKHR) +EGL_ENTRY(EGLBoolean, eglPresentationTimeANDROID, EGLDisplay, EGLSurface, EGLnsecsANDROID) +EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffer*) +EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void) +EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void) +EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint*, EGLint) +EGL_ENTRY(EGLBoolean, eglGetNextFrameIdANDROID, EGLDisplay, EGLSurface, EGLuint64KHR*) +EGL_ENTRY(EGLBoolean, eglGetCompositorTimingANDROID, EGLDisplay, EGLSurface, EGLint, const EGLint*, EGLnsecsANDROID*) +EGL_ENTRY(EGLBoolean, eglGetCompositorTimingSupportedANDROID, EGLDisplay, EGLSurface, EGLint) +EGL_ENTRY(EGLBoolean, eglGetFrameTimestampsANDROID, EGLDisplay, EGLSurface, EGLuint64KHR, EGLint, const EGLint*, EGLnsecsANDROID*) +EGL_ENTRY(EGLBoolean, eglGetFrameTimestampSupportedANDROID, EGLDisplay, EGLSurface, EGLint) +GL_ENTRY(const GLubyte*, glGetString, GLenum) +GL_ENTRY(const GLubyte*, glGetStringi, GLenum, GLuint) +GL_ENTRY(void, glGetBooleanv, GLenum, GLboolean*) +GL_ENTRY(void, glGetFloatv, GLenum, GLfloat*) +GL_ENTRY(void, glGetIntegerv, GLenum, GLint*) +GL_ENTRY(void, glGetInteger64v, GLenum, GLint64*) diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles deleted file mode 100755 index feef3186a8..0000000000 --- a/opengl/libs/tools/genfiles +++ /dev/null @@ -1,50 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2008 Google Inc. -# -# 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. - -# Force a specific locale for sorting to avoid irrelevant differences -# in the generated files that could hide real differences. -export LC_ALL=POSIX - -./glapigen ../../include/GLES/gl.h > ../GLES_CM/gl_api.in -./glapigen ../../include/GLES/glext.h > ../GLES_CM/glext_api.in -./glapigen ../../include/GLES3/gl3.h > ../GLES2/gl2_api.in -./glapigen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_api.in - -./glentrygen ../../include/GLES/gl.h > /tmp/gl_entries.in -./glentrygen ../../include/GLES/glext.h > /tmp/glext_entries.in -./glentrygen ../../include/GLES3/gl3.h > /tmp/gl2_entries.in -./glentrygen ../../include/GLES2/gl2ext.h > /tmp/gl2ext_entries.in - -# The awk command removes lines with the same function name as an earlier -# line, even if the rest of the line differs. Although signatures of -# functions with the same name should be the same, the different versions -# have some irrelevant whitespace and parameter name differences. -cat /tmp/gl_entries.in \ - /tmp/glext_entries.in \ - /tmp/gl2_entries.in \ - /tmp/gl2ext_entries.in \ - | sort -t, -k2 \ - | awk -F, '!_[$2]++' \ - > ../entries.in - -cat ../../include/GLES/gl.h \ - ../../include/GLES/glext.h \ - ../../include/GLES2/gl2ext.h \ - ../../include/GLES3/gl3.h \ - | ./glenumsgen \ - | sort \ - > ../enums.in - diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen deleted file mode 100755 index 4d8334f90f..0000000000 --- a/opengl/libs/tools/glapigen +++ /dev/null @@ -1,76 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -sub rtrim($) -{ - my $string = shift; - $string =~ s/\s+$//; - return $string; -} - -while (my $line = <>) { - next if $line =~ /^\//; - next if $line =~ /^#/; - next if $line =~ /^\s*$/; - if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) { - next; - } - my $type = rtrim($2); - my $name = $3; - my $args = $4; - - #printf("%s", $line); - - my $prefix = ""; - if ($name eq "glGetString") { - $prefix = "__"; - } - - printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args); - - printf(" {\n"); - if ($type eq "void") { - printf(" CALL_GL_API(%s", $name); - } else { - printf(" CALL_GL_API_RETURN(%s", $name); - } - my @args = split ',', $args; - my $len = scalar(@args); - for (my $num = 0; $num < $len; $num++) { - if ($args[$num] ne "void") { - print ", "; - # - # extract the name from the parameter - # type name - # const type *name - # type *name - # type name[4] - # - if ($args[$num] =~ /(\S+\s)+\**\s*([\w]+)/) { - printf("%s", $2); - } - } - } - printf(");\n"); - printf("}\n"); -} - - - - - diff --git a/opengl/libs/tools/glentrygen b/opengl/libs/tools/glentrygen deleted file mode 100755 index 170f04131a..0000000000 --- a/opengl/libs/tools/glentrygen +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2008 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -sub rtrim($) -{ - my $string = shift; - $string =~ s/\s+$//; - return $string; -} - -while (my $line = <>) { - next if $line =~ /^\//; - next if $line =~ /^#/; - next if $line =~ /^\s*$/; - if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) { - next; - } - my $type = rtrim($2); - my $name = $3; - my $args = $4; - - printf("GL_ENTRY(%s, %s, %s)\n", $type, $name, $args); -} diff --git a/opengl/libs/tools/glenumsgen b/opengl/libs/tools/glenumsgen deleted file mode 100755 index 2ae5fbfa25..0000000000 --- a/opengl/libs/tools/glenumsgen +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2010 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -my %enumHash = (); - -while (my $line = <STDIN>) { - next if $line =~ /^\//; - # Skip bitfield definitions. - next if $line =~ /_BIT(\d+_|\s+)/; - if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) { - next; - } - my $enumName = $1; - my $enumValue = $2; - next if exists($enumHash { $enumValue }); - $enumHash { $enumValue } = $enumName; - printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName); -} - - - - - diff --git a/opengl/specs/README b/opengl/specs/README index fdafb1bffb..6d597d5519 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -19,10 +19,7 @@ Khronos. 0x3145 EGL_SYNC_NATIVE_FENCE_FD_ANDROID (EGL_ANDROID_native_fence_sync) 0x3146 EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID (EGL_ANDROID_native_fence_sync) 0x3147 EGL_FRAMEBUFFER_TARGET_ANDROID (EGL_ANDROID_framebuffer_target) -0x3148 EGL_IMAGE_CROP_LEFT_ANDROID (EGL_ANDROID_image_crop) -0x3149 EGL_IMAGE_CROP_TOP_ANDROID (EGL_ANDROID_image_crop) -0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop) -0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) +0x3148 - 0x314B previously used by the undocumented, deprecated extension EGL_ANDROID_image_crop 0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh) 0x314D EGL_GL_COLORSPACE_DEFAULT_EXT (EGL_EXT_image_gl_colorspace) 0x314E - 0x314F (unused) diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp index 2b76279801..b06422a98c 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -57,7 +57,7 @@ WindowSurface::WindowSurface() { sp<SurfaceControl> sc = surfaceComposerClient->createSurface( String8("Benchmark"), width, height, PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque); - if (sc == NULL || !sc->isValid()) { + if (sc == nullptr || !sc->isValid()) { fprintf(stderr, "Failed to create SurfaceControl\n"); return; } diff --git a/opengl/tests/lib/glTestLib.cpp b/opengl/tests/lib/glTestLib.cpp index 213dffd50f..290d7a0de2 100644 --- a/opengl/tests/lib/glTestLib.cpp +++ b/opengl/tests/lib/glTestLib.cpp @@ -37,7 +37,7 @@ void glTestPrintGLString(const char *name, GLenum s) { const char *v = (const char *) glGetString(s); - if (v == NULL) { + if (v == nullptr) { testPrintI("GL %s unknown", name); } else { testPrintI("GL %s = %s", name, v); diff --git a/opengl/tests/lib/include/EGLUtils.h b/opengl/tests/lib/include/EGLUtils.h index 9dc6bcf56a..eb9571d478 100644 --- a/opengl/tests/lib/include/EGLUtils.h +++ b/opengl/tests/lib/include/EGLUtils.h @@ -100,11 +100,11 @@ status_t EGLUtils::selectConfigForPixelFormat( if (!attrs) return BAD_VALUE; - if (outConfig == NULL) + if (outConfig == nullptr) return BAD_VALUE; // Get all the "potential match" configs... - if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE) + if (eglGetConfigs(dpy, nullptr, 0, &numConfigs) == EGL_FALSE) return BAD_VALUE; std::vector<EGLConfig> configs(numConfigs); @@ -113,7 +113,7 @@ status_t EGLUtils::selectConfigForPixelFormat( } int i; - EGLConfig config = NULL; + EGLConfig config = nullptr; for (i=0 ; i<n ; i++) { EGLint nativeVisualId = 0; eglGetConfigAttrib(dpy, configs[i], EGL_NATIVE_VISUAL_ID, &nativeVisualId); @@ -243,7 +243,7 @@ String8 EGLUtils::printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { bool EGLUtils::printEGLConfigurations(EGLDisplay dpy, String8& msg) { EGLint numConfig = 0; - EGLint returnVal = eglGetConfigs(dpy, NULL, 0, &numConfig); + EGLint returnVal = eglGetConfigs(dpy, nullptr, 0, &numConfig); msg.append(checkEglError("eglGetConfigs", returnVal)); if (!returnVal) { return false; diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index f9e96eacb6..9fa58e2b58 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -93,6 +93,7 @@ rm src/*.class pushd out > /dev/null mkdir classes javac -d classes android/opengl/EGL14.java \ + android/opengl/EGL15.java \ android/opengl/EGLExt.java \ com/google/android/gles_jni/GLImpl.java \ javax/microedition/khronos/opengles/GL10.java \ @@ -155,13 +156,13 @@ do compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x done -for x in EGL14 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32 +for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32 do compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp done -for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface +for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync do compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java done diff --git a/opengl/tools/glgen/specs/egl/EGL15.spec b/opengl/tools/glgen/specs/egl/EGL15.spec new file mode 100644 index 0000000000..e0aad30f3c --- /dev/null +++ b/opengl/tools/glgen/specs/egl/EGL15.spec @@ -0,0 +1,14 @@ +EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) +EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) +EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) +EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) +// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib +// so that the generate creates mostly correct code (do not want a buffer) +// have to manually change cast to (void *) in generated code that calls +// the native function. +EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) +EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) +EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) +EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) +EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list ) +EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image ) diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec index ae531eef78..e2bae482d5 100644 --- a/opengl/tools/glgen/specs/egl/checks.spec +++ b/opengl/tools/glgen/specs/egl/checks.spec @@ -11,3 +11,5 @@ eglQuerySurface check value 1 //STUB function: eglCreatePbufferFromClientBuffer nullAllowed attrib_list sentinel attrib_list EGL_NONE eglCreateContext sentinel attrib_list EGL_NONE eglQueryContext check value 1 +//unsupported: eglCreatePlatformPixmapSurface nullAllowed attrib_list sentinel attrib_list EGL_NONE +eglCreatePlatformPixmapSurface unsupported diff --git a/opengl/tools/glgen/src/CType.java b/opengl/tools/glgen/src/CType.java index aba98afb95..b1f8e0580d 100644 --- a/opengl/tools/glgen/src/CType.java +++ b/opengl/tools/glgen/src/CType.java @@ -57,7 +57,9 @@ public class CType { if(baseType.equals("EGLContext") || baseType.equals("EGLConfig") || baseType.equals("EGLSurface") - || baseType.equals("EGLDisplay")) { + || baseType.equals("EGLDisplay") + || baseType.equals("EGLImage") + || baseType.equals("EGLSync")) { return true; } return false; diff --git a/opengl/tools/glgen/src/GenerateEGL.java b/opengl/tools/glgen/src/GenerateEGL.java index 2ef3970299..57958c6ef2 100644 --- a/opengl/tools/glgen/src/GenerateEGL.java +++ b/opengl/tools/glgen/src/GenerateEGL.java @@ -84,7 +84,7 @@ public class GenerateEGL { ParameterChecker checker = new ParameterChecker(checksReader); - for(String suffix: new String[] {"EGL14", "EGLExt"}) { + for(String suffix: new String[] {"EGL14", "EGL15", "EGLExt"}) { BufferedReader specReader = new BufferedReader(new FileReader( "specs/egl/" + suffix + ".spec")); String egljFilename = "android/opengl/" + suffix + ".java"; diff --git a/opengl/tools/glgen/src/JType.java b/opengl/tools/glgen/src/JType.java index 7f08503fa6..0b4401ad99 100644 --- a/opengl/tools/glgen/src/JType.java +++ b/opengl/tools/glgen/src/JType.java @@ -60,12 +60,16 @@ public class JType { typeMapping.put(new CType("EGLNativeDisplayType"), new JType("long")); typeMapping.put(new CType("EGLClientBuffer"), new JType("long")); typeMapping.put(new CType("EGLnsecsANDROID"), new JType("long")); + typeMapping.put(new CType("EGLAttrib"), new JType("long")); + typeMapping.put(new CType("EGLTime"), new JType("long")); // EGL nonprimitive types typeMapping.put(new CType("EGLConfig"), new JType("EGLConfig", true, false)); typeMapping.put(new CType("EGLContext"), new JType("EGLContext", true, false)); typeMapping.put(new CType("EGLDisplay"), new JType("EGLDisplay", true, false)); typeMapping.put(new CType("EGLSurface"), new JType("EGLSurface", true, false)); + typeMapping.put(new CType("EGLImage"), new JType("EGLImage", true, false)); + typeMapping.put(new CType("EGLSync"), new JType("EGLSync", true, false)); // Untyped pointers map to untyped Buffers @@ -139,6 +143,8 @@ public class JType { arrayTypeMapping.put(new CType("EGLint", true, true), new JType("int", false, true)); arrayTypeMapping.put(new CType("EGLConfig", false, true), new JType("EGLConfig", true, true)); arrayTypeMapping.put(new CType("EGLConfig", true, true), new JType("EGLConfig", true, true)); + arrayTypeMapping.put(new CType("EGLAttrib", false, true), new JType("long", false, true)); + arrayTypeMapping.put(new CType("EGLAttrib", true, true), new JType("long", false, true)); } diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java index e8691bb7f8..6697189695 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -103,6 +103,12 @@ public class JniCodeEmitter { if (cfunc.hasEGLHandleArg()) { return; } + // eglGetPlatformDisplay does not have any EGLHandleArgs + // but we do not want to create IOBuffers of this, so + // exit + if (cfunc.getName().equals("eglGetPlatformDisplay")) { + return; + } } jfunc = JFunc.convert(cfunc, false); diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java new file mode 100644 index 0000000000..731ce72aa0 --- /dev/null +++ b/opengl/tools/glgen/static/egl/EGLImage.java @@ -0,0 +1,37 @@ +/* +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.opengl; + +/** + * Wrapper class for native EGLImage objects. + * + */ +public class EGLImage extends EGLObjectHandle { + private EGLImage(long handle) { + super(handle); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EGLImage)) return false; + + EGLImage that = (EGLImage) o; + return getNativeHandle() == that.getNativeHandle(); + } +} diff --git a/opengl/tools/glgen/static/egl/EGLSync.java b/opengl/tools/glgen/static/egl/EGLSync.java new file mode 100644 index 0000000000..472f9e7125 --- /dev/null +++ b/opengl/tools/glgen/static/egl/EGLSync.java @@ -0,0 +1,37 @@ +/* +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.opengl; + +/** + * Wrapper class for native EGLSync objects. + * + */ +public class EGLSync extends EGLObjectHandle { + private EGLSync(long handle) { + super(handle); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EGLSync)) return false; + + EGLSync that = (EGLSync) o; + return getNativeHandle() == that.getNativeHandle(); + } +} diff --git a/opengl/tools/glgen/stubs/egl/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if new file mode 100644 index 0000000000..859380f6fc --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if @@ -0,0 +1,78 @@ +/* +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.opengl; + +/** + * EGL 1.5 + * + */ +public final class EGL15 { + + private EGL15() {}; + + public static final int EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT = 0x00000001; + public static final int EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT = 0x00000002; + public static final int EGL_OPENGL_ES3_BIT = 0x00000040; + public static final int EGL_SYNC_FLUSH_COMMANDS_BIT = 0x0001; + public static final int EGL_GL_COLORSPACE_SRGB = 0x3089; + public static final int EGL_GL_COLORSPACE_LINEAR = 0x308A; + public static final int EGL_CONTEXT_MAJOR_VERSION = 0x3098; + public static final int EGL_CL_EVENT_HANDLE = 0x309C; + public static final int EGL_GL_COLORSPACE = 0x309D; + public static final int EGL_GL_TEXTURE_2D = 0x30B1; + public static final int EGL_GL_TEXTURE_3D = 0x30B2; + public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X = 0x30B3; + public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X = 0x30B4; + public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y = 0x30B5; + public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x30B6; + public static final int EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z = 0x30B7; + public static final int EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x30B8; + public static final int EGL_GL_RENDERBUFFER = 0x30B9; + public static final int EGL_GL_TEXTURE_LEVEL = 0x30BC; + public static final int EGL_GL_TEXTURE_ZOFFSET = 0x30BD; + public static final int EGL_IMAGE_PRESERVED = 0x30D2; + public static final int EGL_SYNC_PRIOR_COMMANDS_COMPLETE = 0x30F0; + public static final int EGL_SYNC_STATUS = 0x30F1; + public static final int EGL_SIGNALED = 0x30F2; + public static final int EGL_UNSIGNALED = 0x30F3; + public static final int EGL_TIMEOUT_EXPIRED = 0x30F5; + public static final int EGL_CONDITION_SATISFIED = 0x30F6; + public static final int EGL_SYNC_TYPE = 0x30F7; + public static final int EGL_SYNC_CONDITION = 0x30F8; + public static final int EGL_SYNC_FENCE = 0x30F9; + public static final int EGL_CONTEXT_MINOR_VERSION = 0x30FB; + public static final int EGL_CONTEXT_OPENGL_PROFILE_MASK = 0x30FD; + public static final int EGL_SYNC_CL_EVENT = 0x30FE; + public static final int EGL_SYNC_CL_EVENT_COMPLETE = 0x30FF; + public static final int EGL_CONTEXT_OPENGL_DEBUG = 0x31B0; + public static final int EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE = 0x31B1; + public static final int EGL_CONTEXT_OPENGL_ROBUST_ACCESS = 0x31B2; + public static final int EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY = 0x31BD; + public static final int EGL_NO_RESET_NOTIFICATION = 0x31BE; + public static final int EGL_LOSE_CONTEXT_ON_RESET = 0x31BF; + public static final int EGL_PLATFORM_ANDROID_KHR = 0x3141; + public static final long EGL_FOREVER = 0xFFFFFFFFFFFFFFFFL; + public static final EGLImage EGL_NO_IMAGE = null; + public static final EGLSync EGL_NO_SYNC = null; + public static final EGLContext EGL_NO_CONTEXT = null; + public static final EGLDisplay EGL_NO_DISPLAY = null; + public static final EGLSurface EGL_NO_SURFACE = null; + + native private static void _nativeClassInit(); + static { + _nativeClassInit(); + } diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp new file mode 100644 index 0000000000..70b46f7585 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -0,0 +1,205 @@ +/* +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" + +#include <android_runtime/AndroidRuntime.h> +#include <nativehelper/JNIHelp.h> +#include <utils/misc.h> +#include "jni.h" + +#include <EGL/egl.h> +#include <assert.h> + +#include <ui/ANativeObjectBase.h> + +static int initialized = 0; + +// classes from EGL 1.4 +static jclass egldisplayClass; +static jclass eglsurfaceClass; +static jclass eglconfigClass; +static jclass eglcontextClass; +static jclass bufferClass; +static jclass nioAccessClass; + +static jfieldID positionID; +static jfieldID limitID; +static jfieldID elementSizeShiftID; + +static jmethodID getBasePointerID; +static jmethodID getBaseArrayID; +static jmethodID getBaseArrayOffsetID; + +static jmethodID egldisplayGetHandleID; +static jmethodID eglconfigGetHandleID; +static jmethodID eglcontextGetHandleID; +static jmethodID eglsurfaceGetHandleID; + +static jmethodID egldisplayConstructor; +static jmethodID eglcontextConstructor; +static jmethodID eglsurfaceConstructor; +static jmethodID eglconfigConstructor; + +static jobject eglNoContextObject; +static jobject eglNoDisplayObject; +static jobject eglNoSurfaceObject; + +// classes from EGL 1.5 +static jclass eglimageClass; +static jclass eglsyncClass; + +static jmethodID eglimageGetHandleID; +static jmethodID eglsyncGetHandleID; + +static jmethodID eglimageConstructor; +static jmethodID eglsyncConstructor; + +static jobject eglNoImageObject; +static jobject eglNoSyncObject; + +/* Cache method IDs each time the class is loaded. */ + +static void nativeClassInit(JNIEnv *_env, jclass glImplClass) { + // EGL 1.4 Init + jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); + eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal); + jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); + eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal); + jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); + egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal); + jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); + eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal); + + eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); + eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); + egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); + eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); + + eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); + eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); + egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); + eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); + + jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, + reinterpret_cast<jlong>(EGL_NO_CONTEXT)); + eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); + jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, + reinterpret_cast<jlong>(EGL_NO_DISPLAY)); + eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); + jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, + reinterpret_cast<jlong>(EGL_NO_SURFACE)); + eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); + + jclass eglClass = _env->FindClass("android/opengl/EGL15"); + jfieldID noContextFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); + _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject); + + jfieldID noDisplayFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); + _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject); + + jfieldID noSurfaceFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); + _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject); + + // EGL 1.5 init + jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); + nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal); + + jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); + bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal); + + getBasePointerID = + _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", + "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = + _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + positionID = _env->GetFieldID(bufferClass, "position", "I"); + limitID = _env->GetFieldID(bufferClass, "limit", "I"); + elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); + + jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage"); + eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal); + jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); + eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal); + + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V"); + eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V"); + + jfieldID noImageFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;"); + _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject); + + jfieldID noSyncFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;"); + _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject); +} + +static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, + jint *offset) { + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + + position = _env->GetIntField(buffer, positionID); + limit = _env->GetIntField(buffer, limitID); + elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + *remaining = (limit - position) << elementSizeShift; + pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return reinterpret_cast<void *>(pointer); + } + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); + + return NULL; +} + +static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) { + _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); +} + +static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { + if (obj == NULL) { + jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + } + + jlong handle = _env->CallLongMethod(obj, mid); + return reinterpret_cast<void *>(handle); +} + +static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) { + if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) { + return eglNoImageObject; + } + + return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); +} + +// -------------------------------------------------------------------------- diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp new file mode 100644 index 0000000000..fd44498e43 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp @@ -0,0 +1,46 @@ +/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */ +static jobject +android_eglGetPlatformDisplay + (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + EGLDisplay _returnValue = (EGLDisplay) 0; + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _remaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _remaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + _returnValue = eglGetPlatformDisplay( + (EGLenum)platform, + (void *)native_display, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue); +} + diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java new file mode 100644 index 0000000000..28945e8247 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.java @@ -0,0 +1,9 @@ + // C function EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) + + public static native EGLDisplay eglGetPlatformDisplay( + int platform, + long native_display, + long[] attrib_list, + int offset + ); + diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg new file mode 100644 index 0000000000..8a309bf0a2 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.nativeReg @@ -0,0 +1 @@ +{"eglGetPlatformDisplay", "(IJ[JI)Landroid/opengl/EGLDisplay;", (void *) android_eglGetPlatformDisplay }, diff --git a/opengl/tools/glgen2/glgen.py b/opengl/tools/glgen2/glgen.py index fa981a89a7..86fa28fb1f 100755 --- a/opengl/tools/glgen2/glgen.py +++ b/opengl/tools/glgen2/glgen.py @@ -250,6 +250,27 @@ if __name__ == '__main__': for opts in TRAMPOLINE_OPTIONS: registry.apiGen(opts) + # Generate a GLESv1_CM entries separately to avoid extra driver loading time + apigen = ApiGenerator() + registry.setGenerator(apigen) + API_OPTIONS = [ + # Generate non-extension versions of each API first, then extensions, + # so that if an extension enum was later standardized, we see the non- + # suffixed version first. + reg.GeneratorOptions( + apiname = 'gles1', + profile = 'common'), + reg.GeneratorOptions( + apiname = 'gles1', + profile = 'common', + emitversions = None, + defaultExtensions = 'gles1')] + for opts in API_OPTIONS: + registry.apiGen(opts) + apigen.finish() + with open('../../libs/entries_gles1.in', 'w') as f: + apigen.writeEntries(f) + apigen = ApiGenerator() registry.setGenerator(apigen) API_OPTIONS = [ diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml index e422e96557..26771e19b5 100644 --- a/opengl/tools/glgen2/registry/egl.xml +++ b/opengl/tools/glgen2/registry/egl.xml @@ -801,7 +801,9 @@ <enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/> <enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/> <enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/> - <unused start="0x3364" end="0x339F"/> + <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/> + <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/> + <unused start="0x3366" end="0x339F"/> </enums> <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)"> @@ -887,6 +889,10 @@ <enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/> <enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/> </enums> + <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)"> + <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/> + <unused start="0x3481" end="0x348F"/> + </enums> <!-- Please remember that new enumerant allocations must be obtained by request to the Khronos API registrar (see comments at the top of this @@ -897,8 +903,8 @@ <!-- Reservable for future use. To generate a new range, allocate multiples of 16 starting at the lowest available point in this block. --> - <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> - <unused start="0x3480" end="0x3FFF"/> + <enums namespace="EGL" start="0x3490" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> + <unused start="0x3490" end="0x3FFF"/> </enums> <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL."> @@ -930,6 +936,12 @@ <param><ptype>EGLint</ptype> *<name>num_config</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLSync</ptype> <name>sync</name></param> + <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> + </command> + <command> <proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto> <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> <param><ptype>EGLSync</ptype> <name>sync</name></param> @@ -1647,6 +1659,11 @@ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param> + </command> + <command> <proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto> <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> <param><ptype>EGLSurface</ptype> <name>surface</name></param> @@ -1701,6 +1718,12 @@ <param><ptype>EGLSurface</ptype> <name>surface</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLSync</ptype> <name>sync</name></param> + <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> + </command> + <command> <proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto> </command> <command> @@ -2146,6 +2169,13 @@ </require> </extension> <extension name="EGL_EXT_client_extensions" supported="egl"/> + <extension name="EGL_EXT_client_sync" supported="egl"> + <require> + <enum name="EGL_SYNC_CLIENT_EXT"/> + <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/> + <command name="eglClientSignalSyncEXT"/> + </require> + </extension> <extension name="EGL_EXT_create_context_robustness" supported="egl"> <require> <enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/> @@ -2371,6 +2401,11 @@ <command name="eglSwapBuffersWithDamageEXT"/> </require> </extension> + <extension name="EGL_EXT_sync_reuse" supported="egl"> + <require> + <command name="eglUnsignalSyncEXT"/> + </require> + </extension> <extension name="EGL_EXT_yuv_surface" supported="egl"> <require> <enum name="EGL_YUV_ORDER_EXT"/> @@ -2932,6 +2967,11 @@ <enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/> </require> </extension> + <extension name="EGL_NV_stream_flush" supported="egl"> + <require> + <command name="eglStreamFlushNV"/> + </require> + </extension> <extension name="EGL_NV_stream_frame_limits" supported="egl"> <require> <enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/> diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp new file mode 100644 index 0000000000..a9af22fd83 --- /dev/null +++ b/services/bufferhub/Android.bp @@ -0,0 +1,61 @@ +// +// 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. +// + +cc_library_shared { + name: "libbufferhubservice", + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + srcs: [ + "BufferHubService.cpp", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libhidlbase", + "libhidltransport", + "libhwbinder", + "liblog", + "libutils", + ], + export_include_dirs: [ + "include" + ], +} + +cc_binary { + name: "android.frameworks.bufferhub@1.0-service", + relative_install_path: "hw", + srcs: [ + "main_bufferhub.cpp" + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libbufferhubservice", + "libhidltransport", + "libhwbinder", + "liblog", + "libutils", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + init_rc: ["android.frameworks.bufferhub@1.0-service.rc"], + vintf_fragments: ["android.frameworks.bufferhub@1.0-service.xml"], +} diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp new file mode 100644 index 0000000000..8be85a503a --- /dev/null +++ b/services/bufferhub/BufferHubService.cpp @@ -0,0 +1,41 @@ +/* + * 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 <bufferhub/BufferHubService.h> + +namespace android { +namespace frameworks { +namespace bufferhub { +namespace V1_0 { +namespace implementation { + +using ::android::status_t; +using ::android::hardware::Void; + +Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& /*description*/, + allocateBuffer_cb /*hidl_cb*/) { + return Void(); +} + +Return<sp<IBase>> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/) { + return nullptr; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace bufferhub +} // namespace frameworks +} // namespace android diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc new file mode 100644 index 0000000000..36fbedee15 --- /dev/null +++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc @@ -0,0 +1,6 @@ +service system_bufferhub /system/bin/hw/android.frameworks.bufferhub@1.0-service + class hal animation + user system + group system graphics + onrestart restart surfaceflinger + writepid /dev/cpuset/system-background/tasks diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml new file mode 100644 index 0000000000..bd958d3954 --- /dev/null +++ b/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="framework"> + <hal> + <name>android.frameworks.bufferhub</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IBufferHub</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h new file mode 100644 index 0000000000..b273e5b93c --- /dev/null +++ b/services/bufferhub/include/bufferhub/BufferHubService.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H +#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H + +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <android/hardware/graphics/common/1.2/types.h> + +namespace android { +namespace frameworks { +namespace bufferhub { +namespace V1_0 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::hidl_handle; +using ::android::hardware::Return; +using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription; +using ::android::hidl::base::V1_0::IBase; + +class BufferHubService : public IBufferHub { +public: + Return<void> allocateBuffer(const HardwareBufferDescription& /*description*/, + allocateBuffer_cb /*hidl_cb*/) override; + Return<sp<IBase>> importBuffer(const hidl_handle& /*nativeHandle*/) override; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace bufferhub +} // namespace frameworks +} // namespace android + +#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H diff --git a/services/bufferhub/main_bufferhub.cpp b/services/bufferhub/main_bufferhub.cpp new file mode 100644 index 0000000000..084460d993 --- /dev/null +++ b/services/bufferhub/main_bufferhub.cpp @@ -0,0 +1,37 @@ +/* + * 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 <bufferhub/BufferHubService.h> +#include <hidl/HidlTransportSupport.h> +#include <hwbinder/IPCThreadState.h> +#include <log/log.h> + +using android::sp; +using android::frameworks::bufferhub::V1_0::IBufferHub; +using android::frameworks::bufferhub::V1_0::implementation::BufferHubService; + +int main(int /*argc*/, char** /*argv*/) { + ALOGI("Bootstrap bufferhub HIDL service."); + + android::hardware::configureRpcThreadpool(/*numThreads=*/1, /*willJoin=*/true); + + sp<IBufferHub> service = new BufferHubService(); + LOG_ALWAYS_FATAL_IF(service->registerAsService() != android::OK, "Failed to register service"); + + android::hardware::joinRpcThreadpool(); + + return 0; +} diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 887119992c..622a623644 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -15,14 +15,14 @@ cc_library_shared { name: "libinputflinger", + cpp_std: "c++17", + srcs: [ "EventHub.cpp", - "InputApplication.cpp", "InputDispatcher.cpp", "InputListener.cpp", "InputManager.cpp", "InputReader.cpp", - "InputWindow.cpp", ], shared_libs: [ diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index 4d9a2a0254..a964d29489 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -76,16 +76,16 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } -static String8 sha1(const String8& in) { +static std::string sha1(const std::string& in) { SHA_CTX ctx; SHA1_Init(&ctx); - SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.string()), in.size()); + SHA1_Update(&ctx, reinterpret_cast<const u_char*>(in.c_str()), in.size()); u_char digest[SHA_DIGEST_LENGTH]; SHA1_Final(digest, &ctx); - String8 out; + std::string out; for (size_t i = 0; i < SHA_DIGEST_LENGTH; i++) { - out.appendFormat("%02x", digest[i]); + out += StringPrintf("%02x", digest[i]); } return out; } @@ -141,11 +141,11 @@ uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { // --- EventHub::Device --- -EventHub::Device::Device(int fd, int32_t id, const String8& path, +EventHub::Device::Device(int fd, int32_t id, const std::string& path, const InputDeviceIdentifier& identifier) : - next(NULL), + next(nullptr), fd(fd), id(id), path(path), identifier(identifier), - classes(0), configuration(NULL), virtualKeyMap(NULL), + classes(0), configuration(nullptr), virtualKeyMap(nullptr), ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0), timestampOverrideSec(0), timestampOverrideUsec(0), enabled(true), isVirtual(fd < 0) { @@ -172,9 +172,9 @@ void EventHub::Device::close() { } status_t EventHub::Device::enable() { - fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK); + fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); if(fd < 0) { - ALOGE("could not open %s, %s\n", path.string(), strerror(errno)); + ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno)); return -errno; } enabled = true; @@ -200,7 +200,7 @@ const int EventHub::EPOLL_MAX_EVENTS; EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), - mOpeningDevices(0), mClosingDevices(0), + mOpeningDevices(nullptr), mClosingDevices(nullptr), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { @@ -267,21 +267,21 @@ EventHub::~EventHub(void) { InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) return InputDeviceIdentifier(); + if (device == nullptr) return InputDeviceIdentifier(); return device->identifier; } uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) return 0; + if (device == nullptr) return 0; return device->classes; } int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) return 0; + if (device == nullptr) return 0; return device->controllerNumber; } @@ -307,7 +307,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.string(), device->fd, errno); + axis, device->identifier.name.c_str(), device->fd, errno); return -errno; } @@ -416,7 +416,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.string(), device->fd, errno); + axis, device->identifier.name.c_str(), device->fd, errno); return -errno; } @@ -465,7 +465,7 @@ status_t EventHub::mapKey(int32_t deviceId, if (device) { // Check the key character map first. sp<KeyCharacterMap> kcm = device->getKeyCharacterMap(); - if (kcm != NULL) { + if (kcm != nullptr) { if (!kcm->mapKey(scanCode, usageCode, outKeycode)) { *outFlags = 0; status = NO_ERROR; @@ -481,7 +481,7 @@ status_t EventHub::mapKey(int32_t deviceId, } if (status == NO_ERROR) { - if (kcm != NULL) { + if (kcm != nullptr) { kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState); } else { *outMetaState = metaState; @@ -512,7 +512,7 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis return NAME_NOT_FOUND; } -void EventHub::setExcludedDevices(const Vector<String8>& devices) { +void EventHub::setExcludedDevices(const std::vector<std::string>& devices) { AutoMutex _l(mLock); mExcludedDevices = devices; @@ -581,7 +581,7 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const { if (device) { return device->getKeyCharacterMap(); } - return NULL; + return nullptr; } bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, @@ -599,16 +599,16 @@ bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, return false; } -static String8 generateDescriptor(InputDeviceIdentifier& identifier) { - String8 rawDescriptor; - rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, +static std::string generateDescriptor(InputDeviceIdentifier& identifier) { + std::string rawDescriptor; + rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, identifier.product); // TODO add handling for USB devices to not uniqueify kbs that show up twice - if (!identifier.uniqueId.isEmpty()) { - rawDescriptor.append("uniqueId:"); - rawDescriptor.append(identifier.uniqueId); + if (!identifier.uniqueId.empty()) { + rawDescriptor += "uniqueId:"; + rawDescriptor += identifier.uniqueId; } else if (identifier.nonce != 0) { - rawDescriptor.appendFormat("nonce:%04x", identifier.nonce); + rawDescriptor += StringPrintf("nonce:%04x", identifier.nonce); } if (identifier.vendor == 0 && identifier.product == 0) { @@ -616,12 +616,12 @@ static String8 generateDescriptor(InputDeviceIdentifier& identifier) { // built-in so we need to rely on other information to uniquely identify // the input device. Usually we try to avoid relying on the device name or // location but for built-in input device, they are unlikely to ever change. - if (!identifier.name.isEmpty()) { - rawDescriptor.append("name:"); - rawDescriptor.append(identifier.name); - } else if (!identifier.location.isEmpty()) { - rawDescriptor.append("location:"); - rawDescriptor.append(identifier.location); + if (!identifier.name.empty()) { + rawDescriptor += "name:"; + rawDescriptor += identifier.name; + } else if (!identifier.location.empty()) { + rawDescriptor += "location:"; + rawDescriptor += identifier.location; } } identifier.descriptor = sha1(rawDescriptor); @@ -637,17 +637,17 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { // Ideally, we also want the descriptor to be short and relatively opaque. identifier.nonce = 0; - String8 rawDescriptor = generateDescriptor(identifier); - if (identifier.uniqueId.isEmpty()) { + std::string rawDescriptor = generateDescriptor(identifier); + if (identifier.uniqueId.empty()) { // If it didn't have a unique id check for conflicts and enforce // uniqueness if necessary. - while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) { + while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { identifier.nonce++; rawDescriptor = generateDescriptor(identifier); } } - ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(), - identifier.descriptor.string()); + ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), + identifier.descriptor.c_str()); } void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { @@ -664,7 +664,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { effect.replay.delay = 0; if (ioctl(device->fd, EVIOCSFF, &effect)) { ALOGW("Could not upload force feedback effect to device %s due to error %d.", - device->identifier.name.string(), errno); + device->identifier.name.c_str(), errno); return; } device->ffEffectId = effect.id; @@ -677,7 +677,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { ev.value = 1; if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { ALOGW("Could not start force feedback effect on device %s due to error %d.", - device->identifier.name.string(), errno); + device->identifier.name.c_str(), errno); return; } device->ffEffectPlaying = true; @@ -699,22 +699,22 @@ void EventHub::cancelVibrate(int32_t deviceId) { ev.value = 0; if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { ALOGW("Could not stop force feedback effect on device %s due to error %d.", - device->identifier.name.string(), errno); + device->identifier.name.c_str(), errno); return; } } } } -EventHub::Device* EventHub::getDeviceByDescriptorLocked(String8& descriptor) const { +EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { size_t size = mDevices.size(); for (size_t i = 0; i < size; i++) { Device* device = mDevices.valueAt(i); - if (descriptor.compare(device->identifier.descriptor) == 0) { + if (descriptor == device->identifier.descriptor) { return device; } } - return NULL; + return nullptr; } EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { @@ -732,7 +732,7 @@ EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const return device; } } - return NULL; + return nullptr; } size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { @@ -763,7 +763,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz while (mClosingDevices) { Device* device = mClosingDevices; ALOGV("Reporting device closed: id=%d, name=%s\n", - device->id, device->path.string()); + device->id, device->path.c_str()); mClosingDevices = device->next; event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; @@ -782,10 +782,10 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz mNeedToSendFinishedDeviceScan = true; } - while (mOpeningDevices != NULL) { + while (mOpeningDevices != nullptr) { Device* device = mOpeningDevices; ALOGV("Reporting device opened: id=%d, name=%s\n", - device->id, device->path.string()); + device->id, device->path.c_str()); mOpeningDevices = device->next; event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; @@ -867,7 +867,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz for (size_t i = 0; i < count; i++) { struct input_event& iev = readBuffer[i]; ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d", - device->path.string(), + device->path.c_str(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); @@ -936,7 +936,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz "event time %" PRId64 ", current time %" PRId64 ", call time %" PRId64 ". " "Using current time instead.", - device->path.string(), event->when, time, now); + device->path.c_str(), event->when, time, now); event->when = time; } else { ALOGV("Event time is ok but failed the fast path and required " @@ -962,12 +962,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } } else if (eventItem.events & EPOLLHUP) { ALOGI("Removing device %s due to epoll hang-up event.", - device->identifier.name.string()); + device->identifier.name.c_str()); deviceChanged = true; closeDeviceLocked(device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", - eventItem.events, device->identifier.name.string()); + eventItem.events, device->identifier.name.c_str()); } } @@ -1099,7 +1099,7 @@ status_t EventHub::registerDeviceForEpollLocked(Device* device) { status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) { if (device->hasValidFd()) { - if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { + if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, nullptr)) { ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); return -errno; } @@ -1125,14 +1125,14 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; - identifier.name.setTo(buffer); + identifier.name = buffer; } // Check to see if the device is on our excluded list for (size_t i = 0; i < mExcludedDevices.size(); i++) { - const String8& item = mExcludedDevices.itemAt(i); + const std::string& item = mExcludedDevices[i]; if (identifier.name == item) { - ALOGI("ignoring event id %s driver %s\n", devicePath, item.string()); + ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str()); close(fd); return -1; } @@ -1163,7 +1163,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; - identifier.location.setTo(buffer); + identifier.location = buffer; } // Get device unique id. @@ -1171,7 +1171,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; - identifier.uniqueId.setTo(buffer); + identifier.uniqueId = buffer; } // Fill in the descriptor. @@ -1179,7 +1179,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; - Device* device = new Device(fd, deviceId, String8(devicePath), identifier); + Device* device = new Device(fd, deviceId, devicePath, identifier); ALOGV("add device %d: %s\n", deviceId, devicePath); ALOGV(" bus: %04x\n" @@ -1187,10 +1187,10 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { " product %04x\n" " version %04x\n", identifier.bus, identifier.vendor, identifier.product, identifier.version); - ALOGV(" name: \"%s\"\n", identifier.name.string()); - ALOGV(" location: \"%s\"\n", identifier.location.string()); - ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string()); - ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string()); + ALOGV(" name: \"%s\"\n", identifier.name.c_str()); + ALOGV(" location: \"%s\"\n", identifier.location.c_str()); + ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str()); + ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str()); ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); @@ -1343,7 +1343,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { ALOGV("Dropping device: id=%d, path='%s', name='%s'", - deviceId, devicePath, device->identifier.name.string()); + deviceId, devicePath, device->identifier.name.c_str()); delete device; return -1; } @@ -1374,11 +1374,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", - deviceId, fd, devicePath, device->identifier.name.string(), + deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes, - device->configurationFile.string(), - device->keyMap.keyLayoutFile.string(), - device->keyMap.keyCharacterMapFile.string(), + device->configurationFile.c_str(), + device->keyMap.keyLayoutFile.c_str(), + device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId)); addDeviceLocked(device); @@ -1392,11 +1392,11 @@ void EventHub::configureFd(Device* device) { unsigned int repeatRate[] = {0, 0}; if (ioctl(device->fd, EVIOCSREP, repeatRate)) { ALOGW("Unable to disable kernel key repeat for %s: %s", - device->path.string(), strerror(errno)); + device->path.c_str(), strerror(errno)); } } - String8 wakeMechanism("EPOLLWAKEUP"); + std::string wakeMechanism = "EPOLLWAKEUP"; if (!mUsingEpollWakeup) { #ifndef EVIOCSSUSPENDBLOCK // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels @@ -1416,14 +1416,14 @@ void EventHub::configureFd(Device* device) { // clock. int clockId = CLOCK_MONOTONIC; bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.string(), + ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), toString(usingClockIoctl)); } bool EventHub::isDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); return false; } @@ -1433,7 +1433,7 @@ bool EventHub::isDeviceEnabled(int32_t deviceId) { status_t EventHub::enableDevice(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); return BAD_VALUE; } @@ -1455,7 +1455,7 @@ status_t EventHub::enableDevice(int32_t deviceId) { status_t EventHub::disableDevice(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); return BAD_VALUE; } @@ -1473,7 +1473,7 @@ void EventHub::createVirtualKeyboardLocked() { identifier.uniqueId = "<virtual>"; assignDescriptorLocked(identifier); - Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier); + Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, "<virtual>", identifier); device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | INPUT_DEVICE_CLASS_DPAD @@ -1491,26 +1491,26 @@ void EventHub::addDeviceLocked(Device* device) { void EventHub::loadConfigurationLocked(Device* device) { device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); - if (device->configurationFile.isEmpty()) { + if (device->configurationFile.empty()) { ALOGD("No input device configuration file found for device '%s'.", - device->identifier.name.string()); + device->identifier.name.c_str()); } else { - status_t status = PropertyMap::load(device->configurationFile, + status_t status = PropertyMap::load(String8(device->configurationFile.c_str()), &device->configuration); if (status) { ALOGE("Error loading input device configuration file for device '%s'. " "Using default configuration.", - device->identifier.name.string()); + device->identifier.name.c_str()); } } } status_t EventHub::loadVirtualKeyMapLocked(Device* device) { // The virtual key map is supplied by the kernel as a system board property file. - String8 path; - path.append("/sys/board_properties/virtualkeys."); - path.append(device->identifier.name); - if (access(path.string(), R_OK)) { + std::string path; + path += "/sys/board_properties/virtualkeys."; + path += device->identifier.name; + if (access(path.c_str(), R_OK)) { return NAME_NOT_FOUND; } return VirtualKeyMap::load(path, &device->virtualKeyMap); @@ -1543,7 +1543,7 @@ bool EventHub::deviceHasMicLocked(Device* device) { int32_t EventHub::getNextControllerNumberLocked(Device* device) { if (mControllerNumbers.isFull()) { ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s", - device->identifier.name.string()); + device->identifier.name.c_str()); return 0; } // Since the controller number 0 is reserved for non-controllers, translate all numbers up by @@ -1617,12 +1617,12 @@ void EventHub::closeAllDevicesLocked() { void EventHub::closeDeviceLocked(Device* device) { ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", - device->path.string(), device->identifier.name.string(), device->id, + device->path.c_str(), device->identifier.name.c_str(), device->id, device->fd, device->classes); if (device->id == mBuiltInKeyboardId) { ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.string(), mBuiltInKeyboardId); + device->path.c_str(), mBuiltInKeyboardId); mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; } @@ -1634,9 +1634,9 @@ void EventHub::closeDeviceLocked(Device* device) { device->close(); // Unlink for opening devices list if it is present. - Device* pred = NULL; + Device* pred = nullptr; bool found = false; - for (Device* entry = mOpeningDevices; entry != NULL; ) { + for (Device* entry = mOpeningDevices; entry != nullptr; ) { if (entry == device) { found = true; break; @@ -1648,7 +1648,7 @@ void EventHub::closeDeviceLocked(Device* device) { // Unlink the device from the opening devices list then delete it. // We don't need to tell the client that the device was closed because // it does not even know it was opened in the first place. - ALOGI("Device %s was immediately closed after opening.", device->path.string()); + ALOGI("Device %s was immediately closed after opening.", device->path.c_str()); if (pred) { pred->next = device->next; } else { @@ -1712,7 +1712,7 @@ status_t EventHub::scanDirLocked(const char *dirname) DIR *dir; struct dirent *de; dir = opendir(dirname); - if(dir == NULL) + if(dir == nullptr) return -1; strcpy(devname, dirname); filename = devname + strlen(devname); @@ -1750,30 +1750,30 @@ void EventHub::dump(std::string& dump) { const Device* device = mDevices.valueAt(i); if (mBuiltInKeyboardId == device->id) { dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", - device->id, device->identifier.name.string()); + device->id, device->identifier.name.c_str()); } else { dump += StringPrintf(INDENT2 "%d: %s\n", device->id, - device->identifier.name.string()); + device->identifier.name.c_str()); } dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes); - dump += StringPrintf(INDENT3 "Path: %s\n", device->path.string()); + dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str()); dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled)); - dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string()); - dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.string()); + dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str()); + dump += StringPrintf(INDENT3 "Location: %s\n", device->identifier.location.c_str()); dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber); - dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string()); + dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str()); dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " "product=0x%04x, version=0x%04x\n", device->identifier.bus, device->identifier.vendor, device->identifier.product, device->identifier.version); dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n", - device->keyMap.keyLayoutFile.string()); + device->keyMap.keyLayoutFile.c_str()); dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", - device->keyMap.keyCharacterMapFile.string()); + device->keyMap.keyCharacterMapFile.c_str()); dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", - device->configurationFile.string()); + device->configurationFile.c_str()); dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", - toString(device->overlayKeyMap != NULL)); + toString(device->overlayKeyMap != nullptr)); } } // release lock } diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h index 66bc29456b..ea663b7cb6 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/EventHub.h @@ -18,6 +18,8 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H +#include <vector> + #include <input/Input.h> #include <input/InputDevice.h> #include <input/Keyboard.h> @@ -29,7 +31,6 @@ #include <utils/List.h> #include <utils/Errors.h> #include <utils/PropertyMap.h> -#include <utils/Vector.h> #include <utils/KeyedVector.h> #include <utils/BitSet.h> @@ -207,7 +208,7 @@ public: // Sets devices that are excluded from opening. // This can be used to ignore input devices for sensors. - virtual void setExcludedDevices(const Vector<String8>& devices) = 0; + virtual void setExcludedDevices(const std::vector<std::string>& devices) = 0; /* * Wait for events to become available and returns them. @@ -303,7 +304,7 @@ public: virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; - virtual void setExcludedDevices(const Vector<String8>& devices); + virtual void setExcludedDevices(const std::vector<std::string>& devices); virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; @@ -344,7 +345,7 @@ private: int fd; // may be -1 if device is closed const int32_t id; - const String8 path; + const std::string path; const InputDeviceIdentifier identifier; uint32_t classes; @@ -357,7 +358,7 @@ private: uint8_t ffBitmask[(FF_MAX + 1) / 8]; uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; - String8 configurationFile; + std::string configurationFile; PropertyMap* configuration; VirtualKeyMap* virtualKeyMap; KeyMap keyMap; @@ -373,7 +374,8 @@ private: int32_t timestampOverrideSec; int32_t timestampOverrideUsec; - Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier); + Device(int fd, int32_t id, const std::string& path, + const InputDeviceIdentifier& identifier); ~Device(); void close(); @@ -385,7 +387,7 @@ private: const bool isVirtual; // set if fd < 0 is passed to constructor const sp<KeyCharacterMap>& getKeyCharacterMap() const { - if (combinedKeyMap != NULL) { + if (combinedKeyMap != nullptr) { return combinedKeyMap; } return keyMap.keyCharacterMap; @@ -413,7 +415,7 @@ private: void scanDevicesLocked(); status_t readNotifyLocked(); - Device* getDeviceByDescriptorLocked(String8& descriptor) const; + Device* getDeviceByDescriptorLocked(const std::string& descriptor) const; Device* getDeviceLocked(int32_t deviceId) const; Device* getDeviceByPathLocked(const char* devicePath) const; @@ -457,7 +459,7 @@ private: bool mNeedToSendFinishedDeviceScan; bool mNeedToReopenDevices; bool mNeedToScanDevices; - Vector<String8> mExcludedDevices; + std::vector<std::string> mExcludedDevices; int mEpollFd; int mINotifyFd; diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 9a449fa6d1..2e984d9ca9 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -235,19 +235,26 @@ static void dumpRegion(std::string& dump, const Region& region) { } } +template<typename T, typename U> +static T getValueByKey(std::unordered_map<U, T>& map, U key) { + typename std::unordered_map<U, T>::const_iterator it = map.find(key); + return it != map.end() ? it->second : T{}; +} + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy), - mPendingEvent(NULL), mLastDropReason(DROP_REASON_NOT_DROPPED), + mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), - mNextUnblockedEvent(NULL), + mNextUnblockedEvent(nullptr), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), + mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); - mKeyRepeatState.lastKeyEntry = NULL; + mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); } @@ -360,7 +367,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Now we have an event to dispatch. // All events are eventually dequeued and processed this way, even if we intend to drop them. - ALOG_ASSERT(mPendingEvent != NULL); + ALOG_ASSERT(mPendingEvent != nullptr); bool done = false; DropReason dropReason = DROP_REASON_NOT_DROPPED; if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { @@ -370,7 +377,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } if (mNextUnblockedEvent == mPendingEvent) { - mNextUnblockedEvent = NULL; + mNextUnblockedEvent = nullptr; } switch (mPendingEvent->type) { @@ -481,14 +488,14 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY - && mInputTargetWaitApplicationHandle != NULL) { + && mInputTargetWaitApplicationHandle != nullptr) { int32_t displayId = motionEntry->displayId; int32_t x = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_Y)); sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); - if (touchedWindowHandle != NULL + if (touchedWindowHandle != nullptr && touchedWindowHandle->inputApplicationHandle != mInputTargetWaitApplicationHandle) { // User touched a different application than the one we are waiting on. @@ -515,9 +522,10 @@ void InputDispatcher::addRecentEventLocked(EventEntry* entry) { sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y) { // Traverse windows from front to back to find touched window. - size_t numWindows = mWindowHandles.size(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { int32_t flags = windowInfo->layoutParamsFlags; @@ -534,7 +542,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } } } - return NULL; + return nullptr; } void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { @@ -663,7 +671,7 @@ void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { resetANRTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); - mPendingEvent = NULL; + mPendingEvent = nullptr; } } @@ -676,7 +684,7 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); } if (entry == mNextUnblockedEvent) { - mNextUnblockedEvent = NULL; + mNextUnblockedEvent = nullptr; } addRecentEventLocked(entry); entry->release(); @@ -685,7 +693,7 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { void InputDispatcher::resetKeyRepeatLocked() { if (mKeyRepeatState.lastKeyEntry) { mKeyRepeatState.lastKeyEntry->release(); - mKeyRepeatState.lastKeyEntry = NULL; + mKeyRepeatState.lastKeyEntry = nullptr; } } @@ -702,7 +710,7 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cu entry->repeatCount += 1; } else { KeyEntry* newEntry = new KeyEntry(currentTime, - entry->deviceId, entry->source, policyFlags, + entry->deviceId, entry->source, entry->displayId, policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount + 1, entry->downTime); @@ -807,8 +815,10 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - if (mFocusedWindowHandle != NULL) { - commandEntry->inputWindowHandle = mFocusedWindowHandle; + sp<InputWindowHandle> focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); + if (focusedWindowHandle != nullptr) { + commandEntry->inputWindowHandle = focusedWindowHandle; } commandEntry->keyEntry = entry; entry->refCount += 1; @@ -842,7 +852,8 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, return true; } - addMonitoringTargetsLocked(inputTargets); + // Add monitor channels from event's or focused display. + addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); @@ -851,11 +862,11 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, " - "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " - "repeatCount=%d, downTime=%" PRId64, + 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->policyFlags, + 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 @@ -909,7 +920,8 @@ bool InputDispatcher::dispatchMotionLocked( return true; } - addMonitoringTargetsLocked(inputTargets); + // Add monitor channels from event's or focused display. + addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // Dispatch the motion. if (conflictingPointerActions) { @@ -924,12 +936,13 @@ bool InputDispatcher::dispatchMotionLocked( void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, " + 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->policyFlags, + 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, @@ -987,7 +1000,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { - if (applicationHandle == NULL && windowHandle == NULL) { + if (applicationHandle == nullptr && windowHandle == nullptr) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { #if DEBUG_FOCUS ALOGD("Waiting for system to become ready for input. Reason: %s", reason); @@ -1006,9 +1019,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, reason); #endif nsecs_t timeout; - if (windowHandle != NULL) { + if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); - } else if (applicationHandle != NULL) { + } else if (applicationHandle != nullptr) { timeout = applicationHandle->getDispatchingTimeout( DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else { @@ -1021,10 +1034,10 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationHandle.clear(); - if (windowHandle != NULL) { + if (windowHandle != nullptr) { mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle; } - if (mInputTargetWaitApplicationHandle == NULL && applicationHandle != NULL) { + if (mInputTargetWaitApplicationHandle == nullptr && applicationHandle != nullptr) { mInputTargetWaitApplicationHandle = applicationHandle; } } @@ -1067,7 +1080,7 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); sp<InputWindowHandle> windowHandle = connection->inputWindowHandle; - if (windowHandle != NULL) { + if (windowHandle != nullptr) { const InputWindowInfo* info = windowHandle->getInfo(); if (info) { ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(info->displayId); @@ -1106,17 +1119,49 @@ void InputDispatcher::resetANRTimeoutsLocked() { mInputTargetWaitApplicationHandle.clear(); } +/** + * Get the display id that the given event should go to. If this event specifies a valid display id, + * then it should be dispatched to that display. Otherwise, the event goes to the focused display. + * Focused display is the display that the user most recently interacted with. + */ +int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { + int32_t displayId; + switch (entry->type) { + case EventEntry::TYPE_KEY: { + const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry); + displayId = typedEntry->displayId; + break; + } + case EventEntry::TYPE_MOTION: { + const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry); + displayId = typedEntry->displayId; + break; + } + default: { + ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); + return ADISPLAY_ID_NONE; + } + } + return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; +} + int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) { int32_t injectionResult; std::string reason; + int32_t displayId = getTargetDisplayId(entry); + sp<InputWindowHandle> focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + sp<InputApplicationHandle> focusedApplicationHandle = + getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + // If there is no currently focused window and no focused application // then drop the event. - if (mFocusedWindowHandle == NULL) { - if (mFocusedApplicationHandle != NULL) { + if (focusedWindowHandle == nullptr) { + if (focusedApplicationHandle != nullptr) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, NULL, nextWakeupTime, + focusedApplicationHandle, nullptr, nextWakeupTime, "Waiting because no window has focus but there is a " "focused application that may eventually add a window " "when it finishes starting up."); @@ -1129,23 +1174,23 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, } // Check permissions. - if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) { + if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; goto Failed; } // Check whether the window is ready for more input. reason = checkWindowReadyForMoreInputLocked(currentTime, - mFocusedWindowHandle, entry, "focused"); + focusedWindowHandle, entry, "focused"); if (!reason.empty()) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.c_str()); + focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str()); goto Unresponsive; } // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(mFocusedWindowHandle, + addWindowTargetLocked(focusedWindowHandle, InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), inputTargets); @@ -1186,7 +1231,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Copy current touch state into mTempTouchState. // This state is always reset at the end of this function, so if we don't find state // for the specified display then our initial state will be empty. - const TouchState* oldState = NULL; + const TouchState* oldState = nullptr; ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId); if (oldStateIndex >= 0) { oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex); @@ -1246,9 +1291,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool isTouchModal = false; // Traverse windows from front to back to find touched window and outside targets. - size_t numWindows = mWindowHandles.size(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId != displayId) { continue; // wrong display @@ -1274,21 +1320,21 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Figure out whether splitting will be allowed for this window. - if (newTouchedWindowHandle != NULL + if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { // New window supports splitting. isSplit = true; } else if (isSplit) { // New window does not support splitting but we have already split events. // Ignore the new window. - newTouchedWindowHandle = NULL; + newTouchedWindowHandle = nullptr; } // Handle the case where we did not find a window. - if (newTouchedWindowHandle == NULL) { + if (newTouchedWindowHandle == nullptr) { // Try to assign the pointer to the first foreground window we find, if there is one. newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); - if (newTouchedWindowHandle == NULL) { + if (newTouchedWindowHandle == nullptr) { ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; @@ -1345,7 +1391,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); if (oldTouchedWindowHandle != newTouchedWindowHandle - && newTouchedWindowHandle != NULL) { + && newTouchedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Touch is slipping out of window %s into window %s.", oldTouchedWindowHandle->getName().c_str(), @@ -1380,7 +1426,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newHoverWindowHandle != mLastHoverWindowHandle) { // Let the previous window know that the hover sequence is over. - if (mLastHoverWindowHandle != NULL) { + if (mLastHoverWindowHandle != nullptr) { #if DEBUG_HOVER ALOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->getName().c_str()); @@ -1390,7 +1436,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Let the new window know that the hover sequence is starting. - if (newHoverWindowHandle != NULL) { + if (newHoverWindowHandle != nullptr) { #if DEBUG_HOVER ALOGD("Sending hover enter event to window %s.", newHoverWindowHandle->getName().c_str()); @@ -1455,7 +1501,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, touchedWindow.windowHandle, entry, "touched"); if (!reason.empty()) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); + nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); goto Unresponsive; } } @@ -1471,8 +1517,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> foregroundWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle->getInfo()->hasWallpaper) { - for (size_t i = 0; i < mWindowHandles.size(); i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && windowHandle->getInfo()->layoutParamsType @@ -1503,7 +1551,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, Failed: // Check injection permission once and for all. if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { - if (checkInjectionPermission(NULL, entry->injectionState)) { + if (checkInjectionPermission(nullptr, entry->injectionState)) { injectionPermission = INJECTION_PERMISSION_GRANTED; } else { injectionPermission = INJECTION_PERMISSION_DENIED; @@ -1619,27 +1667,39 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH target.pointerIds = pointerIds; } -void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - inputTargets.push(); +void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, + int32_t displayId) { + std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it = + mMonitoringChannelsByDisplay.find(displayId); + + if (it != mMonitoringChannelsByDisplay.end()) { + const Vector<sp<InputChannel>>& monitoringChannels = it->second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + inputTargets.push(); - InputTarget& target = inputTargets.editTop(); - target.inputChannel = mMonitoringChannels[i]; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.xOffset = 0; - target.yOffset = 0; - target.pointerIds.clear(); - target.scaleFactor = 1.0f; + InputTarget& target = inputTargets.editTop(); + target.inputChannel = monitoringChannels[i]; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.xOffset = 0; + target.yOffset = 0; + target.pointerIds.clear(); + target.scaleFactor = 1.0f; + } + } else { + // If there is no monitor channel registered or all monitor channel unregistered, + // the display can't detect the extra system gesture by a copy of input events. + ALOGW("There is no monitor channel found in display=%" PRId32, displayId); } } bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState) { if (injectionState - && (windowHandle == NULL + && (windowHandle == nullptr || windowHandle->getInfo()->ownerUid != injectionState->injectorUid) && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (windowHandle != NULL) { + if (windowHandle != nullptr) { ALOGW("Permission denied: injecting event from pid %d uid %d to window %s " "owned by uid %d", injectionState->injectorPid, injectionState->injectorUid, @@ -1657,9 +1717,10 @@ bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& wind bool InputDispatcher::isWindowObscuredAtPointLocked( const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { int32_t displayId = windowHandle->getInfo()->displayId; - size_t numWindows = mWindowHandles.size(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i); if (otherHandle == windowHandle) { break; } @@ -1677,10 +1738,11 @@ bool InputDispatcher::isWindowObscuredAtPointLocked( bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const { int32_t displayId = windowHandle->getInfo()->displayId; + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - size_t numWindows = mWindowHandles.size(); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i); if (otherHandle == windowHandle) { break; } @@ -1778,8 +1840,8 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT std::string InputDispatcher::getApplicationWindowLabelLocked( const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle) { - if (applicationHandle != NULL) { - if (windowHandle != NULL) { + if (applicationHandle != nullptr) { + if (windowHandle != nullptr) { std::string label(applicationHandle->getName()); label += " - "; label += windowHandle->getName(); @@ -1787,7 +1849,7 @@ std::string InputDispatcher::getApplicationWindowLabelLocked( } else { return applicationHandle->getName(); } - } else if (windowHandle != NULL) { + } else if (windowHandle != nullptr) { return windowHandle->getName(); } else { return "<unknown application or window>"; @@ -1795,8 +1857,11 @@ std::string InputDispatcher::getApplicationWindowLabelLocked( } void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - if (mFocusedWindowHandle != NULL) { - const InputWindowInfo* info = mFocusedWindowHandle->getInfo(); + int32_t displayId = getTargetDisplayId(eventEntry); + sp<InputWindowHandle> focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (focusedWindowHandle != nullptr) { + const InputWindowInfo* info = focusedWindowHandle->getInfo(); if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { #if DEBUG_DISPATCH_CYCLE ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); @@ -2017,7 +2082,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Publish the key event. status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, - keyEntry->deviceId, keyEntry->source, + keyEntry->deviceId, keyEntry->source, keyEntry->displayId, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode, keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, @@ -2243,8 +2308,12 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options); + for (auto& it : mMonitoringChannelsByDisplay) { + const Vector<sp<InputChannel>>& monitoringChannels = it.second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options); + } } } @@ -2291,7 +2360,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget target; sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel); - if (windowHandle != NULL) { + if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; target.yOffset = -windowInfo->frameTop; @@ -2349,7 +2418,7 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet "we expected there to be %d pointers. This probably means we received " "a broken sequence of pointer ids from the input device.", splitPointerCount, pointerIds.count()); - return NULL; + return nullptr; } int32_t action = originalMotionEntry->action; @@ -2384,6 +2453,7 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet originalMotionEntry->eventTime, originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, action, originalMotionEntry->actionButton, @@ -2394,7 +2464,6 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, originalMotionEntry->downTime, - originalMotionEntry->displayId, splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { @@ -2423,12 +2492,49 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange } } +/** + * If one of the meta shortcuts is detected, process them here: + * Meta + Backspace -> generate BACK + * Meta + Enter -> generate HOME + * This will potentially overwrite keyCode and metaState. + */ +void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, + int32_t& keyCode, int32_t& metaState) { + if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) { + int32_t newKeyCode = AKEYCODE_UNKNOWN; + if (keyCode == AKEYCODE_DEL) { + newKeyCode = AKEYCODE_BACK; + } else if (keyCode == AKEYCODE_ENTER) { + newKeyCode = AKEYCODE_HOME; + } + if (newKeyCode != AKEYCODE_UNKNOWN) { + AutoMutex _l(mLock); + struct KeyReplacement replacement = {keyCode, deviceId}; + mReplacedKeys.add(replacement, newKeyCode); + keyCode = newKeyCode; + metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } + } else if (action == AKEY_EVENT_ACTION_UP) { + // In order to maintain a consistent stream of up and down events, check to see if the key + // going up is one we've replaced in a down event and haven't yet replaced in an up event, + // even if the modifier was released between the down and the up events. + AutoMutex _l(mLock); + struct KeyReplacement replacement = {keyCode, deviceId}; + ssize_t index = mReplacedKeys.indexOfKey(replacement); + if (index >= 0) { + keyCode = mReplacedKeys.valueAt(index); + mReplacedKeys.removeItemsAt(index); + metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } + } +} + void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyKey - eventTime=%" PRId64 - ", deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, " + ", 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->policyFlags, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->flags, args->keyCode, args->scanCode, args->metaState, args->downTime); #endif @@ -2439,6 +2545,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { uint32_t policyFlags = args->policyFlags; int32_t flags = args->flags; int32_t metaState = args->metaState; + // InputDispatcher tracks and generates key repeats on behalf of + // whatever notifies it, so repeatCount should always be set to 0 + constexpr int32_t repeatCount = 0; if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) { policyFlags |= POLICY_FLAG_VIRTUAL; flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; @@ -2450,37 +2559,11 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { policyFlags |= POLICY_FLAG_TRUSTED; int32_t keyCode = args->keyCode; - if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) { - int32_t newKeyCode = AKEYCODE_UNKNOWN; - if (keyCode == AKEYCODE_DEL) { - newKeyCode = AKEYCODE_BACK; - } else if (keyCode == AKEYCODE_ENTER) { - newKeyCode = AKEYCODE_HOME; - } - if (newKeyCode != AKEYCODE_UNKNOWN) { - AutoMutex _l(mLock); - struct KeyReplacement replacement = {keyCode, args->deviceId}; - mReplacedKeys.add(replacement, newKeyCode); - keyCode = newKeyCode; - metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } - } else if (args->action == AKEY_EVENT_ACTION_UP) { - // In order to maintain a consistent stream of up and down events, check to see if the key - // going up is one we've replaced in a down event and haven't yet replaced in an up event, - // even if the modifier was released between the down and the up events. - AutoMutex _l(mLock); - struct KeyReplacement replacement = {keyCode, args->deviceId}; - ssize_t index = mReplacedKeys.indexOfKey(replacement); - if (index >= 0) { - keyCode = mReplacedKeys.valueAt(index); - mReplacedKeys.removeItemsAt(index); - metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); - } - } + accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->action, - flags, keyCode, args->scanCode, metaState, 0, + event.initialize(args->deviceId, args->source, args->displayId, args->action, + flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); android::base::Timer t; @@ -2505,9 +2588,8 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { mLock.lock(); } - int32_t repeatCount = 0; KeyEntry* newEntry = new KeyEntry(args->eventTime, - args->deviceId, args->source, policyFlags, + args->deviceId, args->source, args->displayId, policyFlags, args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime); @@ -2526,10 +2608,11 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, policyFlags=0x%x, " + ALOGD("notifyMotion - 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, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->policyFlags, + 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->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { @@ -2573,7 +2656,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->action, args->actionButton, + event.initialize(args->deviceId, args->source, args->displayId, + args->action, args->actionButton, args->flags, args->edgeFlags, args->metaState, args->buttonState, 0, 0, args->xPrecision, args->yPrecision, args->downTime, args->eventTime, @@ -2589,11 +2673,10 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { // Just enqueue a new motion event. MotionEntry* newEntry = new MotionEntry(args->eventTime, - args->deviceId, args->source, policyFlags, + args->deviceId, args->source, args->displayId, policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, - args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); @@ -2642,14 +2725,13 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } } -int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displayId, +int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x, displayId=%d", - event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags, - displayId); + "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", + event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); #endif nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); @@ -2663,20 +2745,29 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ EventEntry* lastInjectedEntry; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { - const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); - int32_t action = keyEvent->getAction(); + KeyEvent keyEvent; + keyEvent.initialize(*static_cast<const KeyEvent*>(event)); + int32_t action = keyEvent.getAction(); if (! validateKeyEvent(action)) { return INPUT_EVENT_INJECTION_FAILED; } - int32_t flags = keyEvent->getFlags(); + int32_t flags = keyEvent.getFlags(); + int32_t keyCode = keyEvent.getKeyCode(); + int32_t metaState = keyEvent.getMetaState(); + accelerateMetaShortcuts(keyEvent.getDeviceId(), action, + /*byref*/ keyCode, /*byref*/ metaState); + keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), + action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), + keyEvent.getDownTime(), keyEvent.getEventTime()); + if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { policyFlags |= POLICY_FLAG_VIRTUAL; } if (!(policyFlags & POLICY_FLAG_FILTERED)) { android::base::Timer t; - mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags); + mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -2684,11 +2775,11 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ } mLock.lock(); - firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(), - keyEvent->getDeviceId(), keyEvent->getSource(), + firstInjectedEntry = new KeyEntry(keyEvent.getEventTime(), + keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), policyFlags, action, flags, - keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), - keyEvent->getRepeatCount(), keyEvent->getDownTime()); + keyEvent.getKeyCode(), keyEvent.getScanCode(), keyEvent.getMetaState(), + keyEvent.getRepeatCount(), keyEvent.getDownTime()); lastInjectedEntry = firstInjectedEntry; break; } @@ -2717,12 +2808,13 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); firstInjectedEntry = new MotionEntry(*sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, + motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), + policyFlags, action, actionButton, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), displayId, + motionEvent->getDownTime(), uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry = firstInjectedEntry; @@ -2730,12 +2822,13 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ sampleEventTimes += 1; samplePointerCoords += pointerCount; MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), displayId, + motionEvent->getDownTime(), uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry->next = nextInjectedEntry; @@ -2758,7 +2851,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ lastInjectedEntry->injectionState = injectionState; bool needWake = false; - for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) { + for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) { EventEntry* nextEntry = entry->next; needWake |= enqueueInboundEventLocked(entry); entry = nextEntry; @@ -2886,84 +2979,136 @@ void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* ent } } +Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const { + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it = + mWindowHandlesByDisplay.find(displayId); + if(it != mWindowHandlesByDisplay.end()) { + return it->second; + } + + // Return an empty one if nothing found. + return Vector<sp<InputWindowHandle>>(); +} + sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( const sp<InputChannel>& inputChannel) const { - size_t numWindows = mWindowHandles.size(); - for (size_t i = 0; i < numWindows; i++) { - const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); - if (windowHandle->getInputChannel() == inputChannel) { - return windowHandle; + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + if (windowHandle->getInputChannel() == inputChannel) { + return windowHandle; + } } } - return NULL; + return nullptr; } bool InputDispatcher::hasWindowHandleLocked( const sp<InputWindowHandle>& windowHandle) const { - size_t numWindows = mWindowHandles.size(); - for (size_t i = 0; i < numWindows; i++) { - if (mWindowHandles.itemAt(i) == windowHandle) { - return true; + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + if (windowHandles.itemAt(i) == windowHandle) { + if (windowHandle->getInfo()->displayId != it.first) { + ALOGE("Found window %s in display %d, but it should belong to display %d", + windowHandle->getName().c_str(), it.first, + windowHandle->getInfo()->displayId); + } + return true; + } } } return false; } -void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { +/** + * Called from InputManagerService, update window handle list by displayId that can receive input. + * A window handle contains information about InputChannel, Touch Region, Types, Focused,... + * If set an empty list, remove all handles from the specific display. + * For focused handle, check if need to change and send a cancel event to previous one. + * For removed handle, check if need to send a cancel event if already in touch. + */ +void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& inputWindowHandles, + int32_t displayId) { #if DEBUG_FOCUS ALOGD("setInputWindows"); #endif { // acquire lock AutoMutex _l(mLock); - Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles; - mWindowHandles = inputWindowHandles; + // Copy old handles for release if they are no longer present. + const Vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); - sp<InputWindowHandle> newFocusedWindowHandle; + sp<InputWindowHandle> newFocusedWindowHandle = nullptr; bool foundHoveredWindow = false; - for (size_t i = 0; i < mWindowHandles.size(); i++) { - const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); - if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) { - mWindowHandles.removeAt(i--); - continue; - } - if (windowHandle->getInfo()->hasFocus) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; + + if (inputWindowHandles.isEmpty()) { + // Remove all handles on a display if there are no windows left. + mWindowHandlesByDisplay.erase(displayId); + } else { + size_t numWindows = inputWindowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = inputWindowHandles.itemAt(i); + if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == nullptr) { + continue; + } + + if (windowHandle->getInfo()->displayId != displayId) { + ALOGE("Window %s updated by wrong display %d, should belong to display %d", + windowHandle->getName().c_str(), displayId, + windowHandle->getInfo()->displayId); + continue; + } + + if (windowHandle->getInfo()->hasFocus) { + newFocusedWindowHandle = windowHandle; + } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; + } } + + // Insert or replace + mWindowHandlesByDisplay[displayId] = inputWindowHandles; } if (!foundHoveredWindow) { - mLastHoverWindowHandle = NULL; + mLastHoverWindowHandle = nullptr; } - if (mFocusedWindowHandle != newFocusedWindowHandle) { - if (mFocusedWindowHandle != NULL) { + sp<InputWindowHandle> oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + + if (oldFocusedWindowHandle != newFocusedWindowHandle) { + if (oldFocusedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Focus left window: %s", - mFocusedWindowHandle->getName().c_str()); + oldFocusedWindowHandle->getName().c_str()); #endif - sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel(); - if (focusedInputChannel != NULL) { + sp<InputChannel> focusedInputChannel = oldFocusedWindowHandle->getInputChannel(); + if (focusedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked( focusedInputChannel, options); } + mFocusedWindowHandlesByDisplay.erase(displayId); } - if (newFocusedWindowHandle != NULL) { + if (newFocusedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Focus entered window: %s", newFocusedWindowHandle->getName().c_str()); #endif + mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; } - mFocusedWindowHandle = newFocusedWindowHandle; } - for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) { - TouchState& state = mTouchStatesByDisplay.editValueAt(d); + ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); + if (stateIndex >= 0) { + TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); for (size_t i = 0; i < state.windows.size(); ) { TouchedWindow& touchedWindow = state.windows.editItemAt(i); if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { @@ -2973,7 +3118,7 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inpu #endif sp<InputChannel> touchedInputChannel = touchedWindow.windowHandle->getInputChannel(); - if (touchedInputChannel != NULL) { + if (touchedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked( @@ -2990,7 +3135,8 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inpu // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. - for (size_t i = 0; i < oldWindowHandles.size(); i++) { + size_t numWindows = oldWindowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i); if (!hasWindowHandleLocked(oldWindowHandle)) { #if DEBUG_FOCUS @@ -3006,25 +3152,28 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inpu } void InputDispatcher::setFocusedApplication( - const sp<InputApplicationHandle>& inputApplicationHandle) { + int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) { #if DEBUG_FOCUS ALOGD("setFocusedApplication"); #endif { // acquire lock AutoMutex _l(mLock); - if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) { - if (mFocusedApplicationHandle != inputApplicationHandle) { - if (mFocusedApplicationHandle != NULL) { + sp<InputApplicationHandle> oldFocusedApplicationHandle = + getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { + if (oldFocusedApplicationHandle != inputApplicationHandle) { + if (oldFocusedApplicationHandle != nullptr) { resetANRTimeoutsLocked(); - mFocusedApplicationHandle->releaseInfo(); + oldFocusedApplicationHandle->releaseInfo(); } - mFocusedApplicationHandle = inputApplicationHandle; + mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } - } else if (mFocusedApplicationHandle != NULL) { + } else if (oldFocusedApplicationHandle != nullptr) { resetANRTimeoutsLocked(); - mFocusedApplicationHandle->releaseInfo(); - mFocusedApplicationHandle.clear(); + oldFocusedApplicationHandle->releaseInfo(); + oldFocusedApplicationHandle.clear(); + mFocusedApplicationHandlesByDisplay.erase(displayId); } #if DEBUG_FOCUS @@ -3036,6 +3185,62 @@ void InputDispatcher::setFocusedApplication( mLooper->wake(); } +/** + * Sets the focused display, which is responsible for receiving focus-dispatched input events where + * the display not specified. + * + * We track any unreleased events for each window. If a window loses the ability to receive the + * released event, we will send a cancel event to it. So when the focused display is changed, we + * cancel all the unreleased display-unspecified events for the focused window on the old focused + * display. The display-specified events won't be affected. + */ +void InputDispatcher::setFocusedDisplay(int32_t displayId) { +#if DEBUG_FOCUS + ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); +#endif + { // acquire lock + AutoMutex _l(mLock); + + if (mFocusedDisplayId != displayId) { + sp<InputWindowHandle> oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); + if (oldFocusedWindowHandle != nullptr) { + sp<InputChannel> inputChannel = oldFocusedWindowHandle->getInputChannel(); + if (inputChannel != nullptr) { + CancelationOptions options( + CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS, + "The display which contains this window no longer has focus."); + synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + } + } + mFocusedDisplayId = displayId; + + // Sanity check + sp<InputWindowHandle> newFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (newFocusedWindowHandle == nullptr) { + ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId); + if (!mFocusedWindowHandlesByDisplay.empty()) { + ALOGE("But another display has a focused window:"); + for (auto& it : mFocusedWindowHandlesByDisplay) { + const int32_t displayId = it.first; + const sp<InputWindowHandle>& windowHandle = it.second; + ALOGE("Display #%" PRId32 " has focused window: '%s'\n", + displayId, windowHandle->getName().c_str()); + } + } + } + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { #if DEBUG_FOCUS ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); @@ -3062,7 +3267,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } #if DEBUG_FOCUS - //logDispatchStateLocked(); + logDispatchStateLocked(); #endif } // release lock @@ -3103,7 +3308,7 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel); sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel); - if (fromWindowHandle == NULL || toWindowHandle == NULL) { + if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { #if DEBUG_FOCUS ALOGD("Cannot transfer focus because from or to window not found."); #endif @@ -3207,17 +3412,35 @@ void InputDispatcher::logDispatchStateLocked() { void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "DispatchEnabled: %d\n", mDispatchEnabled); dump += StringPrintf(INDENT "DispatchFrozen: %d\n", mDispatchFrozen); + dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId); + + if (!mFocusedApplicationHandlesByDisplay.empty()) { + dump += StringPrintf(INDENT "FocusedApplications:\n"); + for (auto& it : mFocusedApplicationHandlesByDisplay) { + const int32_t displayId = it.first; + const sp<InputApplicationHandle>& applicationHandle = it.second; + dump += StringPrintf( + INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n", + displayId, + applicationHandle->getName().c_str(), + applicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0); + } + } else { + dump += StringPrintf(INDENT "FocusedApplications: <none>\n"); + } - if (mFocusedApplicationHandle != NULL) { - dump += StringPrintf(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", - mFocusedApplicationHandle->getName().c_str(), - mFocusedApplicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0); + if (!mFocusedWindowHandlesByDisplay.empty()) { + dump += StringPrintf(INDENT "FocusedWindows:\n"); + for (auto& it : mFocusedWindowHandlesByDisplay) { + const int32_t displayId = it.first; + const sp<InputWindowHandle>& windowHandle = it.second; + dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", + displayId, windowHandle->getName().c_str()); + } } else { - dump += StringPrintf(INDENT "FocusedApplication: <null>\n"); + dump += StringPrintf(INDENT "FocusedWindows: <none>\n"); } - dump += StringPrintf(INDENT "FocusedWindow: name='%s'\n", - mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().c_str() : "<null>"); if (!mTouchStatesByDisplay.isEmpty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); @@ -3243,44 +3466,56 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "TouchStates: <no displays touched>\n"; } - if (!mWindowHandles.isEmpty()) { - dump += INDENT "Windows:\n"; - for (size_t i = 0; i < mWindowHandles.size(); i++) { - const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i); - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - - dump += StringPrintf(INDENT2 "%zu: name='%s', displayId=%d, " - "paused=%s, hasFocus=%s, hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], scale=%f, " - "touchableRegion=", - i, windowInfo->name.c_str(), windowInfo->displayId, - toString(windowInfo->paused), - toString(windowInfo->hasFocus), - toString(windowInfo->hasWallpaper), - toString(windowInfo->visible), - toString(windowInfo->canReceiveKeys), - windowInfo->layoutParamsFlags, windowInfo->layoutParamsType, - windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->scaleFactor); - dumpRegion(dump, windowInfo->touchableRegion); - dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + if (!mWindowHandlesByDisplay.empty()) { + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + dump += StringPrintf(INDENT "Display: %d\n", it.first); + if (!windowHandles.isEmpty()) { + dump += INDENT2 "Windows:\n"; + for (size_t i = 0; i < windowHandles.size(); i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + + dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " + "paused=%s, hasFocus=%s, hasWallpaper=%s, " + "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " + "frame=[%d,%d][%d,%d], scale=%f, " + "touchableRegion=", + i, windowInfo->name.c_str(), windowInfo->displayId, + toString(windowInfo->paused), + toString(windowInfo->hasFocus), + toString(windowInfo->hasWallpaper), + toString(windowInfo->visible), + toString(windowInfo->canReceiveKeys), + windowInfo->layoutParamsFlags, windowInfo->layoutParamsType, + windowInfo->layer, + windowInfo->frameLeft, windowInfo->frameTop, + windowInfo->frameRight, windowInfo->frameBottom, + windowInfo->scaleFactor); + dumpRegion(dump, windowInfo->touchableRegion); + dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + windowInfo->ownerPid, windowInfo->ownerUid, + windowInfo->dispatchingTimeout / 1000000.0); + } + } else { + dump += INDENT2 "Windows: <none>\n"; + } } } else { - dump += INDENT "Windows: <none>\n"; + dump += INDENT "Displays: <none>\n"; } - if (!mMonitoringChannels.isEmpty()) { - dump += INDENT "MonitoringChannels:\n"; - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - const sp<InputChannel>& channel = mMonitoringChannels[i]; - dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str()); - } + if (!mMonitoringChannelsByDisplay.empty()) { + for (auto& it : mMonitoringChannelsByDisplay) { + const Vector<sp<InputChannel>>& monitoringChannels = it.second; + dump += INDENT "MonitoringChannels in Display %d:\n"; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + const sp<InputChannel>& channel = monitoringChannels[i]; + dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str()); + } + } } else { dump += INDENT "MonitoringChannels: <none>\n"; } @@ -3398,10 +3633,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { + const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) { #if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(), - toString(monitor)); + ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, + inputChannel->getName().c_str(), displayId); #endif { // acquire lock @@ -3413,13 +3648,20 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan return BAD_VALUE; } + // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE, + // treat inputChannel as monitor channel for displayId. + bool monitor = inputWindowHandle == nullptr && displayId != ADISPLAY_ID_NONE; + sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); + // Store monitor channel by displayId. if (monitor) { - mMonitoringChannels.push(inputChannel); + Vector<sp<InputChannel>>& monitoringChannels = + mMonitoringChannelsByDisplay[displayId]; + monitoringChannels.push(inputChannel); } mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); @@ -3476,11 +3718,21 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i } void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - if (mMonitoringChannels[i] == inputChannel) { - mMonitoringChannels.removeAt(i); - break; - } + for (auto it = mMonitoringChannelsByDisplay.begin(); + it != mMonitoringChannelsByDisplay.end(); ) { + Vector<sp<InputChannel>>& monitoringChannels = it->second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + if (monitoringChannels[i] == inputChannel) { + monitoringChannels.removeAt(i); + break; + } + } + if (monitoringChannels.empty()) { + it = mMonitoringChannelsByDisplay.erase(it); + } else { + ++it; + } } } @@ -3528,7 +3780,7 @@ void InputDispatcher::onANRLocked( dispatchLatency, waitDuration, reason); // Capture a record of the InputDispatcher state at the time of the ANR. - time_t t = time(NULL); + time_t t = time(nullptr); struct tm tm; localtime_r(&t, &tm); char timestr[64]; @@ -3583,8 +3835,8 @@ void InputDispatcher::doNotifyANRLockedInterruptible( mLock.lock(); resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, - commandEntry->inputWindowHandle != NULL - ? commandEntry->inputWindowHandle->getInputChannel() : NULL); + commandEntry->inputWindowHandle != nullptr + ? commandEntry->inputWindowHandle->getInputChannel() : nullptr); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -3817,6 +4069,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con keyEntry->eventTime = event.getEventTime(); keyEntry->deviceId = event.getDeviceId(); keyEntry->source = event.getSource(); + keyEntry->displayId = event.getDisplayId(); keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; keyEntry->keyCode = fallbackKeyCode; keyEntry->scanCode = event.getScanCode(); @@ -3855,7 +4108,7 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman } void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) { - event->initialize(entry->deviceId, entry->source, entry->action, entry->flags, + event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, entry->downTime, entry->eventTime); } @@ -3934,7 +4187,7 @@ void InputDispatcher::InjectionState::release() { InputDispatcher::EventEntry::EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags) : refCount(1), type(type), eventTime(eventTime), policyFlags(policyFlags), - injectionState(NULL), dispatchInProgress(false) { + injectionState(nullptr), dispatchInProgress(false) { } InputDispatcher::EventEntry::~EventEntry() { @@ -3953,7 +4206,7 @@ void InputDispatcher::EventEntry::release() { void InputDispatcher::EventEntry::releaseInjectionState() { if (injectionState) { injectionState->release(); - injectionState = NULL; + injectionState = nullptr; } } @@ -3991,11 +4244,11 @@ void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) cons // --- InputDispatcher::KeyEntry --- InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime, - int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime) : EventEntry(TYPE_KEY, eventTime, policyFlags), - deviceId(deviceId), source(source), action(action), flags(flags), + deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags), keyCode(keyCode), scanCode(scanCode), metaState(metaState), repeatCount(repeatCount), downTime(downTime), syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), @@ -4006,10 +4259,10 @@ InputDispatcher::KeyEntry::~KeyEntry() { } void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, action=%s, " + msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, keyActionToString(action).c_str(), flags, keyCode, + deviceId, source, displayId, keyActionToString(action).c_str(), flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @@ -4026,18 +4279,19 @@ void InputDispatcher::KeyEntry::recycle() { // --- InputDispatcher::MotionEntry --- InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime, int32_t deviceId, - uint32_t source, uint32_t policyFlags, int32_t action, int32_t actionButton, + uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, - int32_t displayId, uint32_t pointerCount, + uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset) : EventEntry(TYPE_MOTION, eventTime, policyFlags), eventTime(eventTime), - deviceId(deviceId), source(source), action(action), actionButton(actionButton), - flags(flags), metaState(metaState), buttonState(buttonState), + deviceId(deviceId), source(source), displayId(displayId), action(action), + actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), displayId(displayId), pointerCount(pointerCount) { + downTime(downTime), pointerCount(pointerCount) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); @@ -4051,11 +4305,12 @@ InputDispatcher::MotionEntry::~MotionEntry() { } void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, action=%s, actionButton=0x%08x, " - "flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, " - "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, displayId=%d, pointers=[", - deviceId, source, motionActionToString(action).c_str(), actionButton, flags, metaState, - buttonState, edgeFlags, xPrecision, yPrecision, displayId); + msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, buttonState=0x%08x, " + "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags, + metaState, buttonState, edgeFlags, xPrecision, yPrecision); + for (uint32_t i = 0; i < pointerCount; i++) { if (i) { msg += ", "; @@ -4184,8 +4439,8 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, } #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " - "actionMasked=%d", - entry->deviceId, entry->source, actionMasked); + "displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); #endif return false; } @@ -4237,8 +4492,8 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, } #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Dropping inconsistent motion pointer up/down or move event: " - "deviceId=%d, source=%08x, actionMasked=%d", - entry->deviceId, entry->source, actionMasked); + "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); #endif return false; } @@ -4250,8 +4505,9 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, return true; } #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x", - entry->deviceId, entry->source); + ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " + "displayId=%" PRId32, + entry->deviceId, entry->source, entry->displayId); #endif return false; } @@ -4276,6 +4532,7 @@ ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const const KeyMemento& memento = mKeyMementos.itemAt(i); if (memento.deviceId == entry->deviceId && memento.source == entry->source + && memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && memento.scanCode == entry->scanCode) { return i; @@ -4303,6 +4560,7 @@ void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t f KeyMemento& memento = mKeyMementos.editTop(); memento.deviceId = entry->deviceId; memento.source = entry->source; + memento.displayId = entry->displayId; memento.keyCode = entry->keyCode; memento.scanCode = entry->scanCode; memento.metaState = entry->metaState; @@ -4317,11 +4575,11 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, MotionMemento& memento = mMotionMementos.editTop(); memento.deviceId = entry->deviceId; memento.source = entry->source; + memento.displayId = entry->displayId; memento.flags = flags; memento.xPrecision = entry->xPrecision; memento.yPrecision = entry->yPrecision; memento.downTime = entry->downTime; - memento.displayId = entry->displayId; memento.setPointers(entry); memento.hovering = hovering; memento.policyFlags = entry->policyFlags; @@ -4341,7 +4599,7 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim const KeyMemento& memento = mKeyMementos.itemAt(i); if (shouldCancelKey(memento, options)) { outEvents.push(new KeyEntry(currentTime, - memento.deviceId, memento.source, memento.policyFlags, + memento.deviceId, memento.source, memento.displayId, memento.policyFlags, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime)); } @@ -4351,13 +4609,12 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim const MotionMemento& memento = mMotionMementos.itemAt(i); if (shouldCancelMotion(memento, options)) { outEvents.push(new MotionEntry(currentTime, - memento.deviceId, memento.source, memento.policyFlags, + memento.deviceId, memento.source, memento.displayId, memento.policyFlags, memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL, memento.flags, 0, 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, - memento.displayId, memento.pointerCount, memento.pointerProperties, memento.pointerCoords, 0, 0)); } @@ -4424,6 +4681,8 @@ bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, return true; case CancelationOptions::CANCEL_FALLBACK_EVENTS: return memento.flags & AKEY_EVENT_FLAG_FALLBACK; + case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS: + return memento.displayId == ADISPLAY_ID_NONE; default: return false; } @@ -4442,6 +4701,8 @@ bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& mement return memento.source & AINPUT_SOURCE_CLASS_POINTER; case CancelationOptions::CANCEL_NON_POINTER_EVENTS: return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS: + return memento.displayId == ADISPLAY_ID_NONE; default: return false; } @@ -4461,7 +4722,7 @@ InputDispatcher::Connection::~Connection() { } const std::string InputDispatcher::Connection::getWindowName() const { - if (inputWindowHandle != NULL) { + if (inputWindowHandle != nullptr) { return inputWindowHandle->getName(); } if (monitor) { @@ -4487,19 +4748,19 @@ const char* InputDispatcher::Connection::getStatusLabel() const { } InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) { + for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { if (entry->seq == seq) { return entry; } } - return NULL; + return nullptr; } // --- InputDispatcher::CommandEntry --- InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), + command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0), seq(0), handled(false) { } @@ -4510,7 +4771,7 @@ InputDispatcher::CommandEntry::~CommandEntry() { // --- InputDispatcher::TouchState --- InputDispatcher::TouchState::TouchState() : - down(false), split(false), deviceId(-1), source(0), displayId(-1) { + down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) { } InputDispatcher::TouchState::~TouchState() { @@ -4521,7 +4782,7 @@ void InputDispatcher::TouchState::reset() { split = false; deviceId = -1; source = 0; - displayId = -1; + displayId = ADISPLAY_ID_NONE; windows.clear(); } @@ -4590,7 +4851,7 @@ sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandl return window.windowHandle; } } - return NULL; + return nullptr; } bool InputDispatcher::TouchState::isSlippery() const { diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 8da8450380..5efb2fa05b 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -18,7 +18,9 @@ #define _UI_INPUT_DISPATCHER_H #include <input/Input.h> +#include <input/InputApplication.h> #include <input/InputTransport.h> +#include <input/InputWindow.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> #include <utils/threads.h> @@ -31,9 +33,8 @@ #include <stddef.h> #include <unistd.h> #include <limits.h> +#include <unordered_map> -#include "InputWindow.h" -#include "InputApplication.h" #include "InputListener.h" @@ -299,7 +300,7 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId, + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags) = 0; @@ -307,14 +308,21 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) = 0; + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles, + int32_t displayId) = 0; - /* Sets the focused application. + /* Sets the focused application on the given display. * * This method may be called on any thread (usually by the input manager). */ virtual void setFocusedApplication( - const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + + /* Sets the focused display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedDisplay(int32_t displayId) = 0; /* Sets the input dispatching mode. * @@ -338,13 +346,19 @@ public: virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel, const sp<InputChannel>& toChannel) = 0; - /* Registers or unregister input channels that may be used as targets for input events. - * If monitor is true, the channel will receive a copy of all input events. + /* Registers input channels that may be used as targets for input events. + * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE, + * the channel will receive a copy of all input events form the specific displayId. * - * These methods may be called on any thread (usually by the input manager). + * This method may be called on any thread (usually by the input manager). */ virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0; + const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId) = 0; + + /* Unregister input channels that will no longer receive input events. + * + * This method may be called on any thread (usually by the input manager). + */ virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; }; @@ -383,12 +397,15 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args); virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); - virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId, + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags); - virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles); - virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle); + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles, + int32_t displayId); + virtual void setFocusedApplication(int32_t displayId, + const sp<InputApplicationHandle>& inputApplicationHandle); + virtual void setFocusedDisplay(int32_t displayId); virtual void setInputDispatchMode(bool enabled, bool frozen); virtual void setInputFilterEnabled(bool enabled); @@ -396,7 +413,7 @@ public: const sp<InputChannel>& toChannel); virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor); + const sp<InputWindowHandle>& inputWindowHandle, int32_t displayId); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); private: @@ -406,7 +423,7 @@ private: T* prev; protected: - inline Link() : next(NULL), prev(NULL) { } + inline Link() : next(nullptr), prev(nullptr) { } }; struct InjectionState { @@ -441,7 +458,7 @@ private: bool dispatchInProgress; // initially false, set to true while dispatching - inline bool isInjected() const { return injectionState != NULL; } + inline bool isInjected() const { return injectionState != nullptr; } void release(); @@ -474,6 +491,7 @@ private: struct KeyEntry : EventEntry { int32_t deviceId; uint32_t source; + int32_t displayId; int32_t action; int32_t flags; int32_t keyCode; @@ -494,8 +512,8 @@ private: nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER KeyEntry(nsecs_t eventTime, - int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime); virtual void appendDescription(std::string& msg) const; void recycle(); @@ -508,6 +526,7 @@ private: nsecs_t eventTime; int32_t deviceId; uint32_t source; + int32_t displayId; int32_t action; int32_t actionButton; int32_t flags; @@ -517,17 +536,15 @@ private: float xPrecision; float yPrecision; nsecs_t downTime; - int32_t displayId; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; MotionEntry(nsecs_t eventTime, - int32_t deviceId, uint32_t source, uint32_t policyFlags, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, - float xPrecision, float yPrecision, nsecs_t downTime, - int32_t displayId, uint32_t pointerCount, + float xPrecision, float yPrecision, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset); virtual void appendDescription(std::string& msg) const; @@ -614,7 +631,7 @@ private: T* tail; uint32_t entryCount; - inline Queue() : head(NULL), tail(NULL), entryCount(0) { + inline Queue() : head(nullptr), tail(nullptr), entryCount(0) { } inline bool isEmpty() const { @@ -629,7 +646,7 @@ private: } else { head = entry; } - entry->next = NULL; + entry->next = nullptr; tail = entry; } @@ -641,7 +658,7 @@ private: } else { tail = entry; } - entry->prev = NULL; + entry->prev = nullptr; head = entry; } @@ -664,9 +681,9 @@ private: T* entry = head; head = entry->next; if (head) { - head->prev = NULL; + head->prev = nullptr; } else { - tail = NULL; + tail = nullptr; } return entry; } @@ -683,6 +700,10 @@ private: CANCEL_POINTER_EVENTS = 1, CANCEL_NON_POINTER_EVENTS = 2, CANCEL_FALLBACK_EVENTS = 3, + + /* Cancel events where the display not specified. These events would go to the focused + * display. */ + CANCEL_DISPLAY_UNSPECIFIED_EVENTS = 4, }; // The criterion to use to determine which events should be canceled. @@ -754,6 +775,7 @@ private: struct KeyMemento { int32_t deviceId; uint32_t source; + int32_t displayId; int32_t keyCode; int32_t scanCode; int32_t metaState; @@ -765,11 +787,11 @@ private: struct MotionMemento { int32_t deviceId; uint32_t source; + int32_t displayId; int32_t flags; float xPrecision; float yPrecision; nsecs_t downTime; - int32_t displayId; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; @@ -898,8 +920,8 @@ private: ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel); - // Input channels that will receive a copy of all input events. - Vector<sp<InputChannel> > mMonitoringChannels; + // Input channels that will receive a copy of all input events sent to the provided display. + std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay; // Event injection and synchronization. Condition mInjectionResultAvailableCondition; @@ -932,6 +954,9 @@ private: }; // Maps the key code replaced, device id tuple to the key code it was replaced with KeyedVector<KeyReplacement, int32_t> mReplacedKeys; + // Process certain Meta + Key combinations + void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, + int32_t& keyCode, int32_t& metaState); // Deferred command processing. bool haveCommandsLocked() const; @@ -952,13 +977,14 @@ private: bool mDispatchFrozen; bool mInputFilterEnabled; - Vector<sp<InputWindowHandle> > mWindowHandles; - + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay; + // Get window handles by display, return an empty vector if not found. + Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const; sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const; bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const; // Focus tracking for keys, trackball, etc. - sp<InputWindowHandle> mFocusedWindowHandle; + std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay; // Focus tracking for touch. struct TouchedWindow { @@ -989,8 +1015,11 @@ private: KeyedVector<int32_t, TouchState> mTouchStatesByDisplay; TouchState mTempTouchState; - // Focused application. - sp<InputApplicationHandle> mFocusedApplicationHandle; + // Focused applications. + std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay; + + // Top focused display. + int32_t mFocusedDisplayId; // Dispatcher state at time of last ANR. std::string mLastANRState; @@ -1038,6 +1067,7 @@ private: nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); void resetANRTimeoutsLocked(); + int32_t getTargetDisplayId(const EventEntry* entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, @@ -1046,7 +1076,7 @@ private: void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets); - void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets); + void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId); void pokeUserActivityLocked(const EventEntry* eventEntry); bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 520fea4c95..25a39a8274 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -43,17 +43,18 @@ void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& li // --- NotifyKeyArgs --- NotifyKeyArgs::NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, - uint32_t policyFlags, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) : - eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags), + eventTime(eventTime), deviceId(deviceId), source(source), displayId(displayId), + policyFlags(policyFlags), action(action), flags(flags), keyCode(keyCode), scanCode(scanCode), metaState(metaState), downTime(downTime) { } NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) : eventTime(other.eventTime), deviceId(other.deviceId), source(other.source), - policyFlags(other.policyFlags), + displayId(other.displayId), policyFlags(other.policyFlags), action(other.action), flags(other.flags), keyCode(other.keyCode), scanCode(other.scanCode), metaState(other.metaState), downTime(other.downTime) { @@ -67,16 +68,17 @@ void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { // --- NotifyMotionArgs --- NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, - uint32_t policyFlags, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp, + int32_t buttonState, int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) : - eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags), + eventTime(eventTime), deviceId(deviceId), source(source), displayId(displayId), + policyFlags(policyFlags), action(action), actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), - edgeFlags(edgeFlags), displayId(displayId), deviceTimestamp(deviceTimestamp), + edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp), pointerCount(pointerCount), xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) { for (uint32_t i = 0; i < pointerCount; i++) { @@ -87,10 +89,10 @@ NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : eventTime(other.eventTime), deviceId(other.deviceId), source(other.source), - policyFlags(other.policyFlags), + displayId(other.displayId), policyFlags(other.policyFlags), action(other.action), actionButton(other.actionButton), flags(other.flags), metaState(other.metaState), buttonState(other.buttonState), - edgeFlags(other.edgeFlags), displayId(other.displayId), + edgeFlags(other.edgeFlags), deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount), xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) { for (uint32_t i = 0; i < pointerCount; i++) { diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/InputListener.h index 77afb344c2..a3d919bf5a 100644 --- a/services/inputflinger/InputListener.h +++ b/services/inputflinger/InputListener.h @@ -55,6 +55,7 @@ struct NotifyKeyArgs : public NotifyArgs { nsecs_t eventTime; int32_t deviceId; uint32_t source; + int32_t displayId; uint32_t policyFlags; int32_t action; int32_t flags; @@ -65,8 +66,8 @@ struct NotifyKeyArgs : public NotifyArgs { inline NotifyKeyArgs() { } - NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, + NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime); NotifyKeyArgs(const NotifyKeyArgs& other); @@ -82,6 +83,7 @@ struct NotifyMotionArgs : public NotifyArgs { nsecs_t eventTime; int32_t deviceId; uint32_t source; + int32_t displayId; uint32_t policyFlags; int32_t action; int32_t actionButton; @@ -89,7 +91,6 @@ struct NotifyMotionArgs : public NotifyArgs { int32_t metaState; int32_t buttonState; int32_t edgeFlags; - int32_t displayId; /** * A timestamp in the input device's time base, not the platform's. * The units are microseconds since the last reset. @@ -106,10 +107,11 @@ struct NotifyMotionArgs : public NotifyArgs { inline NotifyMotionArgs() { } - NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags, + NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, int32_t displayId, uint32_t deviceTimestamp, uint32_t pointerCount, + int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index a213b2dfaa..92e0af21c3 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -31,7 +31,6 @@ #include <utils/Vector.h> #include <utils/Timers.h> #include <utils/RefBase.h> -#include <utils/String8.h> namespace android { diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index efa6f88a87..869a2fc170 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -226,7 +226,7 @@ static float calculateCommonVector(float a, float b) { } static void synthesizeButtonKey(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, + nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { if ( @@ -236,19 +236,19 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, || (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && !(currentButtonState & buttonState))) { - NotifyKeyArgs args(when, deviceId, source, policyFlags, + NotifyKeyArgs args(when, deviceId, source, displayId, policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); context->getListener()->notifyKey(&args); } } static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, + nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, deviceId, source, policyFlags, + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, deviceId, source, policyFlags, + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD); } @@ -256,65 +256,32 @@ static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, // --- InputReaderConfiguration --- -bool InputReaderConfiguration::getDisplayViewport(ViewportType viewportType, - const String8* uniqueDisplayId, DisplayViewport* outViewport) const { - const DisplayViewport* viewport = NULL; - if (viewportType == ViewportType::VIEWPORT_VIRTUAL && uniqueDisplayId != NULL) { - for (const DisplayViewport& currentViewport : mVirtualDisplays) { - if (currentViewport.uniqueId == *uniqueDisplayId) { - viewport = ¤tViewport; - break; +std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewport( + ViewportType viewportType, const std::string& uniqueDisplayId) const { + for (const DisplayViewport& currentViewport : mDisplays) { + if (currentViewport.type == viewportType) { + if (uniqueDisplayId.empty() || + (!uniqueDisplayId.empty() && uniqueDisplayId == currentViewport.uniqueId)) { + return std::make_optional(currentViewport); } } - } else if (viewportType == ViewportType::VIEWPORT_EXTERNAL) { - viewport = &mExternalDisplay; - } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) { - viewport = &mInternalDisplay; } - - if (viewport != NULL && viewport->displayId >= 0) { - *outViewport = *viewport; - return true; - } - return false; + return std::nullopt; } -void InputReaderConfiguration::setPhysicalDisplayViewport(ViewportType viewportType, - const DisplayViewport& viewport) { - if (viewportType == ViewportType::VIEWPORT_EXTERNAL) { - mExternalDisplay = viewport; - } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) { - mInternalDisplay = viewport; - } -} - -void InputReaderConfiguration::setVirtualDisplayViewports( - const Vector<DisplayViewport>& viewports) { - mVirtualDisplays = viewports; +void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) { + mDisplays = viewports; } void InputReaderConfiguration::dump(std::string& dump) const { - dump += INDENT4 "ViewportInternal:\n"; - dumpViewport(dump, mInternalDisplay); - dump += INDENT4 "ViewportExternal:\n"; - dumpViewport(dump, mExternalDisplay); - dump += INDENT4 "ViewportVirtual:\n"; - for (const DisplayViewport& viewport : mVirtualDisplays) { + for (const DisplayViewport& viewport : mDisplays) { dumpViewport(dump, viewport); } } -void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport) const { - dump += StringPrintf(INDENT5 "Viewport: displayId=%d, orientation=%d, uniqueId='%s', " - "logicalFrame=[%d, %d, %d, %d], " - "physicalFrame=[%d, %d, %d, %d], " - "deviceSize=[%d, %d]\n", - viewport.displayId, viewport.orientation, viewport.uniqueId.c_str(), - viewport.logicalLeft, viewport.logicalTop, - viewport.logicalRight, viewport.logicalBottom, - viewport.physicalLeft, viewport.physicalTop, - viewport.physicalRight, viewport.physicalBottom, - viewport.deviceWidth, viewport.deviceHeight); +void InputReaderConfiguration::dumpViewport(std::string& dump, const DisplayViewport& viewport) + const { + dump += StringPrintf(INDENT4 "%s\n", viewport.toString().c_str()); } @@ -473,10 +440,10 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { if (device->isIgnored()) { ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, - identifier.name.string()); + identifier.name.c_str()); } else { ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, - identifier.name.string(), device->getSources()); + identifier.name.c_str(), device->getSources()); } mDevices.add(deviceId, device); @@ -488,7 +455,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - InputDevice* device = NULL; + InputDevice* device = nullptr; ssize_t deviceIndex = mDevices.indexOfKey(deviceId); if (deviceIndex < 0) { ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); @@ -501,10 +468,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { if (device->isIgnored()) { ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", - device->getId(), device->getName().string()); + device->getId(), device->getName().c_str()); } else { ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", - device->getId(), device->getName().string(), device->getSources()); + device->getId(), device->getName().c_str(), device->getSources()); } if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { @@ -687,7 +654,7 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, if (now < mDisableVirtualKeysTimeout) { ALOGI("Dropping virtual key from device %s because virtual keys are " "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().string(), + device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, scanCode); return true; @@ -894,7 +861,7 @@ void InputReader::dump(std::string& dump) { if (i != 0) { dump += ", "; } - dump += mConfig.excludedDeviceNames.itemAt(i).string(); + dump += mConfig.excludedDeviceNames[i]; } dump += "]\n"; dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", @@ -1077,7 +1044,7 @@ void InputDevice::dump(std::string& dump) { getDeviceInfo(& deviceInfo); dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), - deviceInfo.getDisplayName().string()); + deviceInfo.getDisplayName().c_str()); dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); @@ -1135,7 +1102,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); + std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); if (mAlias != alias) { mAlias = alias; bumpGeneration(); @@ -1196,7 +1163,7 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { #endif } } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { - ALOGI("Detected input event buffer overrun for device %s.", getName().string()); + ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); mDropUntilNextSync = true; reset(rawEvent->when); } else { @@ -1786,7 +1753,7 @@ void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { // --- MultiTouchMotionAccumulator --- MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : - mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false), + mCurrentSlot(-1), mSlots(nullptr), mSlotCount(0), mUsingSlotsProtocol(false), mHaveStylus(false), mDeviceTimestamp(0) { } @@ -2240,8 +2207,7 @@ void VibratorInputMapper::dump(std::string& dump) { KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) : - InputMapper(device), mSource(source), - mKeyboardType(keyboardType) { + InputMapper(device), mSource(source), mKeyboardType(keyboardType) { } KeyboardInputMapper::~KeyboardInputMapper() { @@ -2251,6 +2217,20 @@ uint32_t KeyboardInputMapper::getSources() { return mSource; } +int32_t KeyboardInputMapper::getOrientation() { + if (mViewport) { + return mViewport->orientation; + } + return DISPLAY_ORIENTATION_0; +} + +int32_t KeyboardInputMapper::getDisplayId() { + if (mViewport) { + return mViewport->displayId; + } + return ADISPLAY_ID_NONE; +} + void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); @@ -2262,13 +2242,12 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += INDENT2 "Keyboard Input Mapper:\n"; dumpParameters(dump); dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); } - void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { InputMapper::configure(when, config, changes); @@ -2279,15 +2258,8 @@ void KeyboardInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - DisplayViewport v; - if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) { - mOrientation = v.orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; - } - } else { - mOrientation = DISPLAY_ORIENTATION_0; + if (mParameters.orientationAware) { + mViewport = config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); } } } @@ -2310,10 +2282,7 @@ void KeyboardInputMapper::configureParameters() { config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - mParameters.hasAssociatedDisplay = false; if (mParameters.orientationAware) { - mParameters.hasAssociatedDisplay = true; - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); @@ -2327,8 +2296,6 @@ void KeyboardInputMapper::configureParameters() { void KeyboardInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", - toString(mParameters.hasAssociatedDisplay)); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", @@ -2423,8 +2390,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, if (down) { // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - keyCode = rotateKeyCode(keyCode, mOrientation); + if (mParameters.orientationAware) { + keyCode = rotateKeyCode(keyCode, getOrientation()); } // Add key down. @@ -2461,7 +2428,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, // key was not actually down ALOGI("Dropping key up from device %s because the key was not down. " "keyCode=%d, scanCode=%d", - getDeviceName().string(), keyCode, scanCode); + getDeviceName().c_str(), keyCode, scanCode); return; } } @@ -2489,7 +2456,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyKeyArgs args(when, getDeviceId(), mSource, getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); getListener()->notifyKey(&args); @@ -2699,15 +2666,13 @@ void CursorInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mOrientation = DISPLAY_ORIENTATION_0; if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - DisplayViewport v; - if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) { - mOrientation = v.orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; + std::optional<DisplayViewport> internalViewport = + config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); + if (internalViewport) { + mOrientation = internalViewport->orientation; } - } else { - mOrientation = DISPLAY_ORIENTATION_0; } bumpGeneration(); } @@ -2826,8 +2791,8 @@ void CursorInputMapper::sync(nsecs_t when) { float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); bool scrolled = vscroll != 0 || hscroll != 0; - mWheelYVelocityControl.move(when, NULL, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, NULL); + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); mPointerVelocityControl.move(when, &deltaX, &deltaY); @@ -2874,7 +2839,7 @@ void CursorInputMapper::sync(nsecs_t when) { // Synthesize key down from buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - policyFlags, lastButtonState, currentButtonState); + displayId, policyFlags, lastButtonState, currentButtonState); // Send motion event. if (downChanged || moved || scrolled || buttonsChanged) { @@ -2894,19 +2859,19 @@ void CursorInputMapper::sync(nsecs_t when) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs releaseArgs(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&releaseArgs); } } - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&args); @@ -2915,10 +2880,10 @@ void CursorInputMapper::sync(nsecs_t when) { while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - NotifyMotionArgs pressArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs pressArgs(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&pressArgs); } @@ -2929,10 +2894,10 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&hoverArgs); } @@ -2942,10 +2907,10 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&scrollArgs); } @@ -2953,7 +2918,7 @@ void CursorInputMapper::sync(nsecs_t when) { // Synthesize key up from buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - policyFlags, lastButtonState, currentButtonState); + displayId, policyFlags, lastButtonState, currentButtonState); mCursorMotionAccumulator.finishSync(); mCursorScrollAccumulator.finishSync(); @@ -2968,7 +2933,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } void CursorInputMapper::fadePointer() { - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } } @@ -3019,9 +2984,10 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, mRotaryEncoderScrollAccumulator.configure(getDevice()); } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - DisplayViewport v; - if (config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, NULL, &v)) { - mOrientation = v.orientation; + std::optional<DisplayViewport> internalViewport = + config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); + if (internalViewport) { + mOrientation = internalViewport->orientation; } else { mOrientation = DISPLAY_ORIENTATION_0; } @@ -3072,10 +3038,10 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = mContext->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0); getListener()->notifyMotion(&scrollArgs); } @@ -3394,8 +3360,10 @@ void TouchInputMapper::configureParameters() { mParameters.hasAssociatedDisplay = true; if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + String8 uniqueDisplayId; getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - mParameters.uniqueDisplayId); + uniqueDisplayId); + mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); } } @@ -3506,7 +3474,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Ensure we have valid X and Y axes. if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", getDeviceName().string()); + "The device will be inoperable.", getDeviceName().c_str()); mDeviceMode = DEVICE_MODE_DISABLED; return; } @@ -3518,28 +3486,31 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Get associated display dimensions. DisplayViewport newViewport; if (mParameters.hasAssociatedDisplay) { - const String8* uniqueDisplayId = NULL; + std::string uniqueDisplayId; ViewportType viewportTypeToUse; if (mParameters.associatedDisplayIsExternal) { viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; - } else if (!mParameters.uniqueDisplayId.isEmpty()) { + } else if (!mParameters.uniqueDisplayId.empty()) { // If the IDC file specified a unique display Id, then it expects to be linked to a // virtual display with the same unique ID. - uniqueDisplayId = &mParameters.uniqueDisplayId; + uniqueDisplayId = mParameters.uniqueDisplayId; viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL; } else { viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; } - if (!mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId, &newViewport)) { + std::optional<DisplayViewport> viewportToUse = + mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId); + if (!viewportToUse) { ALOGI(INDENT "Touch device '%s' could not query the properties of its associated " "display. The device will be inoperable until the display size " "becomes available.", - getDeviceName().string()); + getDeviceName().c_str()); mDeviceMode = DEVICE_MODE_DISABLED; return; } + newViewport = *viewportToUse; } else { newViewport.setNonDisplayViewport(rawWidth, rawHeight); } @@ -3597,6 +3568,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { break; } + if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { + ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); + naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; + naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; + } + mPhysicalWidth = naturalPhysicalWidth; mPhysicalHeight = naturalPhysicalHeight; mPhysicalLeft = naturalPhysicalLeft; @@ -3632,7 +3609,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Create pointer controller if needed. if (mDeviceMode == DEVICE_MODE_POINTER || (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == NULL) { + if (mPointerController == nullptr) { mPointerController = getPolicy()->obtainPointerController(getDeviceId()); } } else { @@ -3642,7 +3619,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { if (viewportChanged || deviceModeChanged) { ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " "display id %d", - getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight, + getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, mSurfaceOrientation, mDeviceMode, mViewport.displayId); // Configure X and Y factors. @@ -3910,17 +3887,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { } void TouchInputMapper::dumpSurface(std::string& dump) { - dump += StringPrintf(INDENT3 "Viewport: displayId=%d, orientation=%d, " - "logicalFrame=[%d, %d, %d, %d], " - "physicalFrame=[%d, %d, %d, %d], " - "deviceSize=[%d, %d]\n", - mViewport.displayId, mViewport.orientation, - mViewport.logicalLeft, mViewport.logicalTop, - mViewport.logicalRight, mViewport.logicalBottom, - mViewport.physicalLeft, mViewport.physicalTop, - mViewport.physicalRight, mViewport.physicalBottom, - mViewport.deviceWidth, mViewport.deviceHeight); - + dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); @@ -4286,7 +4253,7 @@ void TouchInputMapper::reset(nsecs_t when) { mPointerSimple.reset(); resetExternalStylus(); - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->clearSpots(); } @@ -4448,7 +4415,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { // Synthesize key down from raw buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState); + mViewport.displayId, policyFlags, + mLastCookedState.buttonState, mCurrentCookedState.buttonState); // Dispatch the touches either directly or by translation through a pointer on screen. if (mDeviceMode == DEVICE_MODE_POINTER) { @@ -4495,7 +4463,7 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { dispatchPointerUsage(when, policyFlags, pointerUsage); } else { if (mDeviceMode == DEVICE_MODE_DIRECT - && mConfig.showTouches && mPointerController != NULL) { + && mConfig.showTouches && mPointerController != nullptr) { mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); @@ -4520,7 +4488,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { // Synthesize key up from raw buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - policyFlags, mLastCookedState.buttonState, mCurrentCookedState.buttonState); + mViewport.displayId, policyFlags, + mLastCookedState.buttonState, mCurrentCookedState.buttonState); // Clear some transient state. mCurrentRawState.rawVScroll = 0; @@ -4735,8 +4704,8 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t metaState = mContext->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); + NotifyKeyArgs args(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId, + policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); getListener()->notifyKey(&args); } @@ -5428,10 +5397,10 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mViewport.displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime); getListener()->notifyMotion(&args); } @@ -5473,7 +5442,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) mPointerVelocityControl.reset(); // Remove any current spots. - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->clearSpots(); } @@ -6336,7 +6305,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering) { int32_t metaState = getContext()->getGlobalMetaState(); - if (mPointerController != NULL) { + if (mPointerController != nullptr) { if (down || hovering) { mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); mPointerController->clearSpots(); @@ -6351,9 +6320,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6364,9 +6333,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6379,9 +6348,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6389,9 +6358,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send move. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6403,10 +6372,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6414,10 +6383,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, } // Send hover move. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6427,8 +6396,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { float vscroll = mCurrentRawState.rawVScroll; float hscroll = mCurrentRawState.rawHScroll; - mWheelYVelocityControl.move(when, NULL, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, NULL); + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); // Send scroll. PointerCoords pointerCoords; @@ -6436,9 +6405,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -6499,9 +6468,9 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 } } - NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), source, mViewport.displayId, policyFlags, action, actionButton, flags, metaState, buttonState, edgeFlags, - mViewport.displayId, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, + deviceTimestamp, pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); getListener()->notifyMotion(&args); } @@ -6535,7 +6504,7 @@ bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties } void TouchInputMapper::fadePointer() { - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } } @@ -6574,7 +6543,7 @@ const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit( } } - return NULL; + return nullptr; } void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { @@ -6923,7 +6892,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { #if DEBUG_POINTERS ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " "ignoring the rest.", - getDeviceName().string(), MAX_POINTERS); + getDeviceName().c_str(), MAX_POINTERS); #endif break; // too many fingers! } @@ -7014,7 +6983,7 @@ void MultiTouchInputMapper::configureRawPointerAxes() { if (slotCount > MAX_SLOTS) { ALOGW("MultiTouch Device %s reported %zu slots but the framework " "only supports a maximum of %zu slots at this time.", - getDeviceName().string(), slotCount, MAX_SLOTS); + getDeviceName().c_str(), slotCount, MAX_SLOTS); slotCount = MAX_SLOTS; } mMultiTouchMotionAccumulator.configure(getDevice(), @@ -7257,7 +7226,7 @@ void JoystickInputMapper::configure(nsecs_t when, // Prefer to keep explicitly mapped axes. if (mAxes.size() > PointerCoords::MAX_AXES) { ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", - getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES); + getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); pruneAxes(true); pruneAxes(false); } @@ -7279,7 +7248,7 @@ void JoystickInputMapper::configure(nsecs_t when, } else { ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " "have already been assigned to other axes.", - getDeviceName().string(), mAxes.keyAt(i)); + getDeviceName().c_str(), mAxes.keyAt(i)); mAxes.removeItemsAt(i--); numAxes -= 1; } @@ -7308,7 +7277,7 @@ void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { continue; } ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().string(), mAxes.keyAt(i)); + getDeviceName().c_str(), mAxes.keyAt(i)); mAxes.removeItemsAt(i); } } @@ -7423,9 +7392,10 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags, + NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, + policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - ADISPLAY_ID_NONE, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0); getListener()->notifyMotion(&args); } diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index 2f98e69800..3410bc9c5f 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -35,8 +35,10 @@ #include <utils/BitSet.h> #include <utils/SortedVector.h> +#include <optional> #include <stddef.h> #include <unistd.h> +#include <vector> // Maximum supported size of a vibration pattern. // Must be at least 2. @@ -100,7 +102,7 @@ struct InputReaderConfiguration { // The excluded device names for the platform. // Devices with these names will be ignored. - Vector<String8> excludedDeviceNames; + std::vector<std::string> excludedDeviceNames; // Velocity control parameters for mouse pointer movements. VelocityControlParameters pointerVelocityControlParameters; @@ -200,19 +202,16 @@ struct InputReaderConfiguration { pointerGestureZoomSpeedRatio(0.3f), showTouches(false) { } - bool getDisplayViewport(ViewportType viewportType, const String8* displayId, - DisplayViewport* outViewport) const; - void setPhysicalDisplayViewport(ViewportType viewportType, const DisplayViewport& viewport); - void setVirtualDisplayViewports(const Vector<DisplayViewport>& viewports); + std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType, + const std::string& uniqueDisplayId) const; + void setDisplayViewports(const std::vector<DisplayViewport>& viewports); void dump(std::string& dump) const; void dumpViewport(std::string& dump, const DisplayViewport& viewport) const; private: - DisplayViewport mInternalDisplay; - DisplayViewport mExternalDisplay; - Vector<DisplayViewport> mVirtualDisplays; + std::vector<DisplayViewport> mDisplays; }; @@ -273,11 +272,11 @@ public: const InputDeviceIdentifier& identifier) = 0; /* Gets a user-supplied alias for a particular input device, or an empty string if none. */ - virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0; + virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier) = 0; /* Gets the affine calibration associated with the specified device. */ virtual TouchAffineTransformation getTouchAffineTransformation( - const String8& inputDeviceDescriptor, int32_t surfaceRotation) = 0; + const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0; }; @@ -552,8 +551,8 @@ public: inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } inline int32_t getGeneration() const { return mGeneration; } - inline const String8& getName() const { return mIdentifier.name; } - inline const String8& getDescriptor() { return mIdentifier.descriptor; } + inline const std::string getName() const { return mIdentifier.name; } + inline const std::string getDescriptor() { return mIdentifier.descriptor; } inline uint32_t getClasses() const { return mClasses; } inline uint32_t getSources() const { return mSources; } @@ -624,7 +623,7 @@ private: int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; - String8 mAlias; + std::string mAlias; uint32_t mClasses; Vector<InputMapper*> mMappers; @@ -980,7 +979,7 @@ public: inline InputDevice* getDevice() { return mDevice; } inline int32_t getDeviceId() { return mDevice->getId(); } - inline const String8 getDeviceName() { return mDevice->getName(); } + inline const std::string getDeviceName() { return mDevice->getName(); } inline InputReaderContext* getContext() { return mContext; } inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } inline InputListenerInterface* getListener() { return mContext->getListener(); } @@ -1094,6 +1093,9 @@ public: virtual void updateMetaState(int32_t keyCode); private: + // The current viewport. + std::optional<DisplayViewport> mViewport; + struct KeyDown { int32_t keyCode; int32_t scanCode; @@ -1102,8 +1104,6 @@ private: uint32_t mSource; int32_t mKeyboardType; - int32_t mOrientation; // orientation for dpad keys - Vector<KeyDown> mKeyDowns; // keys that are down int32_t mMetaState; nsecs_t mDownTime; // time of most recent key down @@ -1120,7 +1120,6 @@ private: // Immutable configuration parameters. struct Parameters { - bool hasAssociatedDisplay; bool orientationAware; bool handlesKeyRepeat; } mParameters; @@ -1128,6 +1127,9 @@ private: void configureParameters(); void dumpParameters(std::string& dump); + int32_t getOrientation(); + int32_t getDisplayId(); + bool isKeyboardOrGamepadKey(int32_t scanCode); bool isMediaKey(int32_t keyCode); @@ -1305,7 +1307,7 @@ protected: bool associatedDisplayIsExternal; bool orientationAware; bool hasButtonUnderPad; - String8 uniqueDisplayId; + std::string uniqueDisplayId; enum GestureMode { GESTURE_MODE_SINGLE_TOUCH, diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index bd11d5620b..2f046c3527 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -217,18 +217,18 @@ input_property_map_t* InputDriver::inputGetDevicePropertyMap(input_device_identi idi.product = id->productId; idi.version = id->version; - String8 configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( + std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); - if (configFile.isEmpty()) { + if (configFile.empty()) { ALOGD("No input device configuration file found for device '%s'.", - idi.name.string()); + idi.name.c_str()); } else { auto propMap = new input_property_map_t(); - status_t status = PropertyMap::load(configFile, &propMap->propertyMap); + status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap); if (status) { ALOGE("Error loading input device configuration file for device '%s'. " "Using default configuration.", - idi.name.string()); + idi.name.c_str()); delete propMap; return nullptr; } diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index afaf139d37..a1cd71ca9d 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -2,6 +2,7 @@ cc_test { name: "inputflinger_tests", + cpp_std: "c++17", srcs: [ "InputReader_test.cpp", "InputDispatcher_test.cpp", @@ -9,6 +10,7 @@ cc_test { cflags: [ "-Wall", "-Werror", + "-Wextra", "-Wno-unused-parameter", ], shared_libs: [ diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index aa6df244e2..c6eaf9f048 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -28,7 +28,7 @@ static const nsecs_t ARBITRARY_TIME = 1234; static const int32_t DEVICE_ID = 1; // An arbitrary display id. -static const int32_t DISPLAY_ID = 0; +static const int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // An arbitrary injector pid / uid pair that has permission to inject events. static const int32_t INJECTOR_PID = 999; @@ -103,13 +103,20 @@ class InputDispatcherTest : public testing::Test { protected: sp<FakeInputDispatcherPolicy> mFakePolicy; sp<InputDispatcher> mDispatcher; + sp<InputDispatcherThread> mDispatcherThread; virtual void SetUp() { mFakePolicy = new FakeInputDispatcherPolicy(); mDispatcher = new InputDispatcher(mFakePolicy); + mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); + //Start InputDispatcher thread + mDispatcherThread = new InputDispatcherThread(mDispatcher); + mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY); } virtual void TearDown() { + mDispatcherThread->requestExit(); + mDispatcherThread.clear(); mFakePolicy.clear(); mDispatcher.clear(); } @@ -120,20 +127,20 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { KeyEvent event; // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject key events with ACTION_MULTIPLE."; } @@ -149,108 +156,530 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { } // Rejects undefined motion actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, /*action*/ -1, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with more than MAX_POINTERS pointers."; // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer ids greater than MAX_POINTER_ID."; // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with duplicate pointer ids."; } +// --- InputDispatcherTest SetInputWindowTest --- +static const int32_t INJECT_EVENT_TIMEOUT = 500; +static const int32_t DISPATCHING_TIMEOUT = 100; + +class FakeApplicationHandle : public InputApplicationHandle { +public: + FakeApplicationHandle() {} + virtual ~FakeApplicationHandle() {} + + virtual bool updateInfo() { + if (!mInfo) { + mInfo = new InputApplicationInfo(); + } + mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT; + return true; + } +}; + +class FakeInputReceiver { +public: + void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId, + int32_t expectedFlags = 0) { + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, + &consumeSeq, &event); + + ASSERT_EQ(OK, status) + << mName.c_str() << ": consumer consume should return OK."; + ASSERT_TRUE(event != nullptr) + << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(expectedEventType, event->getType()) + << mName.c_str() << ": event type should match."; + + ASSERT_EQ(expectedDisplayId, event->getDisplayId()) + << mName.c_str() << ": event displayId should be the same as expected."; + + int32_t flags; + switch (expectedEventType) { + case AINPUT_EVENT_TYPE_KEY: { + KeyEvent* typedEvent = static_cast<KeyEvent*>(event); + flags = typedEvent->getFlags(); + break; + } + case AINPUT_EVENT_TYPE_MOTION: { + MotionEvent* typedEvent = static_cast<MotionEvent*>(event); + flags = typedEvent->getFlags(); + break; + } + default: { + FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; + } + } + ASSERT_EQ(expectedFlags, flags) + << mName.c_str() << ": event flags should be the same as expected."; + + status = mConsumer->sendFinishedSignal(consumeSeq, handled()); + ASSERT_EQ(OK, status) + << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + } + + void assertNoEvents() { + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, + &consumeSeq, &event); + ASSERT_NE(OK, status) + << mName.c_str() + << ": should not have received any events, so consume(..) should not return OK."; + } + +protected: + explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, + const std::string name, int32_t displayId) : + mDispatcher(dispatcher), mName(name), mDisplayId(displayId) { + InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); + mConsumer = new InputConsumer(mClientChannel); + } + + virtual ~FakeInputReceiver() { + } + + // return true if the event has been handled. + virtual bool handled() { + return false; + } + + sp<InputDispatcher> mDispatcher; + sp<InputChannel> mServerChannel, mClientChannel; + InputConsumer *mConsumer; + PreallocatedInputEventFactory mEventFactory; + + std::string mName; + int32_t mDisplayId; +}; + +class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver { +public: + static const int32_t WIDTH = 600; + static const int32_t HEIGHT = 800; + + FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) : + InputWindowHandle(inputApplicationHandle), + FakeInputReceiver(dispatcher, name, displayId), + mFocused(false) { + mDispatcher->registerInputChannel(mServerChannel, this, displayId); + } + + virtual bool updateInfo() { + if (!mInfo) { + mInfo = new InputWindowInfo(); + } + mInfo->inputChannel = mServerChannel; + mInfo->name = mName; + mInfo->layoutParamsFlags = 0; + mInfo->layoutParamsType = InputWindowInfo::TYPE_APPLICATION; + mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo->frameLeft = 0; + mInfo->frameTop = 0; + mInfo->frameRight = WIDTH; + mInfo->frameBottom = HEIGHT; + mInfo->scaleFactor = 1.0; + mInfo->addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); + mInfo->visible = true; + mInfo->canReceiveKeys = true; + mInfo->hasFocus = mFocused; + mInfo->hasWallpaper = false; + mInfo->paused = false; + mInfo->layer = 0; + mInfo->ownerPid = INJECTOR_PID; + mInfo->ownerUid = INJECTOR_UID; + mInfo->inputFeatures = 0; + mInfo->displayId = mDisplayId; + + return true; + } + + void setFocus() { + mFocused = true; + } + + void assertNoEvents() { + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, + &consumeSeq, &event); + ASSERT_NE(OK, status) + << mName.c_str() + << ": should not have received any events, so consume(..) should not return OK."; + } +protected: + virtual bool handled() { + return true; + } + + bool mFocused; +}; + +static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + KeyEvent event; + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + + // Define a valid key down event. + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + AKEY_EVENT_ACTION_DOWN, /* flags */ 0, + AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); + + // Inject event until dispatch out. + return dispatcher->injectInputEvent( + &event, + INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); +} + +static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, + int32_t displayId) { + MotionEvent event; + PointerProperties pointerProperties[1]; + PointerCoords pointerCoords[1]; + + pointerProperties[0].clear(); + pointerProperties[0].id = 0; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200); + + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + // Define a valid motion down event. + event.initialize(DEVICE_ID, source, displayId, + AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0, + AMETA_NONE, /* buttonState */ 0, /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, + pointerCoords); + + // Inject event until dispatch out. + return dispatcher->injectInputEvent( + &event, + INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); +} + +TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", + ADISPLAY_ID_DEFAULT); + + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(window); + + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + + // Window should receive motion event. + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); +} + +// The foreground window should receive the first touch down event. +TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", + ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", + ADISPLAY_ID_DEFAULT); + + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(windowTop); + inputWindowHandles.add(windowSecond); + + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + + // Top window should receive the touch down event. Second window should not receive anything. + windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowSecond->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", + ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", + ADISPLAY_ID_DEFAULT); + + // Set focus application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + // Expect one focus window exist in display. + windowSecond->setFocus(); + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(windowTop); + inputWindowHandles.add(windowSecond); + + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + + // Focused window should receive event. + windowTop->assertNoEvents(); + windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); +} + +/* Test InputDispatcher for MultiDisplay */ +class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { +public: + static constexpr int32_t SECOND_DISPLAY_ID = 1; + virtual void SetUp() { + InputDispatcherTest::SetUp(); + + application1 = new FakeApplicationHandle(); + windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1", + ADISPLAY_ID_DEFAULT); + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.push(windowInPrimary); + // Set focus window for primary display, but focused display would be second one. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); + windowInPrimary->setFocus(); + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + + application2 = new FakeApplicationHandle(); + windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2", + SECOND_DISPLAY_ID); + // Set focus to second display window. + Vector<sp<InputWindowHandle>> inputWindowHandles_Second; + inputWindowHandles_Second.push(windowInSecondary); + // Set focus display to second one. + mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + // Set focus window for second display. + mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); + windowInSecondary->setFocus(); + mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID); + } + + virtual void TearDown() { + InputDispatcherTest::TearDown(); + + application1.clear(); + windowInPrimary.clear(); + application2.clear(); + windowInSecondary.clear(); + } + +protected: + sp<FakeApplicationHandle> application1; + sp<FakeWindowHandle> windowInPrimary; + sp<FakeApplicationHandle> application2; + sp<FakeWindowHandle> windowInSecondary; +}; + +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) { + // Test touch down on primary display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowInSecondary->assertNoEvents(); + + // Test touch down on second display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); +} + +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) { + // Test inject a key down with display id specified. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT); + windowInSecondary->assertNoEvents(); + + // Test inject a key down without display id specified. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + + // Remove secondary display. + Vector<sp<InputWindowHandle>> noWindows; + mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID); + + // Expect old focus should receive a cancel event. + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE, + AKEY_EVENT_FLAG_CANCELED); + + // Test inject a key down, should timeout because of no target window. + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; + windowInPrimary->assertNoEvents(); + windowInSecondary->assertNoEvents(); +} + +class FakeMonitorReceiver : public FakeInputReceiver, public RefBase { +public: + FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, + int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) { + mDispatcher->registerInputChannel(mServerChannel, nullptr, displayId); + } +}; + +// Test per-display input monitors for motion event. +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { + sp<FakeMonitorReceiver> monitorInPrimary = + new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + sp<FakeMonitorReceiver> monitorInSecondary = + new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // Test touch down on primary display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowInSecondary->assertNoEvents(); + monitorInSecondary->assertNoEvents(); + + // Test touch down on second display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + + // Test inject a non-pointer motion event. + // If specific a display, it will dispatch to the focused window of particular display, + // or it will dispatch to the focused window of focused display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); +} + +// Test per-display input monitors for key event. +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { + //Input monitor per display. + sp<FakeMonitorReceiver> monitorInPrimary = + new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + sp<FakeMonitorReceiver> monitorInSecondary = + new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // Test inject a key down. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); +} + } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 22f15a05fe..707f3c5040 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -33,7 +33,7 @@ static const int32_t DISPLAY_HEIGHT = 800; static const int32_t VIRTUAL_DISPLAY_ID = 1; static const int32_t VIRTUAL_DISPLAY_WIDTH = 400; static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500; -static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "Vr-display-unique-ID"; +static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1"; // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; @@ -132,6 +132,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { InputReaderConfiguration mConfig; KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers; Vector<InputDeviceInfo> mInputDevices; + std::vector<DisplayViewport> mViewports; TouchAffineTransformation transform; protected: @@ -141,23 +142,26 @@ public: FakeInputReaderPolicy() { } - void setDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, - const String8& uniqueId) { - DisplayViewport v = createDisplayViewport(displayId, width, height, orientation, uniqueId); - // Set the size of both the internal and external display at the same time. - mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, v); - mConfig.setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, v); + virtual void clearViewports() { + mViewports.clear(); + mConfig.setDisplayViewports(mViewports); } - void setVirtualDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, - const String8& uniqueId) { - Vector<DisplayViewport> viewports; - viewports.push_back(createDisplayViewport(displayId, width, height, orientation, uniqueId)); - mConfig.setVirtualDisplayViewports(viewports); + std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType, + const std::string& uniqueId) { + return mConfig.getDisplayViewport(viewportType, uniqueId); } - void addExcludedDeviceName(const String8& deviceName) { - mConfig.excludedDeviceNames.push(deviceName); + void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, + const std::string& uniqueId, ViewportType viewportType) { + const DisplayViewport viewport = createDisplayViewport(displayId, width, height, + orientation, uniqueId, viewportType); + mViewports.push_back(viewport); + mConfig.setDisplayViewports(mViewports); + } + + void addExcludedDeviceName(const std::string& deviceName) { + mConfig.excludedDeviceNames.push_back(deviceName); } void addDisabledDevice(int32_t deviceId) { @@ -188,7 +192,7 @@ public: return mInputDevices; } - TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor, + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, int32_t surfaceRotation) { return transform; } @@ -203,7 +207,7 @@ public: private: DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, const String8& uniqueId) { + int32_t orientation, const std::string& uniqueId, ViewportType type) { bool isRotated = (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270); DisplayViewport v; @@ -220,6 +224,7 @@ private: v.deviceWidth = isRotated ? height : width; v.deviceHeight = isRotated ? width : height; v.uniqueId = uniqueId; + v.type = type; return v; } @@ -236,11 +241,11 @@ private: } virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) { - return NULL; + return nullptr; } - virtual String8 getDeviceAlias(const InputDeviceIdentifier&) { - return String8::empty(); + virtual std::string getDeviceAlias(const InputDeviceIdentifier&) { + return ""; } }; @@ -263,7 +268,7 @@ public: } void assertNotifyConfigurationChangedWasCalled( - NotifyConfigurationChangedArgs* outEventArgs = NULL) { + NotifyConfigurationChangedArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty()) << "Expected notifyConfigurationChanged() to have been called."; if (outEventArgs) { @@ -278,7 +283,7 @@ public: } void assertNotifyDeviceResetWasCalled( - NotifyDeviceResetArgs* outEventArgs = NULL) { + NotifyDeviceResetArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty()) << "Expected notifyDeviceReset() to have been called."; if (outEventArgs) { @@ -292,7 +297,7 @@ public: << "Expected notifyDeviceReset() to not have been called."; } - void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) { + void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called."; if (outEventArgs) { @@ -306,7 +311,7 @@ public: << "Expected notifyKey() to not have been called."; } - void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) { + void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called."; if (outEventArgs) { @@ -320,7 +325,7 @@ public: << "Expected notifyMotion() to not have been called."; } - void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) { + void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifySwitchArgsQueue.empty()) << "Expected notifySwitch() to have been called."; if (outEventArgs) { @@ -392,7 +397,7 @@ class FakeEventHub : public EventHubInterface { }; KeyedVector<int32_t, Device*> mDevices; - Vector<String8> mExcludedDevices; + std::vector<std::string> mExcludedDevices; List<RawEvent> mEvents; protected: @@ -405,7 +410,7 @@ protected: public: FakeEventHub() { } - void addDevice(int32_t deviceId, const String8& name, uint32_t classes) { + void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) { Device* device = new Device(classes); device->identifier.name = name; mDevices.add(deviceId, device); @@ -422,7 +427,7 @@ public: bool isDeviceEnabled(int32_t deviceId) { Device* device = getDevice(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); return false; } @@ -432,7 +437,7 @@ public: status_t enableDevice(int32_t deviceId) { status_t result; Device* device = getDevice(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); return BAD_VALUE; } @@ -446,7 +451,7 @@ public: status_t disableDevice(int32_t deviceId) { Device* device = getDevice(deviceId); - if (device == NULL) { + if (device == nullptr) { ALOGE("Incorrect device id=%" PRId32 " provided to %s", deviceId, __func__); return BAD_VALUE; } @@ -534,7 +539,7 @@ public: return device->leds.valueFor(led); } - Vector<String8>& getExcludedDevices() { + std::vector<std::string>& getExcludedDevices() { return mExcludedDevices; } @@ -566,7 +571,7 @@ public: private: Device* getDevice(int32_t deviceId) const { ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : NULL; + return index >= 0 ? mDevices.valueAt(index) : nullptr; } virtual uint32_t getDeviceClasses(int32_t deviceId) const { @@ -651,14 +656,14 @@ private: return &device->keysByScanCode.valueAt(index); } } - return NULL; + return nullptr; } virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const { return NAME_NOT_FOUND; } - virtual void setExcludedDevices(const Vector<String8>& devices) { + virtual void setExcludedDevices(const std::vector<std::string>& devices) { mExcludedDevices = devices; } @@ -781,7 +786,7 @@ private: } virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const { - return NULL; + return nullptr; } virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) { @@ -940,7 +945,7 @@ public: mResetWasCalled = false; } - void assertProcessWasCalled(RawEvent* outLastEvent = NULL) { + void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) { ASSERT_TRUE(mProcessWasCalled) << "Expected process() to have been called."; if (outLastEvent) { @@ -1039,7 +1044,7 @@ public: const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : InputReader(eventHub, policy, listener), - mNextDevice(NULL) { + mNextDevice(nullptr) { } virtual ~InstrumentedInputReader() { @@ -1052,7 +1057,7 @@ public: mNextDevice = device; } - InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const String8& name, + InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name, uint32_t classes) { InputDeviceIdentifier identifier; identifier.name = name; @@ -1066,7 +1071,7 @@ protected: const InputDeviceIdentifier& identifier, uint32_t classes) { if (mNextDevice) { InputDevice* device = mNextDevice; - mNextDevice = NULL; + mNextDevice = nullptr; return device; } return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes); @@ -1075,6 +1080,146 @@ protected: friend class InputReaderTest; }; +// --- InputReaderPolicyTest --- +class InputReaderPolicyTest : public testing::Test { + protected: + sp<FakeInputReaderPolicy> mFakePolicy; + + virtual void SetUp() { + mFakePolicy = new FakeInputReaderPolicy(); + } + virtual void TearDown() { + mFakePolicy.clear(); + } +}; + +/** + * Check that empty set of viewports is an acceptable configuration. + * Also try to get internal viewport two different ways - by type and by uniqueId. + * + * There will be confusion if two viewports with empty uniqueId and identical type are present. + * Such configuration is not currently allowed. + */ +TEST_F(InputReaderPolicyTest, Viewports_GetCleared) { + const std::string uniqueId = "local:0"; + + // We didn't add any viewports yet, so there shouldn't be any. + std::optional<DisplayViewport> internalViewport = + mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId); + ASSERT_FALSE(internalViewport); + + // Add an internal viewport, then clear it + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId, ViewportType::VIEWPORT_INTERNAL); + + // Check matching by uniqueId + internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId); + ASSERT_TRUE(internalViewport); + + // Check matching by viewport type + internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); + ASSERT_TRUE(internalViewport); + + mFakePolicy->clearViewports(); + // Make sure nothing is found after clear + internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId); + ASSERT_FALSE(internalViewport); + internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); + ASSERT_FALSE(internalViewport); +} + +TEST_F(InputReaderPolicyTest, Viewports_GetByType) { + const std::string internalUniqueId = "local:0"; + const std::string externalUniqueId = "local:1"; + const std::string virtualUniqueId1 = "virtual:2"; + const std::string virtualUniqueId2 = "virtual:3"; + constexpr int32_t virtualDisplayId1 = 2; + constexpr int32_t virtualDisplayId2 = 3; + + // Add an internal viewport + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, internalUniqueId, ViewportType::VIEWPORT_INTERNAL); + // Add an external viewport + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, externalUniqueId, ViewportType::VIEWPORT_EXTERNAL); + // Add an virtual viewport + mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, virtualUniqueId1, ViewportType::VIEWPORT_VIRTUAL); + // Add another virtual viewport + mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, virtualUniqueId2, ViewportType::VIEWPORT_VIRTUAL); + + // Check matching by type for internal + std::optional<DisplayViewport> internalViewport = + mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, ""); + ASSERT_TRUE(internalViewport); + ASSERT_EQ(internalUniqueId, internalViewport->uniqueId); + + // Check matching by type for external + std::optional<DisplayViewport> externalViewport = + mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, ""); + ASSERT_TRUE(externalViewport); + ASSERT_EQ(externalUniqueId, externalViewport->uniqueId); + + // Check matching by uniqueId for virtual viewport #1 + std::optional<DisplayViewport> virtualViewport1 = + mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId1); + ASSERT_TRUE(virtualViewport1); + ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId); + ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId); + + // Check matching by uniqueId for virtual viewport #2 + std::optional<DisplayViewport> virtualViewport2 = + mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId2); + ASSERT_TRUE(virtualViewport2); + ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId); + ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId); +} + + +/** + * We can have 2 viewports of the same kind. We can distinguish them by uniqueId, and confirm + * that lookup works by checking display id. + * Check that 2 viewports of each kind is possible, for all existing viewport types. + */ +TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) { + const std::string uniqueId1 = "uniqueId1"; + const std::string uniqueId2 = "uniqueId2"; + constexpr int32_t displayId1 = 2; + constexpr int32_t displayId2 = 3; + + std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL, + ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL}; + for (const ViewportType& type : types) { + mFakePolicy->clearViewports(); + // Add a viewport + mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId1, type); + // Add another viewport + mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId2, type); + + // Check that correct display viewport was returned by comparing the display IDs. + std::optional<DisplayViewport> viewport1 = mFakePolicy->getDisplayViewport(type, uniqueId1); + ASSERT_TRUE(viewport1); + ASSERT_EQ(displayId1, viewport1->displayId); + ASSERT_EQ(type, viewport1->type); + + std::optional<DisplayViewport> viewport2 = mFakePolicy->getDisplayViewport(type, uniqueId2); + ASSERT_TRUE(viewport2); + ASSERT_EQ(displayId2, viewport2->displayId); + ASSERT_EQ(type, viewport2->type); + + // When there are multiple viewports of the same kind, and uniqueId is not specified + // in the call to getDisplayViewport, then that situation is not supported. + // The viewports can be stored in any order, so we cannot rely on the order, since that + // is just implementation detail. + // However, we can check that it still returns *a* viewport, we just cannot assert + // which one specifically is returned. + std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewport(type, ""); + ASSERT_TRUE(someViewport); + } +} // --- InputReaderTest --- @@ -1101,7 +1246,7 @@ protected: mFakeEventHub.clear(); } - void addDevice(int32_t deviceId, const String8& name, uint32_t classes, + void addDevice(int32_t deviceId, const std::string& name, uint32_t classes, const PropertyMap* configuration) { mFakeEventHub->addDevice(deviceId, name, classes); @@ -1129,7 +1274,7 @@ protected: } FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber, - const String8& name, uint32_t classes, uint32_t sources, + const std::string& name, uint32_t classes, uint32_t sources, const PropertyMap* configuration) { InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes); FakeInputMapper* mapper = new FakeInputMapper(device, sources); @@ -1141,17 +1286,18 @@ protected: }; TEST_F(InputReaderTest, GetInputDevices) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD, NULL)); - ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("ignored"), - 0, NULL)); // no classes so device will be ignored + ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", + INPUT_DEVICE_CLASS_KEYBOARD, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", + 0, nullptr)); // no classes so device will be ignored + Vector<InputDeviceInfo> inputDevices; mReader->getInputDevices(inputDevices); ASSERT_EQ(1U, inputDevices.size()); ASSERT_EQ(1, inputDevices[0].getId()); - ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string()); + ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); @@ -1160,7 +1306,7 @@ TEST_F(InputReaderTest, GetInputDevices) { inputDevices = mFakePolicy->getInputDevices(); ASSERT_EQ(1U, inputDevices.size()); ASSERT_EQ(1, inputDevices[0].getId()); - ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.string()); + ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); @@ -1169,14 +1315,14 @@ TEST_F(InputReaderTest, GetInputDevices) { TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { constexpr int32_t deviceId = 1; constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; - InputDevice* device = mReader->newDevice(deviceId, 0, String8("fake"), deviceClass); + InputDevice* device = mReader->newDevice(deviceId, 0, "fake", deviceClass); // Must add at least one mapper or the device will be ignored! FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD); device->addMapper(mapper); mReader->setNextDevice(device); - addDevice(deviceId, String8("fake"), deviceClass, NULL); + addDevice(deviceId, "fake", deviceClass, nullptr); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(NULL)); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); NotifyDeviceResetArgs resetArgs; ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); @@ -1207,9 +1353,9 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { } TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL)); + FakeInputMapper* mapper = nullptr; + ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", + INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0, @@ -1234,9 +1380,9 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL)); + FakeInputMapper* mapper = nullptr; + ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", + INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0, @@ -1261,9 +1407,9 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL)); + FakeInputMapper* mapper = nullptr; + ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", + INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN); ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0, @@ -1288,9 +1434,10 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL)); + FakeInputMapper* mapper = nullptr; + ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", + INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); + mapper->addSupportedKeyCode(AKEYCODE_A); mapper->addSupportedKeyCode(AKEYCODE_B); @@ -1323,7 +1470,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { } TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL); + addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1332,9 +1479,9 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange } TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL)); + FakeInputMapper* mapper = nullptr; + ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake", + INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr)); mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1); mReader->loopOnce(); @@ -1373,7 +1520,7 @@ protected: mFakeListener = new FakeInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); - mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); + mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, @@ -1399,7 +1546,7 @@ const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(DEVICE_ID, mDevice->getId()); - ASSERT_STREQ(DEVICE_NAME, mDevice->getName()); + ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str()); ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses()); } @@ -1427,7 +1574,7 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { InputDeviceInfo info; mDevice->getDeviceInfo(&info); ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string()); + ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources()); @@ -1497,7 +1644,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe InputDeviceInfo info; mDevice->getDeviceInfo(&info); ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.string()); + ASSERT_STREQ(DEVICE_NAME, info.getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType()); ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources()); @@ -1570,7 +1717,7 @@ protected: mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); - mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); + mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); } virtual void TearDown() { @@ -1596,15 +1743,14 @@ protected: } void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, - int32_t orientation) { - mFakePolicy->setDisplayViewport(displayId, width, height, orientation, String8::empty()); + int32_t orientation, const std::string& uniqueId, ViewportType viewportType) { + mFakePolicy->addDisplayViewport( + displayId, width, height, orientation, uniqueId, viewportType); configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); } - void setVirtualDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height, - int32_t orientation, const String8& uniqueId) { - mFakePolicy->setVirtualDisplayViewport(displayId, width, height, orientation, uniqueId); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + void clearViewports() { + mFakePolicy->clearViewports(); } static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type, @@ -1621,7 +1767,7 @@ protected: static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) { const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source); - ASSERT_TRUE(range != NULL) << "Axis: " << axis << " Source: " << source; + ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source; ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source; ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source; ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source; @@ -1708,12 +1854,24 @@ TEST_F(SwitchInputMapperTest, Process) { class KeyboardInputMapperTest : public InputMapperTest { protected: + const std::string UNIQUE_ID = "local:0"; + + void prepareDisplay(int32_t orientation); + void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode); }; +/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the + * orientation. + */ +void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + orientation, UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL); +} + void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t, int32_t rotatedKeyCode) { + int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) { NotifyKeyArgs args; process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, 1); @@ -1901,9 +2059,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); addMapperAndConfigure(mapper); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_90); + prepareDisplay(DISPLAY_ORIENTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1925,9 +2081,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { addConfigurationProperty("keyboard.orientationAware", "1"); addMapperAndConfigure(mapper); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0); + prepareDisplay(DISPLAY_ORIENTATION_0); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1937,9 +2091,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_90); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_90); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1949,9 +2102,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_180); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_180); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1961,9 +2113,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_270); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_270); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, @@ -1976,19 +2127,16 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { // Special case: if orientation changes while key is down, we still emit the same keycode // in the key up as we did in the key down. NotifyKeyArgs args; - - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_270); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_270); process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); ASSERT_EQ(KEY_UP, args.scanCode); ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); - setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_180); + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_180); process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); @@ -1996,6 +2144,64 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); } +TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) { + // If the keyboard is not orientation aware, + // key events should not be associated with a specific display id + mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, + AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addMapperAndConfigure(mapper); + NotifyKeyArgs args; + + // Display id should be ADISPLAY_ID_NONE without any display configuration. + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId); + + prepareDisplay(DISPLAY_ORIENTATION_0); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId); +} + +TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { + // If the keyboard is orientation aware, + // key events should be associated with the internal viewport + mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, + AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addConfigurationProperty("keyboard.orientationAware", "1"); + addMapperAndConfigure(mapper); + NotifyKeyArgs args; + + // Display id should be ADISPLAY_ID_NONE without any display configuration. + // ^--- already checked by the previous test + + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(DISPLAY_ID, args.displayId); + + constexpr int32_t newDisplayId = 2; + clearViewports(); + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(newDisplayId, args.displayId); +} + TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); @@ -2174,8 +2380,8 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF mapper->populateDeviceInfo(&info); // Initially there may not be a valid motion range. - ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); - ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); @@ -2419,9 +2625,12 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMot addConfigurationProperty("cursor.mode", "navigation"); addMapperAndConfigure(mapper); + const std::string uniqueId = "local:0"; + const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL; + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_90); + DISPLAY_ORIENTATION_90, uniqueId, viewportType); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); @@ -2438,8 +2647,11 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) addConfigurationProperty("cursor.orientationAware", "1"); addMapperAndConfigure(mapper); + const std::string uniqueId = "local:0"; + const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL; + setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0); + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, uniqueId, viewportType); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); @@ -2450,7 +2662,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90); + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90, uniqueId, viewportType); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); @@ -2461,7 +2673,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180); + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180, uniqueId, viewportType); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); @@ -2472,7 +2684,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); setDisplayInfoAndReconfigure(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270); + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270, uniqueId, viewportType); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); @@ -2917,6 +3129,8 @@ protected: static const VirtualKeyDefinition VIRTUAL_KEYS[2]; + const std::string UNIQUE_ID = "local:0"; + enum Axes { POSITION = 1 << 0, TOUCH = 1 << 1, @@ -2985,12 +3199,14 @@ const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { }; void TouchInputMapperTest::prepareDisplay(int32_t orientation) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation); + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, + UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL); } void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) { - setVirtualDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, - VIRTUAL_DISPLAY_HEIGHT, orientation, String8(VIRTUAL_DISPLAY_UNIQUE_ID)); + setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, + VIRTUAL_DISPLAY_HEIGHT, orientation, + VIRTUAL_DISPLAY_UNIQUE_ID, ViewportType::VIEWPORT_VIRTUAL); } void TouchInputMapperTest::prepareVirtualKeys() { @@ -3719,6 +3935,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) NotifyMotionArgs args; // Rotation 0. + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_0); processDown(mapper, toRawX(50), toRawY(75)); processSync(mapper); @@ -3732,6 +3949,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); // Rotation 90. + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50)); processSync(mapper); @@ -3745,6 +3963,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); // Rotation 180. + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_180); processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN); processSync(mapper); @@ -3758,6 +3977,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled()); // Rotation 270. + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN); processSync(mapper); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index a7f3a5235e..f87fcdc437 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -46,7 +46,9 @@ cc_library_shared { "libhidlbase", "libhidltransport", "libhwbinder", + "libfmq", "android.hardware.sensors@1.0", + "android.hardware.sensors@2.0", ], static_libs: ["android.hardware.sensors@1.0-convert"], diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp index d8e5b290d0..14f9a12553 100644 --- a/services/sensorservice/BatteryService.cpp +++ b/services/sensorservice/BatteryService.cpp @@ -94,7 +94,7 @@ void BatteryService::cleanupImpl(uid_t uid) { bool BatteryService::checkService() { if (mBatteryStatService == nullptr) { const sp<IServiceManager> sm(defaultServiceManager()); - if (sm != NULL) { + if (sm != nullptr) { const String16 name("batterystats"); mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name)); } diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp index 1025a88eaf..207b09711a 100644 --- a/services/sensorservice/RecentEventLogger.cpp +++ b/services/sensorservice/RecentEventLogger.cpp @@ -26,24 +26,32 @@ namespace SensorServiceUtil { namespace { constexpr size_t LOG_SIZE = 10; + constexpr size_t LOG_SIZE_MED = 30; // debugging for slower sensors constexpr size_t LOG_SIZE_LARGE = 50; // larger samples for debugging }// unnamed namespace RecentEventLogger::RecentEventLogger(int sensorType) : mSensorType(sensorType), mEventSize(eventSizeBySensorType(mSensorType)), - mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false) { + mRecentEvents(logSizeBySensorType(sensorType)), mMaskData(false), + mIsLastEventCurrent(false) { // blank } void RecentEventLogger::addEvent(const sensors_event_t& event) { std::lock_guard<std::mutex> lk(mLock); mRecentEvents.emplace(event); + mIsLastEventCurrent = true; } bool RecentEventLogger::isEmpty() const { return mRecentEvents.size() == 0; } +void RecentEventLogger::setLastEventStale() { + std::lock_guard<std::mutex> lk(mLock); + mIsLastEventCurrent = false; +} + std::string RecentEventLogger::dump() const { std::lock_guard<std::mutex> lk(mLock); @@ -84,10 +92,10 @@ void RecentEventLogger::setFormat(std::string format) { } } -bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const { +bool RecentEventLogger::populateLastEventIfCurrent(sensors_event_t *event) const { std::lock_guard<std::mutex> lk(mLock); - if (mRecentEvents.size()) { + if (mIsLastEventCurrent && mRecentEvents.size()) { // Index 0 contains the latest event emplace()'ed *event = mRecentEvents[0].mEvent; return true; @@ -98,10 +106,16 @@ bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const { size_t RecentEventLogger::logSizeBySensorType(int sensorType) { - return (sensorType == SENSOR_TYPE_STEP_COUNTER || - sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION || - sensorType == SENSOR_TYPE_ACCELEROMETER || - sensorType == SENSOR_TYPE_LIGHT) ? LOG_SIZE_LARGE : LOG_SIZE; + if (sensorType == SENSOR_TYPE_STEP_COUNTER || + sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION || + sensorType == SENSOR_TYPE_ACCELEROMETER || + sensorType == SENSOR_TYPE_LIGHT) { + return LOG_SIZE_LARGE; + } + if (sensorType == SENSOR_TYPE_PROXIMITY) { + return LOG_SIZE_MED; + } + return LOG_SIZE; } RecentEventLogger::SensorEventLog::SensorEventLog(const sensors_event_t& e) : mEvent(e) { diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h index bf1f655704..67378b7dd6 100644 --- a/services/sensorservice/RecentEventLogger.h +++ b/services/sensorservice/RecentEventLogger.h @@ -37,8 +37,13 @@ class RecentEventLogger : public Dumpable { public: explicit RecentEventLogger(int sensorType); void addEvent(const sensors_event_t& event); - bool populateLastEvent(sensors_event_t *event) const; + + // Populate event with the last recorded sensor event if it is not stale. An event is + // considered stale if the sensor has become deactivated since the event was recorded. + // returns true on success, false if no recent event is available or the last event is stale + bool populateLastEventIfCurrent(sensors_event_t *event) const; bool isEmpty() const; + void setLastEventStale(); virtual ~RecentEventLogger() {} // Dumpable interface @@ -59,6 +64,7 @@ protected: RingBuffer<SensorEventLog> mRecentEvents; bool mMaskData; + bool mIsLastEventCurrent; private: static size_t logSizeBySensorType(int sensorType); diff --git a/services/sensorservice/RotationVectorSensor.cpp b/services/sensorservice/RotationVectorSensor.cpp index 7b00f4d98d..f2ea02eb00 100644 --- a/services/sensorservice/RotationVectorSensor.cpp +++ b/services/sensorservice/RotationVectorSensor.cpp @@ -94,7 +94,7 @@ const char* RotationVectorSensor::getSensorName() const { return "GeoMag Rotation Vector Sensor"; default: assert(0); - return NULL; + return nullptr; } } diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 115a983bc4..f2336cbd24 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -13,7 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "SensorDevice.h" + +#include "android/hardware/sensors/2.0/ISensorsCallback.h" +#include "android/hardware/sensors/2.0/types.h" #include "SensorService.h" #include <android-base/logging.h> @@ -26,9 +30,13 @@ #include <cinttypes> #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::sensors::V2_0::ISensorsCallback; +using android::hardware::sensors::V2_0::EventQueueFlagBits; using android::hardware::hidl_vec; +using android::hardware::Return; using android::SensorDeviceUtils::HidlServiceRegistrationWaiter; namespace android { @@ -51,12 +59,53 @@ static status_t StatusFromResult(Result result) { } } +template<typename EnumType> +constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) { + return static_cast<typename std::underlying_type<EnumType>::type>(value); +} + +// 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, +}; + +void SensorsHalDeathReceivier::serviceDied( + uint64_t /* cookie */, + const wp<::android::hidl::base::V1_0::IBase>& /* service */) { + ALOGW("Sensors HAL died, attempting to reconnect."); + SensorDevice::getInstance().prepareForReconnect(); +} + +struct SensorsCallback : public ISensorsCallback { + using Result = ::android::hardware::sensors::V1_0::Result; + Return<void> onDynamicSensorsConnected( + const hidl_vec<SensorInfo> &dynamicSensorsAdded) override { + return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded); + } + + Return<void> onDynamicSensorsDisconnected( + const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override { + return SensorDevice::getInstance().onDynamicSensorsDisconnected( + dynamicSensorHandlesRemoved); + } +}; + SensorDevice::SensorDevice() - : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) { + : mHidlTransportErrors(20), + mRestartWaiter(new HidlServiceRegistrationWaiter()), + mReconnecting(false) { if (!connectHidlService()) { return; } + initializeSensorList(); + + mIsDirectReportSupported = + (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION); +} + +void SensorDevice::initializeSensorList() { float minPowerMa = 0.001; // 1 microAmp checkReturn(mSensors->getSensorsList( @@ -81,37 +130,197 @@ SensorDevice::SensorDevice() checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */)); } })); +} - mIsDirectReportSupported = - (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION); +SensorDevice::~SensorDevice() { + if (mEventQueueFlag != nullptr) { + hardware::EventFlag::deleteEventFlag(&mEventQueueFlag); + mEventQueueFlag = nullptr; + } } bool SensorDevice::connectHidlService() { + HalConnectionStatus status = connectHidlServiceV2_0(); + if (status == HalConnectionStatus::DOES_NOT_EXIST) { + status = connectHidlServiceV1_0(); + } + return (status == HalConnectionStatus::CONNECTED); +} + +SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() { // SensorDevice will wait for HAL service to start if HAL is declared in device manifest. size_t retry = 10; + HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN; while (retry-- > 0) { - mSensors = ISensors::getService(); - if (mSensors == nullptr) { + sp<V1_0::ISensors> sensors = V1_0::ISensors::getService(); + if (sensors == nullptr) { // no sensor hidl service found + connectionStatus = HalConnectionStatus::DOES_NOT_EXIST; break; } + mSensors = new SensorServiceUtil::SensorsWrapperV1_0(sensors); mRestartWaiter->reset(); // 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()) { // ok to continue + connectionStatus = HalConnectionStatus::CONNECTED; break; } // hidl service is restarting, pointer is invalid. mSensors = nullptr; + connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT; ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry); mRestartWaiter->wait(); } - return (mSensors != nullptr); + + return connectionStatus; +} + +SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() { + HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN; + sp<V2_0::ISensors> sensors = V2_0::ISensors::getService(); + + if (sensors == nullptr) { + connectionStatus = HalConnectionStatus::DOES_NOT_EXIST; + } else { + mSensors = new SensorServiceUtil::SensorsWrapperV2_0(sensors); + + mEventQueue = std::make_unique<EventMessageQueue>( + SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, + true /* configureEventFlagWord */); + + mWakeLockQueue = std::make_unique<WakeLockQueue>( + SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT, + true /* configureEventFlagWord */); + + hardware::EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag); + + CHECK(mSensors != nullptr && mEventQueue != nullptr && + mWakeLockQueue != nullptr && mEventQueueFlag != nullptr); + + status_t status = StatusFromResult(checkReturn(mSensors->initialize( + *mEventQueue->getDesc(), + *mWakeLockQueue->getDesc(), + new SensorsCallback()))); + + if (status != NO_ERROR) { + connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT; + ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status)); + } else { + connectionStatus = HalConnectionStatus::CONNECTED; + mSensorsHalDeathReceiver = new SensorsHalDeathReceivier(); + sensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */); + } + } + + return connectionStatus; +} + +void SensorDevice::prepareForReconnect() { + mReconnecting = true; + + // Wake up the polling thread so it returns and allows the SensorService to initiate + // a reconnect. + mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE)); +} + +void SensorDevice::reconnect() { + Mutex::Autolock _l(mLock); + mSensors = nullptr; + + auto previousActivations = mActivationCount; + auto previousSensorList = mSensorList; + + mActivationCount.clear(); + mSensorList.clear(); + + if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) { + initializeSensorList(); + + if (sensorHandlesChanged(previousSensorList, mSensorList)) { + LOG_ALWAYS_FATAL("Sensor handles changed, cannot re-enable sensors."); + } else { + reactivateSensors(previousActivations); + } + } + mReconnecting = false; +} + +bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, + const Vector<sensor_t>& newSensorList) { + bool didChange = false; + + if (oldSensorList.size() != newSensorList.size()) { + didChange = true; + } + + for (size_t i = 0; i < newSensorList.size() && !didChange; i++) { + bool found = false; + const sensor_t& newSensor = newSensorList[i]; + for (size_t j = 0; j < oldSensorList.size() && !found; j++) { + const sensor_t& prevSensor = oldSensorList[j]; + if (prevSensor.handle == newSensor.handle) { + found = true; + if (!sensorIsEquivalent(prevSensor, newSensor)) { + didChange = true; + } + } + } + + if (!found) { + // Could not find the new sensor in the old list of sensors, the lists must + // have changed. + didChange = true; + } + } + return didChange; +} + +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)) { + equivalent = false; + } + return equivalent; +} + +void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations) { + for (size_t i = 0; i < mSensorList.size(); i++) { + int handle = mSensorList[i].handle; + ssize_t activationIndex = previousActivations.indexOfKey(handle); + if (activationIndex < 0 || previousActivations[activationIndex].numActiveClients() <= 0) { + continue; + } + + const Info& info = previousActivations[activationIndex]; + 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); + + if (res == NO_ERROR) { + activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */); + } + } + } } void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) { @@ -173,6 +382,19 @@ status_t SensorDevice::initCheck() const { ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { if (mSensors == nullptr) return NO_INIT; + ssize_t eventsRead = 0; + if (mSensors->supportsMessageQueues()) { + eventsRead = pollFmq(buffer, count); + } else if (mSensors->supportsPolling()) { + eventsRead = pollHal(buffer, count); + } else { + ALOGE("Must support polling or FMQ"); + eventsRead = -1; + } + return eventsRead; +} + +ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { ssize_t err; int numHidlTransportErrors = 0; bool hidlTransportError = false; @@ -208,7 +430,7 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { if(numHidlTransportErrors > 0) { ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors); - HidlTransportErrorLog errLog(time(NULL), numHidlTransportErrors); + HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors); mHidlTransportErrors.add(errLog); mTotalHidlTransportErrors++; } @@ -216,6 +438,82 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { return err; } +ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) { + ssize_t eventsRead = 0; + size_t availableEvents = mEventQueue->availableToRead(); + + if (availableEvents == 0) { + uint32_t eventFlagState = 0; + + // Wait for events to become available. This is necessary so that the Event FMQ's read() is + // able to be called with the correct number of events to read. If the specified number of + // 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); + availableEvents = mEventQueue->availableToRead(); + + if ((eventFlagState & asBaseType(EventQueueFlagBits::READ_AND_PROCESS)) && + availableEvents == 0) { + ALOGW("Event FMQ wake without any events"); + } + + if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { + ALOGD("Event FMQ internal wake, returning from poll with no events"); + return DEAD_OBJECT; + } + } + + size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()}); + if (eventsToRead > 0) { + if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) { + for (size_t i = 0; i < eventsToRead; i++) { + convertToSensorEvent(mEventBuffer[i], &buffer[i]); + } + eventsRead = eventsToRead; + } else { + ALOGW("Failed to read %zu events, currently %zu events available", + eventsToRead, availableEvents); + } + } + + return eventsRead; +} + +Return<void> SensorDevice::onDynamicSensorsConnected( + const hidl_vec<SensorInfo> &dynamicSensorsAdded) { + // 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]; + + auto it = mConnectedDynamicSensors.find(info.sensorHandle); + CHECK(it == mConnectedDynamicSensors.end()); + + sensor_t *sensor = new sensor_t(); + convertToSensor(info, sensor); + + mConnectedDynamicSensors.insert( + std::make_pair(sensor->handle, sensor)); + } + + return Return<void>(); +} + +Return<void> SensorDevice::onDynamicSensorsDisconnected( + const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) { + (void) dynamicSensorHandlesRemoved; + // TODO: Currently dynamic sensors do not seem to be removed + return Return<void>(); +} + +void SensorDevice::writeWakeLockHandled(uint32_t count) { + if (mSensors != nullptr && mSensors->supportsMessageQueues() && + !mWakeLockQueue->write(&count)) { + ALOGW("Failed to write wake lock handled"); + } +} + void SensorDevice::autoDisable(void *ident, int handle) { Mutex::Autolock _l(mLock); ssize_t activationIndex = mActivationCount.indexOfKey(handle); @@ -230,10 +528,15 @@ void SensorDevice::autoDisable(void *ident, int handle) { status_t SensorDevice::activate(void* ident, int handle, int enabled) { if (mSensors == nullptr) return NO_INIT; - status_t err(NO_ERROR); + Mutex::Autolock _l(mLock); + return activateLocked(ident, handle, enabled); +} + +status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { bool actuateHardware = false; - Mutex::Autolock _l(mLock); + status_t err(NO_ERROR); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { ALOGW("Handle %d cannot be found in activation record", handle); @@ -333,6 +636,11 @@ status_t SensorDevice::batch( ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); + return batchLocked(ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); +} + +status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { ALOGW("Handle %d cannot be found in activation record", handle); @@ -563,7 +871,7 @@ int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, // --------------------------------------------------------------------------- -int SensorDevice::Info::numActiveClients() { +int SensorDevice::Info::numActiveClients() const { SensorDevice& device(SensorDevice::getInstance()); int num = 0; for (size_t i = 0; i < batchParams.size(); ++i) { @@ -651,19 +959,9 @@ void SensorDevice::convertToSensorEvents( const hidl_vec<Event> &src, const hidl_vec<SensorInfo> &dynamicSensorsAdded, sensors_event_t *dst) { - // 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]; - - auto it = mConnectedDynamicSensors.find(info.sensorHandle); - CHECK(it == mConnectedDynamicSensors.end()); - - sensor_t *sensor = new sensor_t; - convertToSensor(info, sensor); - mConnectedDynamicSensors.insert( - std::make_pair(sensor->handle, sensor)); + if (dynamicSensorsAdded.size() > 0) { + onDynamicSensorsConnected(dynamicSensorsAdded); } for (size_t i = 0; i < src.size(); ++i) { @@ -672,8 +970,12 @@ void SensorDevice::convertToSensorEvents( } void SensorDevice::handleHidlDeath(const std::string & detail) { - // restart is the only option at present. - LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); + if (!SensorDevice::getInstance().mSensors->supportsMessageQueues()) { + // restart is the only option at present. + LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); + } else { + ALOGD("ISensors HAL died, death recipient will attempt reconnect"); + } } // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 6d7505158d..e1024ac1f7 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -19,20 +19,22 @@ #include "SensorDeviceUtils.h" #include "SensorServiceUtils.h" +#include "SensorsWrapper.h" +#include <fmq/MessageQueue.h> +#include <sensor/SensorEventQueue.h> #include <sensor/Sensor.h> #include <stdint.h> #include <sys/types.h> #include <utils/KeyedVector.h> #include <utils/Singleton.h> #include <utils/String8.h> +#include <utils/Timers.h> #include <string> #include <unordered_map> #include <algorithm> //std::max std::min -#include "android/hardware/sensors/1.0/ISensors.h" - #include "RingBuffer.h" // --------------------------------------------------------------------------- @@ -40,8 +42,13 @@ namespace android { // --------------------------------------------------------------------------- +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: @@ -69,6 +76,10 @@ public: int mCount; // number of transport errors observed }; + ~SensorDevice(); + void prepareForReconnect(); + void reconnect(); + ssize_t getSensorList(sensor_t const** list); void handleDynamicSensorConnection(int handle, bool connected); @@ -76,6 +87,7 @@ public: int getHalDeviceVersion() const; ssize_t poll(sensors_event_t* buffer, size_t count); + void writeWakeLockHandled(uint32_t count); status_t activate(void* ident, int handle, int enabled); status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, @@ -98,12 +110,22 @@ public: 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::V1_0::SensorInfo> &dynamicSensorsAdded); + hardware::Return<void> onDynamicSensorsDisconnected( + const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved); + + bool isReconnecting() const { + return mReconnecting; + } + // Dumpable virtual std::string dump() const; private: friend class Singleton<SensorDevice>; - sp<hardware::sensors::V1_0::ISensors> mSensors; + sp<SensorServiceUtil::ISensorsWrapper> mSensors; Vector<sensor_t> mSensorList; std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; @@ -151,7 +173,7 @@ private: // the removed ident. If index >=0, ident is present and successfully removed. ssize_t removeBatchParamsForIdent(void* ident); - int numActiveClients(); + int numActiveClients() const; }; DefaultKeyedVector<int, Info> mActivationCount; @@ -163,6 +185,26 @@ private: SortedVector<void *> mDisabledClients; SensorDevice(); 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 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 + FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize + UNKNOWN, + }; + HalConnectionStatus connectHidlServiceV1_0(); + HalConnectionStatus connectHidlServiceV2_0(); + + ssize_t pollHal(sensors_event_t* buffer, size_t count); + ssize_t pollFmq(sensors_event_t* buffer, size_t count); + status_t activateLocked(void* ident, int handle, int enabled); + status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs); static void handleHidlDeath(const std::string &detail); template<typename T> @@ -189,6 +231,18 @@ private: sensors_event_t *dst); bool mIsDirectReportSupported; + + typedef hardware::MessageQueue<Event, hardware::kSynchronizedReadWrite> EventMessageQueue; + typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue; + std::unique_ptr<EventMessageQueue> mEventQueue; + std::unique_ptr<WakeLockQueue> mWakeLockQueue; + + hardware::EventFlag* mEventQueueFlag; + + std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer; + + sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver; + std::atomic_bool mReconnecting; }; // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 956844f426..0fb4ac6c11 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -31,7 +31,7 @@ SensorService::SensorEventConnection::SensorEventConnection( const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode, const String16& opPackageName, bool hasSensorAccess) : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false), - mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(NULL), + mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr), mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false), mHasSensorAccess(hasSensorAccess) { mChannel = new BitTube(mService->mSocketBufferSize); @@ -55,8 +55,8 @@ void SensorService::SensorEventConnection::destroy() { } mService->cleanupConnection(this); - if (mEventCache != NULL) { - delete mEventCache; + if (mEventCache != nullptr) { + delete[] mEventCache; } mDestroyed = true; } @@ -200,7 +200,7 @@ void SensorService::SensorEventConnection::updateLooperRegistrationLocked( // Add the file descriptor to the Looper for receiving acknowledegments if the app has // registered for wake-up sensors OR for sending events in the cache. - int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, NULL); + int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, nullptr); if (ret == 1) { ALOGD_IF(DEBUG_CONNECTIONS, "%p addFd fd=%d", this, mChannel->getSendFd()); mHasLooperCallbacks = true; @@ -224,7 +224,7 @@ status_t SensorService::SensorEventConnection::sendEvents( wp<const SensorEventConnection> const * mapFlushEventsToConnections) { // filter out events not for this connection - sensors_event_t* sanitizedBuffer = nullptr; + std::unique_ptr<sensors_event_t[]> sanitizedBuffer; int count = 0; Mutex::Autolock _l(mConnectionLock); @@ -293,7 +293,8 @@ status_t SensorService::SensorEventConnection::sendEvents( scratch = const_cast<sensors_event_t *>(buffer); count = numEvents; } else { - scratch = sanitizedBuffer = new sensors_event_t[numEvents]; + sanitizedBuffer.reset(new sensors_event_t[numEvents]); + scratch = sanitizedBuffer.get(); for (size_t i = 0; i < numEvents; i++) { if (buffer[i].type == SENSOR_TYPE_META_DATA) { scratch[count++] = buffer[i++]; @@ -305,7 +306,6 @@ status_t SensorService::SensorEventConnection::sendEvents( sendPendingFlushEventsLocked(); // Early return if there are no events for this connection. if (count == 0) { - delete sanitizedBuffer; return status_t(NO_ERROR); } @@ -323,7 +323,6 @@ status_t SensorService::SensorEventConnection::sendEvents( // the max cache size that is desired. if (mCacheSize + count < computeMaxCacheSizeLocked()) { reAllocateCacheLocked(scratch, count); - delete sanitizedBuffer; return status_t(NO_ERROR); } // Some events need to be dropped. @@ -342,7 +341,6 @@ status_t SensorService::SensorEventConnection::sendEvents( memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize, numEventsDropped * sizeof(sensors_event_t)); } - delete sanitizedBuffer; return status_t(NO_ERROR); } @@ -373,7 +371,7 @@ status_t SensorService::SensorEventConnection::sendEvents( --mTotalAcksNeeded; #endif } - if (mEventCache == NULL) { + if (mEventCache == nullptr) { mMaxCacheSize = computeMaxCacheSizeLocked(); mEventCache = new sensors_event_t[mMaxCacheSize]; mCacheSize = 0; @@ -384,7 +382,6 @@ status_t SensorService::SensorEventConnection::sendEvents( // Add this file descriptor to the looper to get a callback when this fd is available for // writing. updateLooperRegistrationLocked(mService->getLooper()); - delete sanitizedBuffer; return size; } @@ -394,7 +391,6 @@ status_t SensorService::SensorEventConnection::sendEvents( } #endif - delete sanitizedBuffer; return size < 0 ? status_t(size) : status_t(NO_ERROR); } @@ -415,7 +411,7 @@ void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t ALOGD_IF(DEBUG_CONNECTIONS, "reAllocateCacheLocked maxCacheSize=%d %d", mMaxCacheSize, new_cache_size); - delete mEventCache; + delete[] mEventCache; mEventCache = eventCache_new; mCacheSize += count; mMaxCacheSize = new_cache_size; diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index 032721ea39..40c21ff585 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -53,7 +53,7 @@ public: bool hasSensorAccess); status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch, - wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL); + wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr); bool hasSensor(int32_t handle) const; bool hasAnySensor() const; bool hasOneShotSensors() const; diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp index 53fb9de230..7c4c6a2046 100644 --- a/services/sensorservice/SensorRecord.cpp +++ b/services/sensorservice/SensorRecord.cpp @@ -71,7 +71,7 @@ wp<const SensorService::SensorEventConnection> if (mPendingFlushConnections.size() > 0) { return mPendingFlushConnections[0]; } - return NULL; + return nullptr; } void SensorService::SensorRecord::clearAllPendingFlushConnections() { diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h index bba83727d9..5411515d49 100644 --- a/services/sensorservice/SensorRegistrationInfo.h +++ b/services/sensorservice/SensorRegistrationInfo.h @@ -47,7 +47,7 @@ public: mPid = (thread != nullptr) ? thread->getCallingPid() : -1; mUid = (thread != nullptr) ? thread->getCallingUid() : -1; - time_t rawtime = time(NULL); + time_t rawtime = time(nullptr); struct tm * timeinfo = localtime(&rawtime); mHour = static_cast<int8_t>(timeinfo->tm_hour); mMin = static_cast<int8_t>(timeinfo->tm_min); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 8e9e7fdf7c..85450f813c 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -47,6 +47,7 @@ #include "SensorRecord.h" #include "SensorRegistrationInfo.h" +#include <ctime> #include <inttypes.h> #include <math.h> #include <sched.h> @@ -250,7 +251,7 @@ void SensorService::onFirstRef() { // it to maxSystemSocketBufferSize if necessary. FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r"); char line[128]; - if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) { + if (fp != nullptr && fgets(line, sizeof(line), fp) != nullptr) { line[sizeof(line) - 1] = '\0'; size_t maxSystemSocketBufferSize; sscanf(line, "%zu", &maxSystemSocketBufferSize); @@ -295,7 +296,7 @@ void SensorService::setSensorAccess(uid_t uid, bool hasAccess) { { Mutex::Autolock _l(mLock); for (size_t i = 0 ; i < activeConnections.size(); i++) { - if (activeConnections[i] != 0 && activeConnections[i]->getUid() == uid) { + if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) { activeConnections[i]->setSensorAccess(hasAccess); } } @@ -306,7 +307,7 @@ const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bo int handle = s->getSensor().getHandle(); int type = s->getSensor().getType(); if (mSensors.add(handle, s, isDebug, isVirtual)){ - mRecentEvent.emplace(handle, new RecentEventLogger(type)); + mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type)); return s->getSensor(); } else { return mSensors.getNonSensor(); @@ -423,6 +424,11 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { } else { // Default dump the sensor list and debugging information. // + timespec curTime; + clock_gettime(CLOCK_REALTIME, &curTime); + struct tm* timeinfo = localtime(&(curTime.tv_sec)); + result.appendFormat("Captured at: %02d:%02d:%02d.%03d\n", timeinfo->tm_hour, + timeinfo->tm_min, timeinfo->tm_sec, (int)ns2ms(curTime.tv_nsec)); result.append("Sensor Device:\n"); result.append(SensorDevice::getInstance().dump().c_str()); @@ -475,7 +481,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { result.appendFormat("%zd active connections\n", mActiveConnections.size()); for (size_t i=0 ; i < mActiveConnections.size() ; i++) { sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != 0) { + if (connection != nullptr) { result.appendFormat("Connection Number: %zu \n", i); connection->dump(result); } @@ -627,8 +633,13 @@ bool SensorService::threadLoop() { do { ssize_t count = device.poll(mSensorEventBuffer, numEventMax); if (count < 0) { - ALOGE("sensor poll failed (%s)", strerror(-count)); - break; + if(count == DEAD_OBJECT && device.isReconnecting()) { + device.reconnect(); + continue; + } else { + ALOGE("sensor poll failed (%s)", strerror(-count)); + break; + } } // Reset sensors_event_t.flags to zero for all events in the buffer. @@ -651,16 +662,18 @@ bool SensorService::threadLoop() { // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should // not be interleaved with decrementing SensorEventConnection::mWakeLockRefCount and // releasing the wakelock. - bool bufferHasWakeUpEvent = false; + uint32_t wakeEvents = 0; for (int i = 0; i < count; i++) { if (isWakeUpSensorEvent(mSensorEventBuffer[i])) { - bufferHasWakeUpEvent = true; - break; + wakeEvents++; } } - if (bufferHasWakeUpEvent && !mWakeLockAcquired) { - setWakeLockAcquiredLocked(true); + if (wakeEvents > 0) { + if (!mWakeLockAcquired) { + setWakeLockAcquiredLocked(true); + } + device.writeWakeLockHandled(wakeEvents); } recordLastValueLocked(mSensorEventBuffer, count); @@ -722,11 +735,11 @@ bool SensorService::threadLoop() { // on the hardware sensor. mapFlushEventsToConnections[i] will be the // SensorEventConnection mapped to the corresponding flush_complete_event in // mSensorEventBuffer[i] if such a mapping exists (NULL otherwise). - mMapFlushEventsToConnections[i] = NULL; + mMapFlushEventsToConnections[i] = nullptr; if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) { const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor; SensorRecord* rec = mActiveSensors.valueFor(sensor_handle); - if (rec != NULL) { + if (rec != nullptr) { mMapFlushEventsToConnections[i] = rec->getFirstPendingFlushConnection(); rec->removeFirstPendingFlushConnection(); } @@ -770,7 +783,7 @@ bool SensorService::threadLoop() { size_t numConnections = activeConnections.size(); for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != NULL) { + if (activeConnections[i] != nullptr) { activeConnections[i]->removeSensor(handle); } } @@ -783,7 +796,7 @@ bool SensorService::threadLoop() { bool needsWakeLock = false; size_t numConnections = activeConnections.size(); for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != 0) { + if (activeConnections[i] != nullptr) { activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, mMapFlushEventsToConnections); needsWakeLock |= activeConnections[i]->needsWakeLock(); @@ -816,7 +829,7 @@ void SensorService::resetAllWakeLockRefCounts() { { Mutex::Autolock _l(mLock); for (size_t i=0 ; i < activeConnections.size(); ++i) { - if (activeConnections[i] != 0) { + if (activeConnections[i] != nullptr) { activeConnections[i]->resetWakeLockRefCount(); } } @@ -1021,15 +1034,15 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri int requestedMode, const String16& opPackageName) { // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION. if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) { - return NULL; + return nullptr; } Mutex::Autolock _l(mLock); // To create a client in DATA_INJECTION mode to inject data, SensorService should already be // operating in DI mode. if (requestedMode == DATA_INJECTION) { - if (mCurrentOperatingMode != DATA_INJECTION) return NULL; - if (!isWhiteListedPackage(packageName)) return NULL; + if (mCurrentOperatingMode != DATA_INJECTION) return nullptr; + if (!isWhiteListedPackage(packageName)) return nullptr; } uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -1277,6 +1290,15 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection"); mActiveSensors.removeItemsAt(i, 1); mActiveVirtualSensors.erase(handle); + + // If this is the last connection, then mark the RecentEventLogger as stale. This is + // critical for on-change events since the previous event is sent to a client if the + // sensor is already active. If two clients request the sensor at the same time, one + // of the clients would receive a stale event. + auto logger = mRecentEvent.find(handle); + if (logger != mRecentEvent.end()) { + logger->second->setLastEventStale(); + } delete rec; size--; } else { @@ -1325,7 +1347,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, } SensorRecord* rec = mActiveSensors.valueFor(handle); - if (rec == 0) { + if (rec == nullptr) { rec = new SensorRecord(connection); mActiveSensors.add(handle, rec); if (sensor->isVirtual()) { @@ -1343,16 +1365,17 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, auto logger = mRecentEvent.find(handle); if (logger != mRecentEvent.end()) { sensors_event_t event; - // It is unlikely that this buffer is empty as the sensor is already active. - // One possible corner case may be two applications activating an on-change - // sensor at the same time. - if(logger->second->populateLastEvent(&event)) { + // Verify that the last sensor event was generated from the current activation + // of the sensor. If not, it is possible for an on-change sensor to receive a + // sensor event that is stale if two clients re-activate the sensor + // simultaneously. + if(logger->second->populateLastEventIfCurrent(&event)) { event.sensor = handle; if (event.version == sizeof(sensors_event_t)) { if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) { setWakeLockAcquiredLocked(true); } - connection->sendEvents(&event, 1, NULL); + connection->sendEvents(&event, 1, nullptr); if (!connection->needsWakeLock() && mWakeLockAcquired) { checkWakeLockStateLocked(); } @@ -1534,7 +1557,7 @@ status_t SensorService::flushSensor(const sp<SensorEventConnection>& connection, status_t err_flush = sensor->flush(connection.get(), handle); if (err_flush == NO_ERROR) { SensorRecord* rec = mActiveSensors.valueFor(handle); - if (rec != NULL) rec->addPendingFlushConnection(connection); + if (rec != nullptr) rec->addPendingFlushConnection(connection); } err = (err_flush != NO_ERROR) ? err_flush : err; } @@ -1592,7 +1615,7 @@ void SensorService::checkWakeLockStateLocked() { bool releaseLock = true; for (size_t i=0 ; i<mActiveConnections.size() ; i++) { sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != 0) { + if (connection != nullptr) { if (connection->needsWakeLock()) { releaseLock = false; break; @@ -1617,7 +1640,7 @@ void SensorService::populateActiveConnections( Mutex::Autolock _l(mLock); for (size_t i=0 ; i < mActiveConnections.size(); ++i) { sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != 0) { + if (connection != nullptr) { activeConnections->add(connection); } } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index f71723d8c9..db945bb133 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -59,7 +59,6 @@ namespace android { // --------------------------------------------------------------------------- class SensorInterface; -using namespace SensorServiceUtil; class SensorService : public BinderService<SensorService>, @@ -277,7 +276,7 @@ private: static uint8_t sHmacGlobalKey[128]; static bool sHmacGlobalKeyIsValid; - SensorList mSensors; + SensorServiceUtil::SensorList mSensors; status_t mInitCheck; // Socket buffersize used to initialize BitTube. This size depends on whether batching is @@ -294,7 +293,7 @@ private: bool mWakeLockAcquired; sensors_event_t *mSensorEventBuffer, *mSensorEventScratch; wp<const SensorEventConnection> * mMapFlushEventsToConnections; - std::unordered_map<int, RecentEventLogger*> mRecentEvent; + std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; SortedVector< wp<SensorDirectConnection> > mDirectConnections; Mode mCurrentOperatingMode; diff --git a/services/sensorservice/SensorsWrapper.h b/services/sensorservice/SensorsWrapper.h new file mode 100644 index 0000000000..d1a72345f7 --- /dev/null +++ b/services/sensorservice/SensorsWrapper.h @@ -0,0 +1,194 @@ +/* + * 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. + */ + +#ifndef ANDROID_SENSORS_WRAPPER_H +#define ANDROID_SENSORS_WRAPPER_H + +#include "android/hardware/sensors/1.0/ISensors.h" +#include "android/hardware/sensors/2.0/ISensors.h" +#include "android/hardware/sensors/2.0/ISensorsCallback.h" + +#include <utils/LightRefBase.h> + +namespace android { +namespace SensorServiceUtil { + +using ::android::hardware::MQDescriptorSync; +using ::android::hardware::Return; +using ::android::hardware::sensors::V1_0::Event; +using ::android::hardware::sensors::V1_0::ISensors; +using ::android::hardware::sensors::V1_0::OperationMode; +using ::android::hardware::sensors::V1_0::RateLevel; +using ::android::hardware::sensors::V1_0::Result; +using ::android::hardware::sensors::V1_0::SharedMemInfo; +using ::android::hardware::sensors::V2_0::ISensorsCallback; + +/* + * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This + * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors + * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete + * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design + * is beneficial because only the functions that change between Sensors HAL versions must be newly + * newly implemented, any previously implemented function that does not change may remain the same. + * + * Functions that exist across all versions of the Sensors HAL should be implemented as pure + * virtual functions which forces the concrete instantiations to implement the functions. + * + * Functions that do not exist across all versions of the Sensors HAL should include a default + * implementation that generates an error if called. The default implementation should never + * be called and must be overridden by Sensors HAL versions that support the function. + */ +class ISensorsWrapper : public VirtualLightRefBase { +public: + virtual bool supportsPolling() const = 0; + + virtual bool supportsMessageQueues() const = 0; + + virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0; + + virtual Return<Result> setOperationMode(OperationMode mode) = 0; + + virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0; + + virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) = 0; + + virtual Return<Result> flush(int32_t sensorHandle) = 0; + + virtual Return<Result> injectSensorData(const Event& event) = 0; + + virtual Return<void> registerDirectChannel(const SharedMemInfo& mem, + ISensors::registerDirectChannel_cb _hidl_cb) = 0; + + virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0; + + virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, + RateLevel rate, + ISensors::configDirectReport_cb _hidl_cb) = 0; + + virtual Return<void> poll(int32_t maxCount, ISensors::poll_cb _hidl_cb) { + (void)maxCount; + (void)_hidl_cb; + // TODO (b/111070257): Generate an assert-level error since this should never be called + // directly + return Return<void>(); + } + + virtual Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc, + const MQDescriptorSync<uint32_t>& wakeLockDesc, + const ::android::sp<ISensorsCallback>& callback) { + (void)eventQueueDesc; + (void)wakeLockDesc; + (void)callback; + // TODO (b/111070257): Generate an assert-level error since this should never be called + // directly + return Result::INVALID_OPERATION; + } +}; + +template<typename T> +class SensorsWrapperBase : public ISensorsWrapper { +public: + SensorsWrapperBase(sp<T> sensors) : + mSensors(sensors) { }; + + Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override { + return mSensors->getSensorsList(_hidl_cb); + } + + Return<Result> setOperationMode(OperationMode mode) override { + return mSensors->setOperationMode(mode); + } + + Return<Result> activate(int32_t sensorHandle, bool enabled) override { + return mSensors->activate(sensorHandle, enabled); + } + + Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs, + int64_t maxReportLatencyNs) override { + return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs); + } + + Return<Result> flush(int32_t sensorHandle) override { + return mSensors->flush(sensorHandle); + } + + Return<Result> injectSensorData(const Event& event) override { + return mSensors->injectSensorData(event); + } + + Return<void> registerDirectChannel(const SharedMemInfo& mem, + ISensors::registerDirectChannel_cb _hidl_cb) override { + return mSensors->registerDirectChannel(mem, _hidl_cb); + } + + Return<Result> unregisterDirectChannel(int32_t channelHandle) override { + return mSensors->unregisterDirectChannel(channelHandle); + } + + Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle, + RateLevel rate, + ISensors::configDirectReport_cb _hidl_cb) override { + return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb); + } + +protected: + sp<T> mSensors; +}; + +class SensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> { +public: + SensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors) : + SensorsWrapperBase(sensors) { }; + + bool supportsPolling() const override { + return true; + } + + bool supportsMessageQueues() const override { + return false; + } + + Return<void> poll(int32_t maxCount, + hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override { + return mSensors->poll(maxCount, _hidl_cb); + } +}; + +class SensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> { +public: + SensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors) + : SensorsWrapperBase(sensors) { }; + + bool supportsPolling() const override { + return false; + } + + bool supportsMessageQueues() const override { + return true; + } + + Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc, + const MQDescriptorSync<uint32_t>& wakeLockDesc, + const ::android::sp<ISensorsCallback>& callback) override { + return mSensors->initialize(eventQueueDesc, wakeLockDesc, callback); + } +}; + +}; // namespace SensorServiceUtil +}; // namespace android + +#endif // ANDROID_SENSORS_WRAPPER_H diff --git a/services/sensorservice/hidl/EventQueue.cpp b/services/sensorservice/hidl/EventQueue.cpp index ff200660c1..b781744553 100644 --- a/services/sensorservice/hidl/EventQueue.cpp +++ b/services/sensorservice/hidl/EventQueue.cpp @@ -64,7 +64,7 @@ EventQueue::EventQueue( mInternalQueue(internalQueue) { mLooper->addFd(internalQueue->getFd(), ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, - new EventQueueLooperCallback(internalQueue, callback), NULL /* data */); + new EventQueueLooperCallback(internalQueue, callback), nullptr /* data */); } void EventQueue::onLastStrongRef(const void *id) { diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp index fee6da1e60..938060063f 100644 --- a/services/sensorservice/hidl/SensorManager.cpp +++ b/services/sensorservice/hidl/SensorManager.cpp @@ -157,7 +157,7 @@ sp<Looper> SensorManager::getLooper() { JavaVMAttachArgs args{ .version = JNI_VERSION_1_2, .name = POLL_THREAD_NAME, - .group = NULL + .group = nullptr }; JNIEnv* env; if (javaVm->AttachCurrentThread(&env, &args) != JNI_OK) { diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 320e11f7eb..16003a258c 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -4,6 +4,7 @@ cc_defaults { "-DLOG_TAG=\"SurfaceFlinger\"", "-Wall", "-Werror", + "-Wformat", "-Wthread-safety", "-Wunused", "-Wunreachable-code", @@ -18,14 +19,21 @@ cc_defaults { "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", ], + include_dirs: [ + "frameworks/native/vulkan/vkjson", + "frameworks/native/vulkan/include", + ], shared_libs: [ "android.frameworks.vr.composer@1.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", + "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.0", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", "android.hardware.power@1.0", "android.hardware.power@1.3", "libbase", @@ -44,6 +52,7 @@ cc_defaults { "libhwbinder", "liblayers_proto", "liblog", + "libnativewindow", "libpdx_default_transport", "libprotobuf-cpp-lite", "libsync", @@ -53,6 +62,7 @@ cc_defaults { "libvulkan", ], static_libs: [ + "librenderengine", "libserviceutils", "libtrace_proto", "libvkjson", @@ -62,14 +72,18 @@ cc_defaults { 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", ], export_static_lib_headers: [ + "librenderengine", "libserviceutils", ], export_shared_lib_headers: [ "android.hardware.graphics.allocator@2.0", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", "android.hardware.power@1.3", "libhidlbase", "libhidltransport", @@ -77,6 +91,18 @@ cc_defaults { ], } +cc_defaults { + name: "libsurfaceflinger_production_defaults", + defaults: ["libsurfaceflinger_defaults"], + cflags: [ + "-fvisibility=hidden", + "-fwhole-program-vtables", // requires ThinLTO + ], + lto: { + thin: true, + }, +} + cc_library_headers { name: "libsurfaceflinger_headers", export_include_dirs: ["."], @@ -89,79 +115,65 @@ filegroup { srcs: [ "BufferLayer.cpp", "BufferLayerConsumer.cpp", + "BufferQueueLayer.cpp", + "BufferStateLayer.cpp", "Client.cpp", "ColorLayer.cpp", "ContainerLayer.cpp", "DisplayDevice.cpp", "DisplayHardware/ComposerHal.cpp", + "DisplayHardware/DisplayIdentification.cpp", "DisplayHardware/FramebufferSurface.cpp", "DisplayHardware/HWC2.cpp", "DisplayHardware/HWComposer.cpp", "DisplayHardware/HWComposerBufferCache.cpp", "DisplayHardware/PowerAdvisor.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", - "DispSync.cpp", "Effects/Daltonizer.cpp", - "EventControlThread.cpp", "EventLog/EventLog.cpp", - "EventThread.cpp", "FrameTracker.cpp", "GpuService.cpp", "Layer.cpp", + "LayerBE.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", "LayerStats.cpp", "LayerVector.cpp", - "MessageQueue.cpp", "MonitoredProducer.cpp", + "NativeWindowSurface.cpp", "RenderArea.cpp", - "RenderEngine/Description.cpp", - "RenderEngine/GLES20RenderEngine.cpp", - "RenderEngine/GLExtensions.cpp", - "RenderEngine/Image.cpp", - "RenderEngine/Mesh.cpp", - "RenderEngine/Program.cpp", - "RenderEngine/ProgramCache.cpp", - "RenderEngine/RenderEngine.cpp", - "RenderEngine/Surface.cpp", - "RenderEngine/Texture.cpp", + "Scheduler/DispSync.cpp", + "Scheduler/DispSyncSource.cpp", + "Scheduler/EventControlThread.cpp", + "Scheduler/EventThread.cpp", + "Scheduler/MessageQueue.cpp", + "Scheduler/Scheduler.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceInterceptor.cpp", "SurfaceTracing.cpp", "TimeStats/TimeStats.cpp", - "Transform.cpp", ], } cc_library_shared { + // Please use libsurfaceflinger_defaults to configure how the sources are + // built, so the same settings can be used elsewhere. name: "libsurfaceflinger", - defaults: ["libsurfaceflinger_defaults"], - cflags: [ - "-fvisibility=hidden", - "-Werror=format", - ], + defaults: ["libsurfaceflinger_production_defaults"], srcs: [ ":libsurfaceflinger_sources", + + // Note: SurfaceFlingerFactory is not in the default sources so that it + // can be easily replaced. + "SurfaceFlingerFactory.cpp", ], logtags: ["EventLog/EventLogTags.logtags"], - include_dirs: [ - "frameworks/native/vulkan/vkjson", - "frameworks/native/vulkan/include", - ], - cppflags: [ - "-fwhole-program-vtables", // requires ThinLTO - ], - lto: { - thin: true, - }, } -cc_binary { - name: "surfaceflinger", +cc_defaults { + name: "libsurfaceflinger_binary", defaults: ["surfaceflinger_defaults"], - init_rc: ["surfaceflinger.rc"], - srcs: ["main_surfaceflinger.cpp"], whole_static_libs: [ "libsigchain", ], @@ -169,6 +181,7 @@ cc_binary { "android.frameworks.displayservice@1.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", + "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.0", "libbinder", "libcutils", @@ -177,7 +190,6 @@ cc_binary { "libhidltransport", "liblayers_proto", "liblog", - "libsurfaceflinger", "libtimestats_proto", "libutils", ], @@ -186,19 +198,19 @@ cc_binary { "libtrace_proto", ], ldflags: ["-Wl,--export-dynamic"], +} - // TODO(b/71715793): These version-scripts are required due to the use of - // whole_static_libs to pull in libsigchain. To work, the files had to be - // locally duplicated from their original location - // $ANDROID_ROOT/art/sigchainlib/ - multilib: { - lib32: { - version_script: "version-script32.txt", - }, - lib64: { - version_script: "version-script64.txt", - }, - }, +filegroup { + name: "surfaceflinger_binary_sources", + srcs: ["main_surfaceflinger.cpp"], +} + +cc_binary { + name: "surfaceflinger", + defaults: ["libsurfaceflinger_binary"], + init_rc: ["surfaceflinger.rc"], + srcs: [":surfaceflinger_binary_sources"], + shared_libs: ["libsurfaceflinger"], } cc_library_shared { diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h index 3e9d4433ad..97028a89fa 100644 --- a/services/surfaceflinger/Barrier.h +++ b/services/surfaceflinger/Barrier.h @@ -18,46 +18,40 @@ #define ANDROID_BARRIER_H #include <stdint.h> -#include <sys/types.h> -#include <utils/threads.h> +#include <condition_variable> +#include <mutex> namespace android { class Barrier { public: - inline Barrier() : state(CLOSED) { } - inline ~Barrier() { } - // Release any threads waiting at the Barrier. // Provides release semantics: preceding loads and stores will be visible // to other threads before they wake up. void open() { - Mutex::Autolock _l(lock); - state = OPENED; - cv.broadcast(); + std::lock_guard<std::mutex> lock(mMutex); + mIsOpen = true; + mCondition.notify_all(); } // Reset the Barrier, so wait() will block until open() has been called. void close() { - Mutex::Autolock _l(lock); - state = CLOSED; + std::lock_guard<std::mutex> lock(mMutex); + mIsOpen = false; } // Wait until the Barrier is OPEN. // Provides acquire semantics: no subsequent loads or stores will occur // until wait() returns. void wait() const { - Mutex::Autolock _l(lock); - while (state == CLOSED) { - cv.wait(lock); - } + std::unique_lock<std::mutex> lock(mMutex); + mCondition.wait(lock, [this]() NO_THREAD_SAFETY_ANALYSIS { return mIsOpen; }); } private: - enum { OPENED, CLOSED }; - mutable Mutex lock; - mutable Condition cv; - volatile int state; + mutable std::mutex mMutex; + mutable std::condition_variable mCondition; + int mIsOpen GUARDED_BY(mMutex){false}; }; }; // namespace android diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index f5b5eda9ec..440f1e24e1 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -23,9 +23,8 @@ #include "Colorizer.h" #include "DisplayDevice.h" #include "LayerRejecter.h" -#include "clz.h" -#include "RenderEngine/RenderEngine.h" +#include <renderengine/RenderEngine.h> #include <gui/BufferItem.h> #include <gui/BufferQueue.h> @@ -50,28 +49,16 @@ namespace android { -BufferLayer::BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags) - : Layer(flinger, client, name, w, h, flags), - mConsumer(nullptr), - mTextureName(UINT32_MAX), - mFormat(PIXEL_FORMAT_NONE), - mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mBufferLatched(false), - mPreviousFrameNumber(0), - mUpdateTexImageFailed(false), - mRefreshPending(false) { - ALOGV("Creating Layer %s", name.string()); +BufferLayer::BufferLayer(const LayerCreationArgs& args) + : Layer(args), mTextureName(args.flinger->getNewTexture()) { + ALOGV("Creating Layer %s", args.name.string()); - mTextureName = mFlinger->getNewTexture(); - mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName); + mTexture.init(renderengine::Texture::TEXTURE_EXTERNAL, mTextureName); - if (flags & ISurfaceComposerClient::eNonPremultiplied) mPremultipliedAlpha = false; + mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); - mCurrentState.requested = mCurrentState.active; - - // drawing state & current state are identical - mDrawingState = mCurrentState; + mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; + mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; } BufferLayer::~BufferLayer() { @@ -83,13 +70,15 @@ BufferLayer::~BufferLayer() { mName.string()); destroyAllHwcLayers(); } + + mTimeStats.onDestroy(getSequence()); } void BufferLayer::useSurfaceDamage() { if (mFlinger->mForceFullDamage) { surfaceDamageRegion = Region::INVALID_REGION; } else { - surfaceDamageRegion = mConsumer->getSurfaceDamage(); + surfaceDamageRegion = getDrawingSurfaceDamage(); } } @@ -97,46 +86,27 @@ void BufferLayer::useEmptyDamage() { surfaceDamageRegion.clear(); } -bool BufferLayer::isProtected() const { - const sp<GraphicBuffer>& buffer(getBE().compositionInfo.mBuffer); - return (buffer != 0) && - (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); +bool BufferLayer::isOpaque(const Layer::State& s) const { + // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the + // layer's opaque flag. + if ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (mActiveBuffer == nullptr)) { + return false; + } + + // if the layer has the opaque flag, then we're always opaque, + // otherwise we use the current buffer's format. + return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat()); } bool BufferLayer::isVisible() const { return !(isHiddenByPolicy()) && getAlpha() > 0.0f && - (getBE().compositionInfo.mBuffer != nullptr || - getBE().compositionInfo.hwc.sidebandStream != nullptr); + (mActiveBuffer != nullptr || getBE().compositionInfo.hwc.sidebandStream != nullptr); } bool BufferLayer::isFixedSize() const { return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE; } -status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - uint32_t const maxSurfaceDims = - min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); - - // never allow a surface larger than what our underlying GL implementation - // can handle. - if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) { - ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h)); - return BAD_VALUE; - } - - mFormat = format; - - mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false; - mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false; - mCurrentOpacity = getOpacityForFormat(format); - - mConsumer->setDefaultBufferSize(w, h); - mConsumer->setDefaultBufferFormat(format); - mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); - - return NO_ERROR; -} - static constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); @@ -159,10 +129,10 @@ static constexpr mat4 inverseOrientation(uint32_t transform) { * onDraw will draw the current layer onto the presentable buffer */ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const { + bool useIdentityTransform) { ATRACE_CALL(); - if (CC_UNLIKELY(getBE().compositionInfo.mBuffer == 0)) { + if (CC_UNLIKELY(mActiveBuffer == 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 @@ -191,7 +161,7 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, // Bind the current buffer to the GL texture, and wait for it to be // ready for us to draw into. - status_t err = mConsumer->bindTextureImage(); + status_t err = bindTextureImage(); if (err != NO_ERROR) { ALOGW("onDraw: bindTextureImage failed (err=%d)", err); // Go ahead and draw the buffer anyway; no matter what we do the screen @@ -204,12 +174,12 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, if (!blackOutLayer) { // TODO: we could be more subtle with isFixedSize() - const bool useFiltering = getFiltering() || needsFiltering(renderArea) || isFixedSize(); + const bool useFiltering = needsFiltering(renderArea) || isFixedSize(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; - mConsumer->setFilteringEnabled(useFiltering); - mConsumer->getTransformMatrix(textureMatrix); + setFilteringEnabled(useFiltering); + getDrawingTransformMatrix(textureMatrix); if (getTransformToDisplayInverse()) { /* @@ -240,8 +210,7 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, } // Set things up for texturing. - mTexture.setDimensions(getBE().compositionInfo.mBuffer->getWidth(), - getBE().compositionInfo.mBuffer->getHeight()); + mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight()); mTexture.setFiltering(useFiltering); mTexture.setMatrix(textureMatrix); @@ -253,54 +222,106 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, engine.disableTexturing(); } -void BufferLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { - mConsumer->setReleaseFence(releaseFence); +bool BufferLayer::isHdrY410() const { + // pixel format is HDR Y410 masquerading as RGBA_1010102 + return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ && + getDrawingApi() == NATIVE_WINDOW_API_MEDIA && + getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102); } -void BufferLayer::abandon() { - mConsumer->abandon(); -} +void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& display) { + // Apply this display's projection's viewport to the visible region + // before giving it to the HWC HAL. + const ui::Transform& tr = display->getTransform(); + const auto& viewport = display->getViewport(); + Region visible = tr.transform(visibleRegion.intersect(viewport)); + const auto displayId = display->getId(); + if (!hasHwcLayer(displayId)) { + ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)", + mName.string(), displayId); + return; + } + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + auto error = hwcLayer->setVisibleRegion(visible); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + visible.dump(LOG_TAG); + } + getBE().compositionInfo.hwc.visibleRegion = visible; -bool BufferLayer::shouldPresentNow(const DispSync& dispSync) const { - if (mSidebandStreamChanged || mAutoRefresh) { - return true; + error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + surfaceDamageRegion.dump(LOG_TAG); } + getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion; - Mutex::Autolock lock(mQueueItemLock); - if (mQueueItems.empty()) { - return false; + // Sideband layers + if (getBE().compositionInfo.hwc.sidebandStream.get()) { + setCompositionType(displayId, HWC2::Composition::Sideband); + ALOGV("[%s] Requesting Sideband composition", mName.string()); + error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(), + getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + getBE().compositionInfo.compositionType = HWC2::Composition::Sideband; + return; } - auto timestamp = mQueueItems[0].mTimestamp; - nsecs_t expectedPresent = mConsumer->computeExpectedPresent(dispSync); - // Ignore timestamps more than a second in the future - bool isPlausible = timestamp < (expectedPresent + s2ns(1)); - ALOGW_IF(!isPlausible, - "[%s] Timestamp %" PRId64 " seems implausible " - "relative to expectedPresent %" PRId64, - mName.string(), timestamp, expectedPresent); + // Device or Cursor layers + if (mPotentialCursor) { + ALOGV("[%s] Requesting Cursor composition", mName.string()); + setCompositionType(displayId, HWC2::Composition::Cursor); + } else { + ALOGV("[%s] Requesting Device composition", mName.string()); + setCompositionType(displayId, HWC2::Composition::Device); + } - bool isDue = timestamp < expectedPresent; - return isDue || !isPlausible; -} + ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace); + error = hwcLayer->setDataspace(mCurrentDataSpace); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + const HdrMetadata& metadata = getDrawingHdrMetadata(); + error = hwcLayer->setPerFrameMetadata(display->getSupportedPerFrameMetadata(), metadata); + if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) { + ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } -void BufferLayer::setTransformHint(uint32_t orientation) const { - mConsumer->setTransformHint(orientation); + error = hwcLayer->setColorTransform(getColorTransform()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace; + getBE().compositionInfo.hwc.hdrMetadata = getDrawingHdrMetadata(); + getBE().compositionInfo.hwc.supportedPerFrameMetadata = display->getSupportedPerFrameMetadata(); + getBE().compositionInfo.hwc.colorTransform = getColorTransform(); + + setHwcLayerBuffer(display); } bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) { if (mBufferLatched) { Mutex::Autolock lock(mFrameEventHistoryMutex); - mFrameEventHistory.addPreComposition(mCurrentFrameNumber, - refreshStartTime); + mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime); } mRefreshPending = false; - return mQueuedFrames > 0 || mSidebandStreamChanged || - mAutoRefresh; + return hasReadyFrame(); } + bool BufferLayer::onPostComposition(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 (!mFrameLatencyNeeded) return false; @@ -308,18 +329,18 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc // Update mFrameEventHistory. { Mutex::Autolock lock(mFrameEventHistoryMutex); - mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, - presentFence, compositorTiming); + mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence, + compositorTiming); } // Update mFrameTracker. - nsecs_t desiredPresentTime = mConsumer->getTimestamp(); + nsecs_t desiredPresentTime = getDesiredPresentTime(); mFrameTracker.setDesiredPresentTime(desiredPresentTime); - const std::string layerName(getName().c_str()); - mTimeStats.setDesiredTime(layerName, mCurrentFrameNumber, desiredPresentTime); + const int32_t layerID = getSequence(); + mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime); - std::shared_ptr<FenceTime> frameReadyFence = mConsumer->getCurrentFenceTime(); + std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime(); if (frameReadyFence->isValid()) { mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); } else { @@ -329,14 +350,14 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc } if (presentFence->isValid()) { - mTimeStats.setPresentFence(layerName, mCurrentFrameNumber, presentFence); + mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence); mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); - } else { + } else if (mFlinger->getHwComposer().isConnected(HWC_DISPLAY_PRIMARY)) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(HWC_DISPLAY_PRIMARY); - mTimeStats.setPresentTime(layerName, mCurrentFrameNumber, actualPresentTime); + mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime); mFrameTracker.setActualPresentTime(actualPresentTime); } @@ -345,58 +366,20 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc return true; } -std::vector<OccupancyTracker::Segment> BufferLayer::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)", mName.string(), result); - return {}; - } - return history; -} - -bool BufferLayer::getTransformToDisplayInverse() const { - return mConsumer->getTransformToDisplayInverse(); -} - -void BufferLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { - if (!mConsumer->releasePendingBuffer()) { - return; - } - - auto releaseFenceTime = - std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence()); - mReleaseTimeline.updateSignalTimes(); - mReleaseTimeline.push(releaseFenceTime); - - Mutex::Autolock lock(mFrameEventHistoryMutex); - if (mPreviousFrameNumber != 0) { - mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime, - std::move(releaseFenceTime)); - } -} - -Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { +Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) { ATRACE_CALL(); - if (android_atomic_acquire_cas(true, false, &mSidebandStreamChanged) == 0) { - // mSidebandStreamChanged was true - mSidebandStream = mConsumer->getSidebandStream(); - // replicated in LayerBE until FE/BE is ready to be synchronized - getBE().compositionInfo.hwc.sidebandStream = mSidebandStream; - if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { - setTransactionFlags(eTransactionNeeded); - mFlinger->setTransactionFlags(eTraversalNeeded); - } - recomputeVisibleRegions = true; + std::optional<Region> sidebandStreamDirtyRegion = latchSidebandStream(recomputeVisibleRegions); - const State& s(getDrawingState()); - return getTransform().transform(Region(Rect(s.active.w, s.active.h))); + if (sidebandStreamDirtyRegion) { + return *sidebandStreamDirtyRegion; } - Region outDirtyRegion; - if (mQueuedFrames <= 0 && !mAutoRefresh) { - return outDirtyRegion; + Region dirtyRegion; + + if (!hasReadyFrame()) { + return dirtyRegion; } // if we've already called updateTexImage() without going through @@ -405,119 +388,41 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime // compositionComplete() call. // we'll trigger an update in onPreComposition(). if (mRefreshPending) { - return outDirtyRegion; + return dirtyRegion; } // If the head buffer's acquire fence hasn't signaled yet, return and // try again later - if (!headFenceHasSignaled()) { + if (!fenceHasSignaled()) { mFlinger->signalLayerUpdate(); - return outDirtyRegion; + return dirtyRegion; } // Capture the old state of the layer for comparisons later const State& s(getDrawingState()); const bool oldOpacity = isOpaque(s); - sp<GraphicBuffer> oldBuffer = getBE().compositionInfo.mBuffer; + sp<GraphicBuffer> oldBuffer = mActiveBuffer; if (!allTransactionsSignaled()) { mFlinger->signalLayerUpdate(); - return outDirtyRegion; - } - - // This boolean is used to make sure that SurfaceFlinger's shadow copy - // of the buffer queue isn't modified when the buffer queue is returning - // BufferItem's that weren't actually queued. This can happen in shared - // buffer mode. - bool queuedBuffer = false; - LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions, - getProducerStickyTransform() != 0, mName.string(), - mOverrideScalingMode, mFreezeGeometryUpdates); - status_t updateResult = - mConsumer->updateTexImage(&r, mFlinger->mPrimaryDispSync, - &mAutoRefresh, &queuedBuffer, - mLastFrameNumberReceived); - 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(); - return outDirtyRegion; - } 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); - mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber); - mQueueItems.removeAt(0); - android_atomic_dec(&mQueuedFrames); - } - return outDirtyRegion; - } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) { - // This can occur if something goes wrong when trying to create the - // EGLImage for this buffer. If this happens, the buffer has already - // been released, so we need to clean up the queue and bug out - // early. - if (queuedBuffer) { - Mutex::Autolock lock(mQueueItemLock); - mQueueItems.clear(); - android_atomic_and(0, &mQueuedFrames); - mTimeStats.clearLayerRecord(getName().c_str()); - } - - // Once we have hit this state, the shadow queue may no longer - // correctly reflect the incoming BufferQueue's contents, so even if - // updateTexImage starts working, the only safe course of action is - // to continue to ignore updates. - mUpdateTexImageFailed = true; - - return outDirtyRegion; - } - - if (queuedBuffer) { - // Autolock scope - auto currentFrameNumber = mConsumer->getFrameNumber(); - - Mutex::Autolock lock(mQueueItemLock); - - // Remove any stale buffers that have been dropped during - // updateTexImage - while (mQueueItems[0].mFrameNumber != currentFrameNumber) { - mTimeStats.removeTimeRecord(getName().c_str(), mQueueItems[0].mFrameNumber); - mQueueItems.removeAt(0); - android_atomic_dec(&mQueuedFrames); - } - - const std::string layerName(getName().c_str()); - mTimeStats.setAcquireFence(layerName, currentFrameNumber, mQueueItems[0].mFenceTime); - mTimeStats.setLatchTime(layerName, currentFrameNumber, latchTime); - - mQueueItems.removeAt(0); + return dirtyRegion; } - // Decrement the queued-frames count. Signal another event if we - // have more frames pending. - if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1) || - mAutoRefresh) { - mFlinger->signalLayerUpdate(); + status_t err = updateTexImage(recomputeVisibleRegions, latchTime, releaseFence); + if (err != NO_ERROR) { + return dirtyRegion; } - // update the active buffer - getBE().compositionInfo.mBuffer = - mConsumer->getCurrentBuffer(&getBE().compositionInfo.mBufferSlot); - // replicated in LayerBE until FE/BE is ready to be synchronized - mActiveBuffer = getBE().compositionInfo.mBuffer; - if (getBE().compositionInfo.mBuffer == nullptr) { - // this can only happen if the very first buffer was rejected. - return outDirtyRegion; + err = updateActiveBuffer(); + if (err != NO_ERROR) { + return dirtyRegion; } mBufferLatched = true; - mPreviousFrameNumber = mCurrentFrameNumber; - mCurrentFrameNumber = mConsumer->getFrameNumber(); - { - Mutex::Autolock lock(mFrameEventHistoryMutex); - mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime); + err = updateFrameNumber(latchTime); + if (err != NO_ERROR) { + return dirtyRegion; } mRefreshPending = true; @@ -528,7 +433,7 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime recomputeVisibleRegions = true; } - ui::Dataspace dataSpace = mConsumer->getCurrentDataSpace(); + ui::Dataspace dataSpace = getDrawingDataSpace(); // treat modern dataspaces as legacy dataspaces whenever possible, until // we can trust the buffer producers switch (dataSpace) { @@ -555,11 +460,10 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime } mCurrentDataSpace = dataSpace; - Rect crop(mConsumer->getCurrentCrop()); - const uint32_t transform(mConsumer->getCurrentTransform()); - const uint32_t scalingMode(mConsumer->getCurrentScalingMode()); - if ((crop != mCurrentCrop) || - (transform != mCurrentTransform) || + Rect crop(getDrawingCrop()); + const uint32_t transform(getDrawingTransform()); + const uint32_t scalingMode(getDrawingScalingMode()); + if ((crop != mCurrentCrop) || (transform != mCurrentTransform) || (scalingMode != mCurrentScalingMode)) { mCurrentCrop = crop; mCurrentTransform = transform; @@ -568,15 +472,13 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime } if (oldBuffer != nullptr) { - uint32_t bufWidth = getBE().compositionInfo.mBuffer->getWidth(); - uint32_t bufHeight = getBE().compositionInfo.mBuffer->getHeight(); - if (bufWidth != uint32_t(oldBuffer->width) || - bufHeight != uint32_t(oldBuffer->height)) { + uint32_t bufWidth = mActiveBuffer->getWidth(); + uint32_t bufHeight = mActiveBuffer->getHeight(); + if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) { recomputeVisibleRegions = true; } } - mCurrentOpacity = getOpacityForFormat(getBE().compositionInfo.mBuffer->format); if (oldOpacity != isOpaque(s)) { recomputeVisibleRegions = true; } @@ -603,203 +505,78 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime } // FIXME: postedRegion should be dirty & bounds - Region dirtyRegion(Rect(s.active.w, s.active.h)); - // transform the dirty region to window-manager space - outDirtyRegion = (getTransform().transform(dirtyRegion)); - - return outDirtyRegion; + return getTransform().transform(Region(Rect(getActiveWidth(s), getActiveHeight(s)))); } -void BufferLayer::setDefaultBufferSize(uint32_t w, uint32_t h) { - mConsumer->setDefaultBufferSize(w, h); -} - -void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { - // Apply this display's projection's viewport to the visible region - // before giving it to the HWC HAL. - const Transform& tr = displayDevice->getTransform(); - const auto& viewport = displayDevice->getViewport(); - Region visible = tr.transform(visibleRegion.intersect(viewport)); - auto hwcId = displayDevice->getHwcDisplayId(); - if (!hasHwcLayer(hwcId)) { - return; - } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; - auto& hwcLayer = hwcInfo.layer; - auto error = hwcLayer->setVisibleRegion(visible); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - visible.dump(LOG_TAG); - } - - error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - surfaceDamageRegion.dump(LOG_TAG); - } - - // Sideband layers - if (getBE().compositionInfo.hwc.sidebandStream.get()) { - setCompositionType(hwcId, HWC2::Composition::Sideband); - ALOGV("[%s] Requesting Sideband composition", mName.string()); - error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle()); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(), - getBE().compositionInfo.hwc.sidebandStream->handle(), to_string(error).c_str(), - static_cast<int32_t>(error)); +// transaction +void BufferLayer::notifyAvailableFrames() { + auto headFrameNumber = getHeadFrameNumber(); + bool headFenceSignaled = fenceHasSignaled(); + Mutex::Autolock lock(mLocalSyncPointMutex); + for (auto& point : mLocalSyncPoints) { + if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) { + point->setFrameAvailable(); } - return; - } - - // Device or Cursor layers - if (mPotentialCursor) { - ALOGV("[%s] Requesting Cursor composition", mName.string()); - setCompositionType(hwcId, HWC2::Composition::Cursor); - } else { - ALOGV("[%s] Requesting Device composition", mName.string()); - setCompositionType(hwcId, HWC2::Composition::Device); - } - - ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace); - error = hwcLayer->setDataspace(mCurrentDataSpace); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, - to_string(error).c_str(), static_cast<int32_t>(error)); - } - - const HdrMetadata& metadata = mConsumer->getCurrentHdrMetadata(); - error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata); - if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) { - ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - } - - uint32_t hwcSlot = 0; - sp<GraphicBuffer> hwcBuffer; - hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot, - getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer); - - auto acquireFence = mConsumer->getCurrentFence(); - error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), - getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(), - static_cast<int32_t>(error)); } } -bool BufferLayer::isOpaque(const Layer::State& s) const { - // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the - // layer's opaque flag. - if ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (getBE().compositionInfo.mBuffer == nullptr)) { - return false; - } - - // if the layer has the opaque flag, then we're always opaque, - // otherwise we use the current buffer's format. - return ((s.flags & layer_state_t::eLayerOpaque) != 0) || mCurrentOpacity; +bool BufferLayer::hasReadyFrame() const { + return hasDrawingBuffer() || getSidebandStreamChanged() || getAutoRefresh(); } -void BufferLayer::onFirstRef() { - Layer::onFirstRef(); - - // Creates a custom BufferQueue for SurfaceFlingerConsumer to use - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&producer, &consumer, true); - mProducer = new MonitoredProducer(producer, mFlinger, this); - { - // Grab the SF state lock during this since it's the only safe way to access RenderEngine - Mutex::Autolock lock(mFlinger->mStateLock); - mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, - this); - } - mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); - mConsumer->setContentsChangedListener(this); - mConsumer->setName(mName); - - if (mFlinger->isLayerTripleBufferingDisabled()) { - mProducer->setMaxDequeuedBufferCount(2); +uint32_t BufferLayer::getEffectiveScalingMode() const { + if (mOverrideScalingMode >= 0) { + return mOverrideScalingMode; } - const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice()); - updateTransformHint(hw); + return mCurrentScalingMode; } -// --------------------------------------------------------------------------- -// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener -// --------------------------------------------------------------------------- - -void BufferLayer::onFrameAvailable(const BufferItem& item) { - // Add this buffer from our internal queue tracker - { // Autolock scope - Mutex::Autolock lock(mQueueItemLock); - mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(), - item.mGraphicBuffer->getHeight(), - item.mFrameNumber); - // Reset the frame number tracker when we receive the first buffer after - // a frame number reset - if (item.mFrameNumber == 1) { - mLastFrameNumberReceived = 0; - } - - // Ensure that callbacks are handled in order - while (item.mFrameNumber != mLastFrameNumberReceived + 1) { - status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, - ms2ns(500)); - if (result != NO_ERROR) { - ALOGE("[%s] Timed out waiting on callback", mName.string()); - } - } - - mQueueItems.push_back(item); - android_atomic_inc(&mQueuedFrames); +bool BufferLayer::isProtected() const { + const sp<GraphicBuffer>& buffer(mActiveBuffer); + return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); +} - // Wake up any pending callbacks - mLastFrameNumberReceived = item.mFrameNumber; - mQueueItemCondition.broadcast(); +bool BufferLayer::latchUnsignaledBuffers() { + static bool propertyLoaded = false; + static bool latch = false; + static std::mutex mutex; + std::lock_guard<std::mutex> lock(mutex); + if (!propertyLoaded) { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.latch_unsignaled", value, "0"); + latch = atoi(value); + propertyLoaded = true; } - - mFlinger->signalLayerUpdate(); + return latch; } -void BufferLayer::onFrameReplaced(const BufferItem& item) { - { // Autolock scope - Mutex::Autolock lock(mQueueItemLock); +// h/w composer set-up +bool BufferLayer::allTransactionsSignaled() { + auto headFrameNumber = getHeadFrameNumber(); + bool matchingFramesFound = false; + bool allTransactionsApplied = true; + Mutex::Autolock lock(mLocalSyncPointMutex); - // Ensure that callbacks are handled in order - while (item.mFrameNumber != mLastFrameNumberReceived + 1) { - status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, - ms2ns(500)); - if (result != NO_ERROR) { - ALOGE("[%s] Timed out waiting on callback", mName.string()); - } + for (auto& point : mLocalSyncPoints) { + if (point->getFrameNumber() > headFrameNumber) { + break; } + matchingFramesFound = true; - if (mQueueItems.empty()) { - ALOGE("Can't replace a frame on an empty queue"); - return; + if (!point->frameIsAvailable()) { + // We haven't notified the remote layer that the frame for + // this point is available yet. Notify it now, and then + // abort this attempt to latch. + point->setFrameAvailable(); + allTransactionsApplied = false; + break; } - mQueueItems.editItemAt(mQueueItems.size() - 1) = item; - // Wake up any pending callbacks - mLastFrameNumberReceived = item.mFrameNumber; - mQueueItemCondition.broadcast(); - } -} - -void BufferLayer::onSidebandStreamChanged() { - if (android_atomic_release_cas(false, true, &mSidebandStreamChanged) == 0) { - // mSidebandStreamChanged was false - mFlinger->signalLayerUpdate(); + allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied(); } -} - -bool BufferLayer::needsFiltering(const RenderArea& renderArea) const { - return mNeedsFiltering || renderArea.needsFiltering(); + return !matchingFramesFound || allTransactionsApplied; } // As documented in libhardware header, formats in the range @@ -824,11 +601,8 @@ bool BufferLayer::getOpacityForFormat(uint32_t format) { return true; } -bool BufferLayer::isHdrY410() const { - // pixel format is HDR Y410 masquerading as RGBA_1010102 - return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ && - mConsumer->getCurrentApi() == NATIVE_WINDOW_API_MEDIA && - getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102); +bool BufferLayer::needsFiltering(const RenderArea& renderArea) const { + return mNeedsFiltering || renderArea.needsFiltering(); } void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const { @@ -853,27 +627,18 @@ void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityT */ const Rect bounds{computeBounds()}; // Rounds from FloatRect - Transform t = getTransform(); + ui::Transform t = getTransform(); Rect win = bounds; - if (!s.finalCrop.isEmpty()) { - win = t.transform(win); - if (!win.intersect(s.finalCrop, &win)) { - win.clear(); - } - win = t.inverse().transform(win); - if (!win.intersect(bounds, &win)) { - win.clear(); - } - } - float left = float(win.left) / float(s.active.w); - float top = float(win.top) / float(s.active.h); - float right = float(win.right) / float(s.active.w); - float bottom = float(win.bottom) / float(s.active.h); + float left = float(win.left) / float(getActiveWidth(s)); + float top = float(win.top) / float(getActiveHeight(s)); + float right = float(win.right) / float(getActiveWidth(s)); + float bottom = float(win.bottom) / float(getActiveHeight(s)); // TODO: we probably want to generate the texture coords with the mesh // here we assume that we only have 4 vertices - Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>()); + renderengine::Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>()); + // flip texcoords vertically because BufferLayerConsumer expects them to be in GL convention texCoords[0] = vec2(left, 1.0f - top); texCoords[1] = vec2(left, 1.0f - bottom); texCoords[2] = vec2(right, 1.0f - bottom); @@ -894,116 +659,14 @@ void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityT engine.setSourceY410BT2020(false); } -uint32_t BufferLayer::getProducerStickyTransform() const { - int producerStickyTransform = 0; - int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform); - if (ret != OK) { - ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__, - strerror(-ret), ret); - return 0; - } - return static_cast<uint32_t>(producerStickyTransform); -} - -bool BufferLayer::latchUnsignaledBuffers() { - static bool propertyLoaded = false; - static bool latch = false; - static std::mutex mutex; - std::lock_guard<std::mutex> lock(mutex); - if (!propertyLoaded) { - char value[PROPERTY_VALUE_MAX] = {}; - property_get("debug.sf.latch_unsignaled", value, "0"); - latch = atoi(value); - propertyLoaded = true; - } - return latch; -} - uint64_t BufferLayer::getHeadFrameNumber() const { - Mutex::Autolock lock(mQueueItemLock); - if (!mQueueItems.empty()) { - return mQueueItems[0].mFrameNumber; + if (hasDrawingBuffer()) { + return getFrameNumber(); } else { return mCurrentFrameNumber; } } -bool BufferLayer::headFenceHasSignaled() const { - if (latchUnsignaledBuffers()) { - return true; - } - - Mutex::Autolock lock(mQueueItemLock); - if (mQueueItems.empty()) { - return true; - } - if (mQueueItems[0].mIsDroppable) { - // Even though this buffer's fence may not have signaled yet, it could - // be replaced by another buffer before it has a chance to, which means - // that it's possible to get into a situation where a buffer is never - // able to be latched. To avoid this, grab this buffer anyway. - return true; - } - return mQueueItems[0].mFenceTime->getSignalTime() != - Fence::SIGNAL_TIME_PENDING; -} - -uint32_t BufferLayer::getEffectiveScalingMode() const { - if (mOverrideScalingMode >= 0) { - return mOverrideScalingMode; - } - return mCurrentScalingMode; -} - -// ---------------------------------------------------------------------------- -// transaction -// ---------------------------------------------------------------------------- - -void BufferLayer::notifyAvailableFrames() { - auto headFrameNumber = getHeadFrameNumber(); - bool headFenceSignaled = headFenceHasSignaled(); - Mutex::Autolock lock(mLocalSyncPointMutex); - for (auto& point : mLocalSyncPoints) { - if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) { - point->setFrameAvailable(); - } - } -} - -sp<IGraphicBufferProducer> BufferLayer::getProducer() const { - return mProducer; -} - -// --------------------------------------------------------------------------- -// h/w composer set-up -// --------------------------------------------------------------------------- - -bool BufferLayer::allTransactionsSignaled() { - auto headFrameNumber = getHeadFrameNumber(); - bool matchingFramesFound = false; - bool allTransactionsApplied = true; - Mutex::Autolock lock(mLocalSyncPointMutex); - - for (auto& point : mLocalSyncPoints) { - if (point->getFrameNumber() > headFrameNumber) { - break; - } - matchingFramesFound = true; - - if (!point->frameIsAvailable()) { - // We haven't notified the remote layer that the frame for - // this point is available yet. Notify it now, and then - // abort this attempt to latch. - point->setFrameAvailable(); - allTransactionsApplied = false; - break; - } - - allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied(); - } - return !matchingFramesFound || allTransactionsApplied; -} - } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index bf0ca69253..d000d85a2d 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -24,14 +24,12 @@ #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" -#include "RenderEngine/Mesh.h" -#include "RenderEngine/Texture.h" #include "SurfaceFlinger.h" -#include "Transform.h" #include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> - +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> @@ -41,158 +39,153 @@ #include <utils/String8.h> #include <utils/Timers.h> +#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE + #include <stdint.h> #include <sys/types.h> #include <list> namespace android { -/* - * A new BufferQueue and a new BufferLayerConsumer are created when the - * BufferLayer is first referenced. - * - * This also implements onFrameAvailable(), which notifies SurfaceFlinger - * that new data has arrived. - */ -class BufferLayer : public Layer, public BufferLayerConsumer::ContentsChangedListener { +class BufferLayer : public Layer { public: - BufferLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags); - + explicit BufferLayer(const LayerCreationArgs& args); ~BufferLayer() override; - // If we have received a new buffer this frame, we will pass its surface - // damage down to hardware composer. Otherwise, we must send a region with - // one empty rect. - void useSurfaceDamage(); - void useEmptyDamage(); - // ----------------------------------------------------------------------- // Overriden from Layer // ----------------------------------------------------------------------- +public: + // If we have received a new buffer this frame, we will pass its surface + // damage down to hardware composer. Otherwise, we must send a region with + // one empty rect. + void useSurfaceDamage() override; + void useEmptyDamage() override; - /* - * getTypeId - Provide unique string for each class type in the Layer - * hierarchy - */ + // getTypeId - Provide unique string for each class type in the Layer + // hierarchy const char* getTypeId() const override { return "BufferLayer"; } - /* - * isProtected - true if the layer may contain protected content in the - * GRALLOC_USAGE_PROTECTED sense. - */ - bool isProtected() const; + bool isOpaque(const Layer::State& s) const override; - /* - * isVisible - true if this layer is visible, false otherwise - */ + // isVisible - true if this layer is visible, false otherwise bool isVisible() const override; - /* - * isFixedSize - true if content has a fixed size - */ + // isFixedSize - true if content has a fixed size bool isFixedSize() const override; - // the this layer's size and format - status_t setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); - - /* - * onDraw - draws the surface. - */ + // onDraw - draws the surface. void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const override; + bool useIdentityTransform) override; - void onLayerDisplayed(const sp<Fence>& releaseFence) override; + bool isHdrY410() const override; + + void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; - void abandon() override; - bool shouldPresentNow(const DispSync& dispSync) const override; - void setTransformHint(uint32_t orientation) const override; + bool onPreComposition(nsecs_t refreshStartTime) override; bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, const CompositorTiming& compositorTiming) override; - std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override; - bool getTransformToDisplayInverse() const override; -public: - bool onPreComposition(nsecs_t refreshStartTime) override; - - // If a buffer was replaced this frame, release the former buffer - void releasePendingBuffer(nsecs_t dequeueReadyTime); + // latchBuffer - called each time the screen is redrawn and returns whether + // the visible regions need to be recomputed (this is a fairly heavy + // operation, so this should be set only if needed). Typically this is used + // to figure out if the content or size of a surface has changed. + // If there was a GL composition step rendering the previous frame, then + // releaseFence will be populated with a native fence that fires when + // composition has completed. + Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override; - /* - * latchBuffer - called each time the screen is redrawn and returns whether - * the visible regions need to be recomputed (this is a fairly heavy - * operation, so this should be set only if needed). Typically this is used - * to figure out if the content or size of a surface has changed. - */ - Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override; bool isBufferLatched() const override { return mRefreshPending; } - void setDefaultBufferSize(uint32_t w, uint32_t h) override; - bool isHdrY410() const override; + void notifyAvailableFrames() override; - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + bool hasReadyFrame() const override; - bool isOpaque(const Layer::State& s) const override; + // Returns the current scaling mode, unless mOverrideScalingMode + // is set, in which case, it returns mOverrideScalingMode + uint32_t getEffectiveScalingMode() const override; + // ----------------------------------------------------------------------- + // ----------------------------------------------------------------------- + // Functions that must be implemented by derived classes + // ----------------------------------------------------------------------- private: - void onFirstRef() override; + virtual bool fenceHasSignaled() const = 0; - // Interface implementation for - // BufferLayerConsumer::ContentsChangedListener - void onFrameAvailable(const BufferItem& item) override; - void onFrameReplaced(const BufferItem& item) override; - void onSidebandStreamChanged() override; + virtual nsecs_t getDesiredPresentTime() = 0; + virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0; - // needsLinearFiltering - true if this surface's state requires filtering - bool needsFiltering(const RenderArea& renderArea) const; + virtual void getDrawingTransformMatrix(float *matrix) = 0; + virtual uint32_t getDrawingTransform() const = 0; + virtual ui::Dataspace getDrawingDataSpace() const = 0; + virtual Rect getDrawingCrop() const = 0; + virtual uint32_t getDrawingScalingMode() const = 0; + virtual Region getDrawingSurfaceDamage() const = 0; + virtual const HdrMetadata& getDrawingHdrMetadata() const = 0; + virtual int getDrawingApi() const = 0; + virtual PixelFormat getPixelFormat() const = 0; - static bool getOpacityForFormat(uint32_t format); + virtual uint64_t getFrameNumber() const = 0; - // drawing - void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const; + virtual bool getAutoRefresh() const = 0; + virtual bool getSidebandStreamChanged() const = 0; - // Temporary - Used only for LEGACY camera mode. - uint32_t getProducerStickyTransform() const; + virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) = 0; - // Loads the corresponding system property once per process - static bool latchUnsignaledBuffers(); + virtual bool hasDrawingBuffer() const = 0; - uint64_t getHeadFrameNumber() const; - bool headFenceHasSignaled() const; + virtual void setFilteringEnabled(bool enabled) = 0; - // Returns the current scaling mode, unless mOverrideScalingMode - // is set, in which case, it returns mOverrideScalingMode - uint32_t getEffectiveScalingMode() const override; + virtual status_t bindTextureImage() = 0; + virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& flushFence) = 0; -public: - void notifyAvailableFrames() override; + virtual status_t updateActiveBuffer() = 0; + virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; - PixelFormat getPixelFormat() const override { return mFormat; } - sp<IGraphicBufferProducer> getProducer() const; + virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& display) = 0; -private: - sp<BufferLayerConsumer> mConsumer; + // ----------------------------------------------------------------------- + +public: + // isProtected - true if the layer may contain protected content in the + // GRALLOC_USAGE_PROTECTED sense. + bool isProtected() const; + +protected: + // Loads the corresponding system property once per process + static bool latchUnsignaledBuffers(); // Check all of the local sync points to ensure that all transactions // which need to have been applied prior to the frame which is about to // be latched have signaled bool allTransactionsSignaled(); - sp<IGraphicBufferProducer> mProducer; - // constants - uint32_t mTextureName; // from GLES - PixelFormat mFormat; + static bool getOpacityForFormat(uint32_t format); + + // from GLES + const uint32_t mTextureName; + +private: + // needsLinearFiltering - true if this surface's state requires filtering + bool needsFiltering(const RenderArea& renderArea) const; + + // drawing + void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const; + + uint64_t getHeadFrameNumber() const; + + uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; + + // main thread. + bool mBufferLatched{false}; // TODO: Use mActiveBuffer? - // main thread - uint32_t mCurrentScalingMode; - bool mBufferLatched = false; // TODO: Use mActiveBuffer? - uint64_t mPreviousFrameNumber; // Only accessed on the main thread. // The texture used to draw the layer in GLES composition mode - mutable Texture mTexture; + mutable renderengine::Texture mTexture; - bool mUpdateTexImageFailed; // This is only accessed on the main thread. - bool mRefreshPending; + bool mRefreshPending{false}; }; } // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 87333d0ffd..6826050d86 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -20,11 +20,8 @@ //#define LOG_NDEBUG 0 #include "BufferLayerConsumer.h" - -#include "DispSync.h" #include "Layer.h" -#include "RenderEngine/Image.h" -#include "RenderEngine/RenderEngine.h" +#include "Scheduler/DispSync.h" #include <inttypes.h> @@ -38,10 +35,9 @@ #include <gui/GLConsumer.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> - #include <private/gui/ComposerService.h> -#include <private/gui/SyncFeatures.h> - +#include <renderengine/Image.h> +#include <renderengine/RenderEngine.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/Trace.h> @@ -58,7 +54,8 @@ namespace android { static const mat4 mtxIdentity; BufferLayerConsumer::BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, - RE::RenderEngine& engine, uint32_t tex, Layer* layer) + renderengine::RenderEngine& engine, uint32_t tex, + Layer* layer) : ConsumerBase(bq, false), mCurrentCrop(Rect::EMPTY_RECT), mCurrentTransform(0), @@ -101,56 +98,10 @@ void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedLis mContentsChangedListener = listener; } -// We need to determine the time when a buffer acquired now will be -// displayed. This can be calculated: -// time when previous buffer's actual-present fence was signaled -// + current display refresh rate * HWC latency -// + a little extra padding -// -// Buffer producers are expected to set their desired presentation time -// based on choreographer time stamps, which (coming from vsync events) -// will be slightly later then the actual-present timing. If we get a -// desired-present time that is unintentionally a hair after the next -// vsync, we'll hold the frame when we really want to display it. We -// need to take the offset between actual-present and reported-vsync -// into account. -// -// If the system is configured without a DispSync phase offset for the app, -// we also want to throw in a bit of padding to avoid edge cases where we -// just barely miss. We want to do it here, not in every app. A major -// source of trouble is the app's use of the display's ideal refresh time -// (via Display.getRefreshRate()), which could be off of the actual refresh -// by a few percent, with the error multiplied by the number of frames -// between now and when the buffer should be displayed. -// -// If the refresh reported to the app has a phase offset, we shouldn't need -// to tweak anything here. -nsecs_t BufferLayerConsumer::computeExpectedPresent(const DispSync& dispSync) { - // The HWC doesn't currently have a way to report additional latency. - // Assume that whatever we submit now will appear right after the flip. - // For a smart panel this might be 1. This is expressed in frames, - // rather than time, because we expect to have a constant frame delay - // regardless of the refresh rate. - const uint32_t hwcLatency = 0; - - // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC). - const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency); - - // The DispSync time is already adjusted for the difference between - // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so - // we don't need to factor that in here. Pad a little to avoid - // weird effects if apps might be requesting times right on the edge. - nsecs_t extraPadding = 0; - if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) { - extraPadding = 1000000; // 1ms (6% of 60Hz) - } - - return nextRefresh + extraPadding; -} - -status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, +status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, bool* autoRefresh, bool* queuedBuffer, - uint64_t maxFrameNumber) { + uint64_t maxFrameNumber, + const sp<Fence>& releaseFence) { ATRACE_CALL(); BLC_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -160,18 +111,12 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const Dis return NO_INIT; } - // Make sure RenderEngine is current - if (!mRE.isCurrent()) { - BLC_LOGE("updateTexImage: RenderEngine is not current"); - return INVALID_OPERATION; - } - BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - status_t err = acquireBufferLocked(&item, computeExpectedPresent(dispSync), maxFrameNumber); + status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { err = NO_ERROR; @@ -201,12 +146,12 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const Dis } // Release the previous buffer. - err = updateAndReleaseLocked(item, &mPendingRelease); + err = updateAndReleaseLocked(item, &mPendingRelease, releaseFence); if (err != NO_ERROR) { return err; } - if (!SyncFeatures::getInstance().useNativeFenceSync()) { + if (!mRE.useNativeFenceSync()) { // Bind the new buffer to the GL texture. // // Older devices require the "implicit" synchronization provided @@ -234,8 +179,7 @@ void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { return; } - auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer - : mCurrentTextureImage->graphicBuffer(); + auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer; auto err = addReleaseFence(slot, buffer, fence); if (err != OK) { BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -271,32 +215,24 @@ status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t pres } // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before, so any prior EglImage created is using a stale buffer. This - // replaces any old EglImage with a new one (using the new buffer). + // before. if (item->mGraphicBuffer != nullptr) { - mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE); + mImages[item->mSlot] = nullptr; } return NO_ERROR; } -bool BufferLayerConsumer::canUseImageCrop(const Rect& crop) const { - // If the crop rect is not at the origin, we can't set the crop on the - // EGLImage because that's not allowed by the EGL_ANDROID_image_crop - // extension. In the future we can add a layered extension that - // removes this restriction if there is hardware that can support it. - return mRE.supportsImageCrop() && crop.left == 0 && crop.top == 0; -} - status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, - PendingRelease* pendingRelease) { + PendingRelease* pendingRelease, + const sp<Fence>& releaseFence) { status_t err = NO_ERROR; int slot = item.mSlot; // Do whatever sync ops we need to do before releasing the old slot. if (slot != mCurrentTexture) { - err = syncForReleaseLocked(); + err = syncForReleaseLocked(releaseFence); if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. @@ -308,19 +244,18 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, } BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0, - slot, mSlots[slot].mGraphicBuffer->handle); + mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->handle : 0, slot, + mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to // releaseBufferLocked() if we're in shared buffer mode and both buffers are // the same. - sp<Image> nextTextureImage = mImages[slot]; + sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer; // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { - status_t status = - releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer()); + status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer); if (status < NO_ERROR) { BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -329,14 +264,15 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, } } else { pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->graphicBuffer = mCurrentTextureBuffer; pendingRelease->isPending = true; } } // Update the BufferLayerConsumer state. mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; + mCurrentTextureBuffer = nextTextureBuffer; + mCurrentTextureImageFreed = nullptr; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; @@ -357,41 +293,63 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, status_t BufferLayerConsumer::bindTextureImageLocked() { ATRACE_CALL(); + mRE.checkErrors(); - if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + // It is possible for the current slot's buffer to be freed before a new one + // is bound. In that scenario we still want to bind the image. + if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) { BLC_LOGE("bindTextureImage: no currently-bound texture"); mRE.bindExternalTextureImage(mTexName, *mRE.createImage()); return NO_INIT; } - const Rect& imageCrop = canUseImageCrop(mCurrentCrop) ? mCurrentCrop : Rect::EMPTY_RECT; - status_t err = mCurrentTextureImage->createIfNeeded(imageCrop); - if (err != NO_ERROR) { - BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture); - mRE.bindExternalTextureImage(mTexName, *mRE.createImage()); - return UNKNOWN_ERROR; + renderengine::Image* imageToRender; + + // mCurrentTextureImageFreed is non-null iff mCurrentTexture == + // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check. + if (mCurrentTextureImageFreed) { + imageToRender = mCurrentTextureImageFreed.get(); + } else if (mImages[mCurrentTexture]) { + imageToRender = mImages[mCurrentTexture].get(); + } else { + std::unique_ptr<renderengine::Image> image = mRE.createImage(); + bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(), + mCurrentTextureBuffer->getUsage() & + GRALLOC_USAGE_PROTECTED); + if (!success) { + BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 + " fmt=%d", + mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(), + mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(), + mCurrentTextureBuffer->getPixelFormat()); + BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture); + mRE.bindExternalTextureImage(mTexName, *image); + return UNKNOWN_ERROR; + } + imageToRender = image.get(); + // Cache the image here so that we can reuse it. + mImages[mCurrentTexture] = std::move(image); } - mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image()); + mRE.bindExternalTextureImage(mTexName, *imageToRender); // Wait for the new buffer to be ready. return doFenceWaitLocked(); } -status_t BufferLayerConsumer::syncForReleaseLocked() { +status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) { BLC_LOGV("syncForReleaseLocked"); if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - base::unique_fd fenceFd = mRE.flush(); - if (fenceFd == -1) { + if (mRE.useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { + // TODO(alecmouri): fail further upstream if the fence is invalid + if (!releaseFence->isValid()) { BLC_LOGE("syncForReleaseLocked: failed to flush RenderEngine"); return UNKNOWN_ERROR; } - sp<Fence> fence(new Fence(std::move(fenceFd))); - status_t err = addReleaseFenceLocked(mCurrentTexture, - mCurrentTextureImage->graphicBuffer(), fence); + status_t err = + addReleaseFenceLocked(mCurrentTexture, mCurrentTextureBuffer, releaseFence); if (err != OK) { BLC_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", @@ -418,26 +376,23 @@ void BufferLayerConsumer::setFilteringEnabled(bool enabled) { bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute && mCurrentTextureImage == nullptr) { - BLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == nullptr"); + if (needsRecompute && mCurrentTextureBuffer == nullptr) { + BLC_LOGD("setFilteringEnabled called with mCurrentTextureBuffer == nullptr"); } - if (needsRecompute && mCurrentTextureImage != nullptr) { + if (needsRecompute && mCurrentTextureBuffer != nullptr) { computeCurrentTransformMatrixLocked(); } } void BufferLayerConsumer::computeCurrentTransformMatrixLocked() { BLC_LOGV("computeCurrentTransformMatrixLocked"); - sp<GraphicBuffer> buf = - (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); - if (buf == nullptr) { + if (mCurrentTextureBuffer == nullptr) { BLC_LOGD("computeCurrentTransformMatrixLocked: " - "mCurrentTextureImage is nullptr"); + "mCurrentTextureBuffer is nullptr"); } - const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop; - GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform, - mFilteringEnabled); + GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop, + mCurrentTransform, mFilteringEnabled); } nsecs_t BufferLayerConsumer::getTimestamp() { @@ -485,7 +440,7 @@ sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const { *outSlot = mCurrentTexture; } - return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); + return mCurrentTextureBuffer; } Rect BufferLayerConsumer::getCurrentCrop() const { @@ -516,13 +471,8 @@ std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const { } status_t BufferLayerConsumer::doFenceWaitLocked() const { - if (!mRE.isCurrent()) { - BLC_LOGE("doFenceWait: RenderEngine is not current"); - return INVALID_OPERATION; - } - if (mCurrentFence->isValid()) { - if (SyncFeatures::getInstance().useWaitSync()) { + if (mRE.useWaitSync()) { base::unique_fd fenceFd(mCurrentFence->dup()); if (fenceFd == -1) { BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno); @@ -548,8 +498,10 @@ void BufferLayerConsumer::freeBufferLocked(int slotIndex) { BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImageFreed = std::move(mImages[slotIndex]); + } else { + mImages[slotIndex] = nullptr; } - mImages[slotIndex].clear(); ConsumerBase::freeBufferLocked(slotIndex); } @@ -588,7 +540,7 @@ void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* ne void BufferLayerConsumer::abandonLocked() { BLC_LOGV("abandonLocked"); - mCurrentTextureImage.clear(); + mCurrentTextureBuffer.clear(); ConsumerBase::abandonLocked(); } @@ -606,39 +558,4 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const ConsumerBase::dumpLocked(result, prefix); } -BufferLayerConsumer::Image::Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine) - : mGraphicBuffer(graphicBuffer), - mImage{engine.createImage()}, - mCreated(false), - mCropWidth(0), - mCropHeight(0) {} - -BufferLayerConsumer::Image::~Image() = default; - -status_t BufferLayerConsumer::Image::createIfNeeded(const Rect& imageCrop) { - const int32_t cropWidth = imageCrop.width(); - const int32_t cropHeight = imageCrop.height(); - if (mCreated && mCropWidth == cropWidth && mCropHeight == cropHeight) { - return OK; - } - - mCreated = mImage->setNativeWindowBuffer(mGraphicBuffer->getNativeBuffer(), - mGraphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED, - cropWidth, cropHeight); - if (mCreated) { - mCropWidth = cropWidth; - mCropHeight = cropHeight; - } else { - mCropWidth = 0; - mCropHeight = 0; - - const sp<GraphicBuffer>& buffer = mGraphicBuffer; - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); - } - - return mCreated ? OK : UNKNOWN_ERROR; -} - }; // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h index f81cdb1d91..ea46245ea4 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -37,10 +37,10 @@ class DispSync; class Layer; class String8; -namespace RE { +namespace renderengine { class RenderEngine; class Image; -} // namespace RE +} // namespace renderengine /* * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue, @@ -73,15 +73,13 @@ public: // BufferLayerConsumer constructs a new BufferLayerConsumer object. The // tex parameter indicates the name of the RenderEngine texture to which // images are to be streamed. - BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RE::RenderEngine& engine, + BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine, uint32_t tex, Layer* layer); // Sets the contents changed listener. This should be used instead of // ConsumerBase::setFrameAvailableListener(). void setContentsChangedListener(const wp<ContentsChangedListener>& listener); - nsecs_t computeExpectedPresent(const DispSync& dispSync); - // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. // @@ -93,8 +91,9 @@ public: // Unlike the GLConsumer version, this version takes a functor that may be // used to reject the newly acquired buffer. It also does not bind the // RenderEngine texture until bindTextureImage is called. - status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, bool* autoRefresh, - bool* queuedBuffer, uint64_t maxFrameNumber); + status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, + bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber, + const sp<Fence>& releaseFence); // See BufferLayerConsumer::bindTextureImageLocked(). status_t bindTextureImage(); @@ -186,8 +185,7 @@ protected: // specific info in addition to the ConsumerBase behavior. virtual void dumpLocked(String8& result, const char* prefix) const; - // acquireBufferLocked overrides the ConsumerBase method to update the - // mImages array in addition to the ConsumerBase behavior. + // See ConsumerBase::acquireBufferLocked virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, uint64_t maxFrameNumber = 0) override; @@ -208,56 +206,17 @@ protected: // completion of the method will instead be returned to the caller, so that // it may call releaseBufferLocked itself later. status_t updateAndReleaseLocked(const BufferItem& item, - PendingRelease* pendingRelease = nullptr); + PendingRelease* pendingRelease = nullptr, + const sp<Fence>& releaseFence = Fence::NO_FENCE); - // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. Uses - // mCurrentTexture if it's set, mCurrentTextureImage if not. If the - // bind succeeds, this calls doFenceWait. + // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. + // If the bind succeeds, this calls doFenceWait. status_t bindTextureImageLocked(); private: - // Image is a utility class for tracking and creating RE::Images. There - // is primarily just one image per slot, but there is also special cases: - // - After freeBuffer, we must still keep the current image/buffer - // Reference counting RE::Images lets us handle all these cases easily while - // also only creating new RE::Images from buffers when required. - class Image : public LightRefBase<Image> { - public: - Image(sp<GraphicBuffer> graphicBuffer, RE::RenderEngine& engine); - - Image(const Image& rhs) = delete; - Image& operator=(const Image& rhs) = delete; - - // createIfNeeded creates an RE::Image if required (we haven't created - // one yet, or the crop-rect has changed). - status_t createIfNeeded(const Rect& imageCrop); - - const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } - const native_handle* graphicBufferHandle() { - return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; - } - - const RE::Image& image() const { return *mImage; } - - private: - // Only allow instantiation using ref counting. - friend class LightRefBase<Image>; - virtual ~Image(); - - // mGraphicBuffer is the buffer that was used to create this image. - sp<GraphicBuffer> mGraphicBuffer; - - // mImage is the image created from mGraphicBuffer. - std::unique_ptr<RE::Image> mImage; - bool mCreated; - int32_t mCropWidth; - int32_t mCropHeight; - }; - // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in - // that slot and destroy the RE::Image in that slot. Otherwise it has no - // effect. + // that slot. Otherwise it has no effect. // // This method must be called with mMutex locked. virtual void freeBufferLocked(int slotIndex); @@ -283,7 +242,7 @@ private: // current slot from RenderEngine. If needed it will set the current // slot's fence to guard against a producer accessing the buffer before // the outstanding accesses have completed. - status_t syncForReleaseLocked(); + status_t syncForReleaseLocked(const sp<Fence>& releaseFence); // The default consumer usage flags that BufferLayerConsumer always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed @@ -291,10 +250,10 @@ private: // consume buffers as hardware textures. static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - // mCurrentTextureImage is the Image/buffer of the current texture. It's + // mCurrentTextureImage is the buffer containing the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. - sp<Image> mCurrentTextureImage; + sp<GraphicBuffer> mCurrentTextureBuffer; // mCurrentCrop is the crop rectangle that applies to the current texture. // It gets set each time updateTexImage is called. @@ -352,7 +311,7 @@ private: // setFilteringEnabled(). bool mFilteringEnabled; - RE::RenderEngine& mRE; + renderengine::RenderEngine& mRE; // mTexName is the name of the RenderEngine texture to which streamed // images will be bound when bindTexImage is called. It is set at @@ -364,15 +323,6 @@ private: wp<ContentsChangedListener> mContentsChangedListener; - // mImages stores the buffers that have been allocated by the BufferQueue - // for each buffer slot. It is initialized to null pointers, and gets - // filled in with the result of BufferQueue::acquire when the - // client dequeues a buffer from a - // slot that has not yet been used. The buffer allocated to a slot will also - // be replaced if the requested buffer usage or geometry differs from that - // of the buffer allocated to a slot. - sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; - // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, @@ -381,6 +331,17 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; + // Cached image used for rendering the current texture through GPU + // composition, which contains the cached image after freeBufferLocked is + // called on the current buffer. Whenever latchBuffer is called, this is + // expected to be cleared. Then, if bindTexImage is called before the next + // buffer is acquired, then this image is bound. + std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed; + + // Cached images used for rendering the current texture through GPU + // composition. + std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; + // A release that is pending on the receipt of a new release fence from // presentDisplay PendingRelease mPendingRelease; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp new file mode 100644 index 0000000000..e592a8bf98 --- /dev/null +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -0,0 +1,482 @@ +/* + * 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 "BufferQueueLayer.h" +#include "LayerRejecter.h" + +#include <system/window.h> + +namespace android { + +BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {} + +BufferQueueLayer::~BufferQueueLayer() = default; + +// ----------------------------------------------------------------------- +// Interface implementation for Layer +// ----------------------------------------------------------------------- + +void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { + mConsumer->setReleaseFence(releaseFence); +} + +void BufferQueueLayer::abandon() { + mConsumer->abandon(); +} + +void BufferQueueLayer::setTransformHint(uint32_t orientation) const { + mConsumer->setTransformHint(orientation); +} + +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)", mName.string(), result); + return {}; + } + return history; +} + +bool BufferQueueLayer::getTransformToDisplayInverse() const { + return mConsumer->getTransformToDisplayInverse(); +} + +void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { + if (!mConsumer->releasePendingBuffer()) { + return; + } + + auto releaseFenceTime = std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence()); + mReleaseTimeline.updateSignalTimes(); + mReleaseTimeline.push(releaseFenceTime); + + Mutex::Autolock lock(mFrameEventHistoryMutex); + if (mPreviousFrameNumber != 0) { + mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime, + std::move(releaseFenceTime)); + } +} + +void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) { + mConsumer->setDefaultBufferSize(w, h); +} + +int32_t BufferQueueLayer::getQueuedFrameCount() const { + return mQueuedFrames; +} + +bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + if (!hasDrawingBuffer()) { + return false; + } + + Mutex::Autolock lock(mQueueItemLock); + + const int64_t addedTime = mQueueItems[0].mTimestamp; + + // Ignore timestamps more than a second in the future + const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1)); + ALOGW_IF(!isPlausible, + "[%s] Timestamp %" PRId64 " seems implausible " + "relative to expectedPresent %" PRId64, + mName.string(), addedTime, expectedPresentTime); + + const bool isDue = addedTime < expectedPresentTime; + return isDue || !isPlausible; +} + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayer +// ----------------------------------------------------------------------- + +bool BufferQueueLayer::fenceHasSignaled() const { + if (latchUnsignaledBuffers()) { + return true; + } + + if (!hasDrawingBuffer()) { + return true; + } + + Mutex::Autolock lock(mQueueItemLock); + if (mQueueItems[0].mIsDroppable) { + // Even though this buffer's fence may not have signaled yet, it could + // be replaced by another buffer before it has a chance to, which means + // that it's possible to get into a situation where a buffer is never + // able to be latched. To avoid this, grab this buffer anyway. + return true; + } + return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; +} + +nsecs_t BufferQueueLayer::getDesiredPresentTime() { + return mConsumer->getTimestamp(); +} + +std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTime() const { + return mConsumer->getCurrentFenceTime(); +} + +void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) { + return mConsumer->getTransformMatrix(matrix); +} + +// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's +// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state +// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's +// current buffer so the consumer functions start with "getCurrent". +// +// This results in the rather confusing functions below. +uint32_t BufferQueueLayer::getDrawingTransform() const { + return mConsumer->getCurrentTransform(); +} + +ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const { + return mConsumer->getCurrentDataSpace(); +} + +Rect BufferQueueLayer::getDrawingCrop() const { + return mConsumer->getCurrentCrop(); +} + +uint32_t BufferQueueLayer::getDrawingScalingMode() const { + return mConsumer->getCurrentScalingMode(); +} + +Region BufferQueueLayer::getDrawingSurfaceDamage() const { + return mConsumer->getSurfaceDamage(); +} + +const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const { + return mConsumer->getCurrentHdrMetadata(); +} + +int BufferQueueLayer::getDrawingApi() const { + return mConsumer->getCurrentApi(); +} + +PixelFormat BufferQueueLayer::getPixelFormat() const { + return mFormat; +} + +uint64_t BufferQueueLayer::getFrameNumber() const { + Mutex::Autolock lock(mQueueItemLock); + return mQueueItems[0].mFrameNumber; +} + +bool BufferQueueLayer::getAutoRefresh() const { + return mAutoRefresh; +} + +bool BufferQueueLayer::getSidebandStreamChanged() const { + return mSidebandStreamChanged; +} + +std::optional<Region> BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + bool sidebandStreamChanged = true; + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { + // mSidebandStreamChanged was changed to false + // replicated in LayerBE until FE/BE is ready to be synchronized + getBE().compositionInfo.hwc.sidebandStream = mConsumer->getSidebandStream(); + if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + recomputeVisibleRegions = true; + + const State& s(getDrawingState()); + return getTransform().transform(Region(Rect(s.active_legacy.w, s.active_legacy.h))); + } + return {}; +} + +bool BufferQueueLayer::hasDrawingBuffer() const { + return mQueuedFrames > 0; +} + +void BufferQueueLayer::setFilteringEnabled(bool enabled) { + return mConsumer->setFilteringEnabled(enabled); +} + +status_t BufferQueueLayer::bindTextureImage() { + return mConsumer->bindTextureImage(); +} + +status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) { + // This boolean is used to make sure that SurfaceFlinger's shadow copy + // of the buffer queue isn't modified when the buffer queue is returning + // BufferItem's that weren't actually queued. This can happen in shared + // buffer mode. + bool queuedBuffer = false; + const int32_t layerID = getSequence(); + LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions, + getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode, + getTransformToDisplayInverse(), mFreezeGeometryUpdates); + + const nsecs_t expectedPresentTime = mFlinger->mUseScheduler + ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime() + : mFlinger->mPrimaryDispSync->expectedPresentTime(); + status_t updateResult = + mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, &queuedBuffer, + mLastFrameNumberReceived, releaseFence); + 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(); + 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); + mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber); + mQueueItems.removeAt(0); + mQueuedFrames--; + } + return BAD_VALUE; + } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) { + // This can occur if something goes wrong when trying to create the + // EGLImage for this buffer. If this happens, the buffer has already + // been released, so we need to clean up the queue and bug out + // early. + if (queuedBuffer) { + Mutex::Autolock lock(mQueueItemLock); + mQueueItems.clear(); + mQueuedFrames = 0; + mTimeStats.clearLayerRecord(layerID); + } + + // Once we have hit this state, the shadow queue may no longer + // correctly reflect the incoming BufferQueue's contents, so even if + // updateTexImage starts working, the only safe course of action is + // to continue to ignore updates. + mUpdateTexImageFailed = true; + + return BAD_VALUE; + } + + if (queuedBuffer) { + // Autolock scope + auto currentFrameNumber = mConsumer->getFrameNumber(); + + Mutex::Autolock lock(mQueueItemLock); + + // Remove any stale buffers that have been dropped during + // updateTexImage + while (mQueueItems[0].mFrameNumber != currentFrameNumber) { + mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber); + mQueueItems.removeAt(0); + mQueuedFrames--; + } + + mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime); + mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime); + + mQueueItems.removeAt(0); + } + + // Decrement the queued-frames count. Signal another event if we + // have more frames pending. + if ((queuedBuffer && mQueuedFrames.fetch_sub(1) > 1) || mAutoRefresh) { + mFlinger->signalLayerUpdate(); + } + + return NO_ERROR; +} + +status_t BufferQueueLayer::updateActiveBuffer() { + // update the active buffer + mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot); + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.mBufferSlot = mActiveBufferSlot; + + if (mActiveBuffer == nullptr) { + // this can only happen if the very first buffer was rejected. + return BAD_VALUE; + } + return NO_ERROR; +} + +status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) { + mPreviousFrameNumber = mCurrentFrameNumber; + mCurrentFrameNumber = mConsumer->getFrameNumber(); + + { + Mutex::Autolock lock(mFrameEventHistoryMutex); + mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime); + } + return NO_ERROR; +} + +void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { + const auto displayId = display->getId(); + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + + uint32_t hwcSlot = 0; + sp<GraphicBuffer> hwcBuffer; + hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer); + + auto acquireFence = mConsumer->getCurrentFence(); + auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), + getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + getBE().compositionInfo.mBufferSlot = mActiveBufferSlot; + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.hwc.fence = acquireFence; +} + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayerConsumer::ContentsChangedListener +// ----------------------------------------------------------------------- + +void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { + // Add this buffer from our internal queue tracker + { // Autolock scope + Mutex::Autolock lock(mQueueItemLock); + // Reset the frame number tracker when we receive the first buffer after + // a frame number reset + if (item.mFrameNumber == 1) { + mLastFrameNumberReceived = 0; + } + + // Ensure that callbacks are handled in order + while (item.mFrameNumber != mLastFrameNumberReceived + 1) { + status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); + if (result != NO_ERROR) { + ALOGE("[%s] Timed out waiting on callback", mName.string()); + } + } + + mQueueItems.push_back(item); + mQueuedFrames++; + + // Wake up any pending callbacks + mLastFrameNumberReceived = item.mFrameNumber; + mQueueItemCondition.broadcast(); + } + + mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(), + item.mGraphicBuffer->getHeight(), item.mFrameNumber); + mFlinger->signalLayerUpdate(); +} + +void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { + { // Autolock scope + Mutex::Autolock lock(mQueueItemLock); + + // Ensure that callbacks are handled in order + while (item.mFrameNumber != mLastFrameNumberReceived + 1) { + status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); + if (result != NO_ERROR) { + ALOGE("[%s] Timed out waiting on callback", mName.string()); + } + } + + if (!hasDrawingBuffer()) { + ALOGE("Can't replace a frame on an empty queue"); + return; + } + mQueueItems.editItemAt(mQueueItems.size() - 1) = item; + + // Wake up any pending callbacks + mLastFrameNumberReceived = item.mFrameNumber; + mQueueItemCondition.broadcast(); + } +} + +void BufferQueueLayer::onSidebandStreamChanged() { + bool sidebandStreamChanged = false; + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) { + // mSidebandStreamChanged was changed to true + mFlinger->signalLayerUpdate(); + } +} + +// ----------------------------------------------------------------------- + +void BufferQueueLayer::onFirstRef() { + BufferLayer::onFirstRef(); + + // Creates a custom BufferQueue for SurfaceFlingerConsumer to use + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer, true); + mProducer = new MonitoredProducer(producer, mFlinger, this); + { + // Grab the SF state lock during this since it's the only safe way to access RenderEngine + Mutex::Autolock lock(mFlinger->mStateLock); + mConsumer = + new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this); + } + mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + mConsumer->setContentsChangedListener(this); + mConsumer->setName(mName); + + if (mFlinger->isLayerTripleBufferingDisabled()) { + mProducer->setMaxDequeuedBufferCount(2); + } + + if (const auto display = mFlinger->getDefaultDisplayDevice()) { + updateTransformHint(display); + } +} + +status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) { + uint32_t const maxSurfaceDims = + std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); + + // never allow a surface larger than what our underlying GL implementation + // can handle. + if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) { + ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h)); + return BAD_VALUE; + } + + mFormat = format; + + setDefaultBufferSize(w, h); + mConsumer->setDefaultBufferFormat(format); + mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + + return NO_ERROR; +} + +sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const { + return mProducer; +} + +uint32_t BufferQueueLayer::getProducerStickyTransform() const { + int producerStickyTransform = 0; + int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform); + if (ret != OK) { + ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__, + strerror(-ret), ret); + return 0; + } + return static_cast<uint32_t>(producerStickyTransform); +} + +} // namespace android diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h new file mode 100644 index 0000000000..abe0bc7c0a --- /dev/null +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -0,0 +1,145 @@ +/* + * 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 "BufferLayer.h" + +#include <utils/String8.h> + +namespace android { + +/* + * A new BufferQueue and a new BufferLayerConsumer are created when the + * BufferLayer is first referenced. + * + * This also implements onFrameAvailable(), which notifies SurfaceFlinger + * that new data has arrived. + */ +class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener { +public: + explicit BufferQueueLayer(const LayerCreationArgs&); + ~BufferQueueLayer() override; + + // ----------------------------------------------------------------------- + // Interface implementation for Layer + // ----------------------------------------------------------------------- +public: + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + + void abandon() override; + + void setTransformHint(uint32_t orientation) const override; + + std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override; + + bool getTransformToDisplayInverse() const override; + + // If a buffer was replaced this frame, release the former buffer + void releasePendingBuffer(nsecs_t dequeueReadyTime) override; + + void setDefaultBufferSize(uint32_t w, uint32_t h) override; + + int32_t getQueuedFrameCount() const override; + + bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Interface implementation for BufferLayer + // ----------------------------------------------------------------------- +public: + bool fenceHasSignaled() const override; + +private: + nsecs_t getDesiredPresentTime() override; + std::shared_ptr<FenceTime> getCurrentFenceTime() const override; + + void getDrawingTransformMatrix(float *matrix) override; + uint32_t getDrawingTransform() const override; + ui::Dataspace getDrawingDataSpace() const override; + Rect getDrawingCrop() const override; + uint32_t getDrawingScalingMode() const override; + Region getDrawingSurfaceDamage() const override; + const HdrMetadata& getDrawingHdrMetadata() const override; + int getDrawingApi() const override; + PixelFormat getPixelFormat() const override; + + uint64_t getFrameNumber() const override; + + bool getAutoRefresh() const override; + bool getSidebandStreamChanged() const override; + + std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override; + + bool hasDrawingBuffer() const override; + + void setFilteringEnabled(bool enabled) override; + + status_t bindTextureImage() override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override; + + status_t updateActiveBuffer() override; + status_t updateFrameNumber(nsecs_t latchTime) override; + + void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override; + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Interface implementation for BufferLayerConsumer::ContentsChangedListener + // ----------------------------------------------------------------------- +protected: + void onFrameAvailable(const BufferItem& item) override; + void onFrameReplaced(const BufferItem& item) override; + void onSidebandStreamChanged() override; + // ----------------------------------------------------------------------- + +public: + status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format); + + sp<IGraphicBufferProducer> getProducer() const; + +private: + // Temporary - Used only for LEGACY camera mode. + uint32_t getProducerStickyTransform() const; + + void onFirstRef() override; + + sp<BufferLayerConsumer> mConsumer; + sp<IGraphicBufferProducer> mProducer; + + PixelFormat mFormat{PIXEL_FORMAT_NONE}; + + // Only accessed on the main thread. + uint64_t mPreviousFrameNumber{0}; + bool mUpdateTexImageFailed{false}; + + // Local copy of the queued contents of the incoming BufferQueue + mutable Mutex mQueueItemLock; + Condition mQueueItemCondition; + Vector<BufferItem> mQueueItems; + std::atomic<uint64_t> mLastFrameNumberReceived{0}; + + bool mAutoRefresh{false}; + int mActiveBufferSlot{BufferQueue::INVALID_BUFFER_SLOT}; + + // thread-safe + std::atomic<int32_t> mQueuedFrames{0}; + std::atomic<bool> mSidebandStreamChanged{false}; +}; + +} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp new file mode 100644 index 0000000000..73098bf2aa --- /dev/null +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -0,0 +1,527 @@ +/* + * Copyright (C) 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. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "BufferStateLayer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "BufferStateLayer.h" + +#include <private/gui/SyncFeatures.h> +#include <renderengine/Image.h> + +namespace android { + +// clang-format off +const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}; +// clang-format on + +BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) {} +BufferStateLayer::~BufferStateLayer() = default; + +// ----------------------------------------------------------------------- +// Interface implementation for Layer +// ----------------------------------------------------------------------- +void BufferStateLayer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) { + // TODO(marissaw): send the release fence back to buffer owner + return; +} + +void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const { + // TODO(marissaw): send the transform hint to buffer owner + return; +} + +void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { + // TODO(marissaw): use this to signal the buffer owner + return; +} + +bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + return hasDrawingBuffer(); +} + +bool BufferStateLayer::getTransformToDisplayInverse() const { + return mCurrentState.transformToDisplayInverse; +} + +void BufferStateLayer::pushPendingState() { + if (!mCurrentState.modified) { + return; + } + mPendingStates.push_back(mCurrentState); + ATRACE_INT(mTransactionName.string(), mPendingStates.size()); +} + +bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) { + const bool stateUpdateAvailable = !mPendingStates.empty(); + while (!mPendingStates.empty()) { + popPendingState(stateToCommit); + } + mCurrentState.modified = false; + return stateUpdateAvailable; +} + +Rect BufferStateLayer::getCrop(const Layer::State& s) const { + return (getEffectiveScalingMode() == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? GLConsumer::scaleDownCrop(s.crop, s.active.w, s.active.h) + : s.crop; +} + +bool BufferStateLayer::setTransform(uint32_t transform) { + if (mCurrentState.transform == transform) return false; + mCurrentState.sequence++; + mCurrentState.transform = transform; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) { + if (mCurrentState.transformToDisplayInverse == transformToDisplayInverse) return false; + mCurrentState.sequence++; + mCurrentState.transformToDisplayInverse = transformToDisplayInverse; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setCrop(const Rect& crop) { + if (mCurrentState.crop == crop) return false; + mCurrentState.sequence++; + mCurrentState.crop = crop; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setBuffer(sp<GraphicBuffer> buffer) { + mCurrentState.sequence++; + mCurrentState.buffer = buffer; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) { + mCurrentState.acquireFence = fence; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) { + if (mCurrentState.dataspace == dataspace) return false; + mCurrentState.sequence++; + mCurrentState.dataspace = dataspace; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) { + if (mCurrentState.hdrMetadata == hdrMetadata) return false; + mCurrentState.sequence++; + mCurrentState.hdrMetadata = hdrMetadata; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) { + mCurrentState.sequence++; + mCurrentState.surfaceDamageRegion = surfaceDamage; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setApi(int32_t api) { + if (mCurrentState.api == api) return false; + mCurrentState.sequence++; + mCurrentState.api = api; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) { + if (mCurrentState.sidebandStream == sidebandStream) return false; + mCurrentState.sequence++; + mCurrentState.sidebandStream = sidebandStream; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + + if (!mSidebandStreamChanged.exchange(true)) { + // mSidebandStreamChanged was false + mFlinger->signalLayerUpdate(); + } + return true; +} + +bool BufferStateLayer::setSize(uint32_t w, uint32_t h) { + if (mCurrentState.active.w == w && mCurrentState.active.h == h) return false; + mCurrentState.active.w = w; + mCurrentState.active.h = h; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setPosition(float x, float y, bool /*immediate*/) { + if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y) + return false; + + mCurrentState.active.transform.set(x, y); + + mCurrentState.sequence++; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { + mCurrentState.transparentRegionHint = transparent; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, + bool allowNonRectPreservingTransforms) { + ui::Transform t; + t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + + if (!allowNonRectPreservingTransforms && !t.preserveRects()) { + ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER ignored"); + return false; + } + + mCurrentState.sequence++; + mCurrentState.active.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayer +// ----------------------------------------------------------------------- +bool BufferStateLayer::fenceHasSignaled() const { + if (latchUnsignaledBuffers()) { + return true; + } + + return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; +} + +nsecs_t BufferStateLayer::getDesiredPresentTime() { + // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics + return 0; +} + +std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const { + return std::make_shared<FenceTime>(getDrawingState().acquireFence); +} + +void BufferStateLayer::getDrawingTransformMatrix(float *matrix) { + std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix); +} + +uint32_t BufferStateLayer::getDrawingTransform() const { + return getDrawingState().transform; +} + +ui::Dataspace BufferStateLayer::getDrawingDataSpace() const { + return getDrawingState().dataspace; +} + +Rect BufferStateLayer::getDrawingCrop() const { + return Rect::INVALID_RECT; +} + +uint32_t BufferStateLayer::getDrawingScalingMode() const { + return NATIVE_WINDOW_SCALING_MODE_FREEZE; +} + +Region BufferStateLayer::getDrawingSurfaceDamage() const { + return getDrawingState().surfaceDamageRegion; +} + +const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const { + return getDrawingState().hdrMetadata; +} + +int BufferStateLayer::getDrawingApi() const { + return getDrawingState().api; +} + +PixelFormat BufferStateLayer::getPixelFormat() const { + return mActiveBuffer->format; +} + +uint64_t BufferStateLayer::getFrameNumber() const { + return mFrameNumber; +} + +bool BufferStateLayer::getAutoRefresh() const { + // TODO(marissaw): support shared buffer mode + return false; +} + +bool BufferStateLayer::getSidebandStreamChanged() const { + return mSidebandStreamChanged.load(); +} + +std::optional<Region> BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + if (mSidebandStreamChanged.exchange(false)) { + const State& s(getDrawingState()); + // mSidebandStreamChanged was true + // replicated in LayerBE until FE/BE is ready to be synchronized + getBE().compositionInfo.hwc.sidebandStream = s.sidebandStream; + if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + recomputeVisibleRegions = true; + + return getTransform().transform(Region(Rect(s.active.w, s.active.h))); + } + return {}; +} + +bool BufferStateLayer::hasDrawingBuffer() const { + return getDrawingState().buffer != nullptr; +} + +void BufferStateLayer::setFilteringEnabled(bool enabled) { + GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop, + mCurrentTransform, enabled); +} + +status_t BufferStateLayer::bindTextureImage() { + const State& s(getDrawingState()); + auto& engine(mFlinger->getRenderEngine()); + + engine.checkErrors(); + + // TODO(marissaw): once buffers are cached, don't create a new image everytime + mTextureImage = engine.createImage(); + + bool created = + mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(), + s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + if (!created) { + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(), + s.buffer->getUsage(), s.buffer->getPixelFormat()); + engine.bindExternalTextureImage(mTextureName, *engine.createImage()); + return NO_INIT; + } + + engine.bindExternalTextureImage(mTextureName, *mTextureImage); + + // Wait for the new buffer to be ready. + if (s.acquireFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync()) { + base::unique_fd fenceFd(s.acquireFence->dup()); + if (fenceFd == -1) { + ALOGE("error dup'ing fence fd: %d", errno); + return -errno; + } + if (!engine.waitFence(std::move(fenceFd))) { + ALOGE("failed to wait on fence fd"); + return UNKNOWN_ERROR; + } + } else { + status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage"); + if (err != NO_ERROR) { + ALOGE("error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, + const sp<Fence>& releaseFence) { + const State& s(getDrawingState()); + + if (!s.buffer) { + return NO_ERROR; + } + + const int32_t layerID = getSequence(); + + // Reject if the layer is invalid + uint32_t bufferWidth = s.buffer->width; + uint32_t bufferHeight = s.buffer->height; + + if (s.transform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + + if (s.transformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + if (mOverrideScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE && + (s.active.w != bufferWidth || s.active.h != bufferHeight)) { + ALOGE("[%s] rejecting buffer: " + "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}", + mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h); + mTimeStats.removeTimeRecord(layerID, getFrameNumber()); + return BAD_VALUE; + } + + // Handle sync fences + if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { + // TODO(alecmouri): Fail somewhere upstream if the fence is invalid. + if (!releaseFence->isValid()) { + mTimeStats.clearLayerRecord(layerID); + return UNKNOWN_ERROR; + } + + // Check status of fences first because merging is expensive. + // Merging an invalid fence with any other fence results in an + // invalid fence. + auto currentStatus = s.acquireFence->getStatus(); + if (currentStatus == Fence::Status::Invalid) { + ALOGE("Existing fence has invalid state"); + mTimeStats.clearLayerRecord(layerID); + return BAD_VALUE; + } + + auto incomingStatus = releaseFence->getStatus(); + if (incomingStatus == Fence::Status::Invalid) { + ALOGE("New fence has invalid state"); + mDrawingState.acquireFence = releaseFence; + mTimeStats.clearLayerRecord(layerID); + 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:%d", mName.string(), mFrameNumber); + sp<Fence> mergedFence = + Fence::merge(fenceName, mDrawingState.acquireFence, releaseFence); + if (!mergedFence.get()) { + ALOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mDrawingState.acquireFence = releaseFence; + mTimeStats.clearLayerRecord(layerID); + return BAD_VALUE; + } + mDrawingState.acquireFence = 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. + mDrawingState.acquireFence = releaseFence; + } + } else { + // Bind the new buffer to the GL texture. + // + // Older devices require the "implicit" synchronization provided + // by glEGLImageTargetTexture2DOES, which this method calls. Newer + // devices will either call this in Layer::onDraw, or (if it's not + // a GL-composited layer) not at all. + status_t err = bindTextureImage(); + if (err != NO_ERROR) { + mTimeStats.clearLayerRecord(layerID); + return BAD_VALUE; + } + } + + // TODO(marissaw): properly support mTimeStats + mTimeStats.setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime); + mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime()); + mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime); + + return NO_ERROR; +} + +status_t BufferStateLayer::updateActiveBuffer() { + const State& s(getDrawingState()); + + if (s.buffer == nullptr) { + return BAD_VALUE; + } + + mActiveBuffer = s.buffer; + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.mBufferSlot = 0; + + return NO_ERROR; +} + +status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) { + // TODO(marissaw): support frame history events + mCurrentFrameNumber = mFrameNumber; + return NO_ERROR; +} + +void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { + const auto displayId = display->getId(); + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + + const State& s(getDrawingState()); + + // TODO(marissaw): support more than one slot + uint32_t hwcSlot = 0; + + auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), + s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error)); + } + + mFrameNumber++; +} + +void BufferStateLayer::onFirstRef() { + BufferLayer::onFirstRef(); + + if (const auto display = mFlinger->getDefaultDisplayDevice()) { + updateTransformHint(display); + } +} + +} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h new file mode 100644 index 0000000000..0c6eaf5a3b --- /dev/null +++ b/services/surfaceflinger/BufferStateLayer.h @@ -0,0 +1,142 @@ +/* + * 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 "BufferLayer.h" +#include "Layer.h" + +#include <gui/GLConsumer.h> +#include <renderengine/Image.h> +#include <renderengine/RenderEngine.h> +#include <system/window.h> +#include <utils/String8.h> + +namespace android { + +class BufferStateLayer : public BufferLayer { +public: + explicit BufferStateLayer(const LayerCreationArgs&); + ~BufferStateLayer() override; + + // ----------------------------------------------------------------------- + // Interface implementation for Layer + // ----------------------------------------------------------------------- + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + void setTransformHint(uint32_t orientation) const override; + void releasePendingBuffer(nsecs_t dequeueReadyTime) override; + + bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + + bool getTransformToDisplayInverse() const override; + + uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override { + return flags; + } + void pushPendingState() override; + bool applyPendingStates(Layer::State* stateToCommit) override; + + uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; } + uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; } + ui::Transform getActiveTransform(const Layer::State& s) const override { + return s.active.transform; + } + Region getActiveTransparentRegion(const Layer::State& s) const override { + return s.transparentRegionHint; + } + Rect getCrop(const Layer::State& s) const; + + bool setTransform(uint32_t transform) override; + bool setTransformToDisplayInverse(bool transformToDisplayInverse) override; + bool setCrop(const Rect& crop) override; + bool setBuffer(sp<GraphicBuffer> buffer) override; + bool setAcquireFence(const sp<Fence>& fence) override; + bool setDataspace(ui::Dataspace dataspace) override; + bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; + bool setSurfaceDamageRegion(const Region& surfaceDamage) override; + bool setApi(int32_t api) override; + bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override; + + bool setSize(uint32_t w, uint32_t h) override; + bool setPosition(float x, float y, bool immediate) override; + bool setTransparentRegionHint(const Region& transparent) override; + bool setMatrix(const layer_state_t::matrix22_t& matrix, + bool allowNonRectPreservingTransforms) override; + + // Override to ignore legacy layer state properties that are not used by BufferStateLayer + bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; }; + void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/, + uint64_t /*frameNumber*/) override {} + void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/, + uint64_t /*frameNumber*/) override {} + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Interface implementation for BufferLayer + // ----------------------------------------------------------------------- + bool fenceHasSignaled() const override; + +private: + nsecs_t getDesiredPresentTime() override; + std::shared_ptr<FenceTime> getCurrentFenceTime() const override; + + void getDrawingTransformMatrix(float *matrix) override; + uint32_t getDrawingTransform() const override; + ui::Dataspace getDrawingDataSpace() const override; + Rect getDrawingCrop() const override; + uint32_t getDrawingScalingMode() const override; + Region getDrawingSurfaceDamage() const override; + const HdrMetadata& getDrawingHdrMetadata() const override; + int getDrawingApi() const override; + PixelFormat getPixelFormat() const override; + + uint64_t getFrameNumber() const override; + + bool getAutoRefresh() const override; + bool getSidebandStreamChanged() const override; + + std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override; + + bool hasDrawingBuffer() const override; + + void setFilteringEnabled(bool enabled) override; + + status_t bindTextureImage() override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override; + + status_t updateActiveBuffer() override; + status_t updateFrameNumber(nsecs_t latchTime) override; + + void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override; + // ----------------------------------------------------------------------- +private: + void onFirstRef() override; + + static const std::array<float, 16> IDENTITY_MATRIX; + + std::unique_ptr<renderengine::Image> mTextureImage; + + std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX}; + + std::atomic<bool> mSidebandStreamChanged{false}; + + uint32_t mFrameNumber{0}; + + // TODO(marissaw): support sticky transform for LEGACY camera mode +}; + +} // namespace android diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp index ff957c0ee7..263f872826 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -22,54 +22,52 @@ #include <stdlib.h> #include <sys/types.h> +#include <renderengine/RenderEngine.h> +#include <ui/GraphicBuffer.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <ui/GraphicBuffer.h> - #include "ColorLayer.h" #include "DisplayDevice.h" -#include "RenderEngine/RenderEngine.h" #include "SurfaceFlinger.h" namespace android { // --------------------------------------------------------------------------- -ColorLayer::ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags) - : Layer(flinger, client, name, w, h, flags) { - // drawing state & current state are identical - mDrawingState = mCurrentState; -} +ColorLayer::ColorLayer(const LayerCreationArgs& args) : Layer(args) {} + +ColorLayer::~ColorLayer() = default; void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */, - bool useIdentityTransform) const { + bool useIdentityTransform) { half4 color = getColor(); if (color.a > 0) { - Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2); + renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2); computeGeometry(renderArea, mesh, useIdentityTransform); auto& engine(mFlinger->getRenderEngine()); engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */, true /* disableTexture */, color); + engine.setSourceDataSpace(mCurrentDataSpace); engine.drawMesh(mesh); engine.disableBlending(); } } bool ColorLayer::isVisible() const { - const Layer::State& s(getDrawingState()); - return !isHiddenByPolicy() && s.color.a; + return !isHiddenByPolicy() && getAlpha() > 0.0f; } -void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { - const Transform& tr = displayDevice->getTransform(); - const auto& viewport = displayDevice->getViewport(); +void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display) { + const ui::Transform& tr = display->getTransform(); + const auto& viewport = display->getViewport(); Region visible = tr.transform(visibleRegion.intersect(viewport)); - auto hwcId = displayDevice->getHwcDisplayId(); - if (!hasHwcLayer(hwcId)) { + const auto displayId = display->getId(); + if (!hasHwcLayer(displayId)) { + ALOGE("[%s] failed to setPerFrameData: no HWC layer found (%d)", + mName.string(), displayId); return; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; auto& hwcLayer = hwcInfo.layer; auto error = hwcLayer->setVisibleRegion(visible); if (error != HWC2::Error::None) { @@ -77,14 +75,16 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { to_string(error).c_str(), static_cast<int32_t>(error)); visible.dump(LOG_TAG); } + getBE().compositionInfo.hwc.visibleRegion = visible; - setCompositionType(hwcId, HWC2::Composition::SolidColor); + setCompositionType(displayId, HWC2::Composition::SolidColor); error = hwcLayer->setDataspace(mCurrentDataSpace); if (error != HWC2::Error::None) { ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, to_string(error).c_str(), static_cast<int32_t>(error)); } + getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace; half4 color = getColor(); error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)), @@ -94,6 +94,9 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(), static_cast<int32_t>(error)); } + getBE().compositionInfo.hwc.color = { static_cast<uint8_t>(std::round(255.0f * color.r)), + static_cast<uint8_t>(std::round(255.0f * color.g)), + static_cast<uint8_t>(std::round(255.0f * color.b)), 255 }; // Clear out the transform, because it doesn't make sense absent a source buffer error = hwcLayer->setTransform(HWC2::Transform::None); @@ -101,6 +104,22 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(), static_cast<int32_t>(error)); } + getBE().compositionInfo.hwc.transform = HWC2::Transform::None; + + error = hwcLayer->setColorTransform(getColorTransform()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + getBE().compositionInfo.hwc.colorTransform = getColorTransform(); + + error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + surfaceDamageRegion.dump(LOG_TAG); + } + getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index 0cde398ce9..2c1035743e 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -25,16 +25,18 @@ namespace android { class ColorLayer : public Layer { public: - ColorLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags); - virtual ~ColorLayer() = default; + explicit ColorLayer(const LayerCreationArgs&); + ~ColorLayer() override; virtual const char* getTypeId() const { return "ColorLayer"; } virtual void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const; + bool useIdentityTransform); bool isVisible() const override; - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + void setPerFrameData(const sp<const DisplayDevice>& display) override; + +protected: + FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; } }; } // namespace android diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index f259d93c3f..44e843e241 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -22,13 +22,11 @@ namespace android { -ContainerLayer::ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags) - : Layer(flinger, client, name, w, h, flags) { - mDrawingState = mCurrentState; -} +ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {} + +ContainerLayer::~ContainerLayer() = default; -void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) const {} +void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) {} bool ContainerLayer::isVisible() const { return !isHiddenByPolicy(); diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index b352b96762..8eddc7f57b 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -25,16 +25,15 @@ namespace android { class ContainerLayer : public Layer { public: - ContainerLayer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags); - virtual ~ContainerLayer() = default; + explicit ContainerLayer(const LayerCreationArgs&); + ~ContainerLayer() override; const char* getTypeId() const override { return "ContainerLayer"; } void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const override; + bool useIdentityTransform) override; bool isVisible() const override; - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + void setPerFrameData(const sp<const DisplayDevice>& display) override; bool isCreatedFromMainThread() const override { return true; } }; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 309fd0a791..6f645df73f 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -18,6 +18,8 @@ #undef LOG_TAG #define LOG_TAG "DisplayDevice" +#include "DisplayDevice.h" + #include <array> #include <unordered_set> @@ -26,32 +28,24 @@ #include <string.h> #include <math.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> #include <cutils/properties.h> - -#include <utils/RefBase.h> -#include <utils/Log.h> - +#include <gui/Surface.h> +#include <hardware/gralloc.h> +#include <renderengine/RenderEngine.h> #include <ui/DebugUtils.h> #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> - -#include <gui/Surface.h> - -#include <hardware/gralloc.h> +#include <utils/Log.h> +#include <utils/RefBase.h> #include "DisplayHardware/DisplaySurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/HWC2.h" -#include "RenderEngine/RenderEngine.h" - -#include "clz.h" -#include "DisplayDevice.h" #include "SurfaceFlinger.h" #include "Layer.h" -#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> -#include <configstore/Utils.h> - namespace android { // retrieve triple buffer setting from configstore @@ -72,7 +66,8 @@ uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0; namespace { // ordered list of known SDR color modes -const std::array<ColorMode, 2> sSdrColorModes = { +const std::array<ColorMode, 3> sSdrColorModes = { + ColorMode::DISPLAY_BT2020, ColorMode::DISPLAY_P3, ColorMode::SRGB, }; @@ -102,6 +97,8 @@ Dataspace colorModeToDataspace(ColorMode mode) { return Dataspace::SRGB; case ColorMode::DISPLAY_P3: return Dataspace::DISPLAY_P3; + case ColorMode::DISPLAY_BT2020: + return Dataspace::DISPLAY_BT2020; case ColorMode::BT2100_HLG: return Dataspace::BT2020_HLG; case ColorMode::BT2100_PQ: @@ -212,52 +209,46 @@ RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, Ren } // anonymous namespace -// clang-format off -DisplayDevice::DisplayDevice( - const sp<SurfaceFlinger>& flinger, - DisplayType type, - int32_t hwcId, - bool isSecure, - const wp<IBinder>& displayToken, - const sp<ANativeWindow>& nativeWindow, - const sp<DisplaySurface>& displaySurface, - std::unique_ptr<RE::Surface> renderSurface, - int displayWidth, - int displayHeight, - bool hasWideColorGamut, - const HdrCapabilities& hdrCapabilities, - const int32_t supportedPerFrameMetadata, - const std::unordered_map<ColorMode, std::vector<RenderIntent>>& hwcColorModes, - int initialPowerMode) - : lastCompositionHadVisibleLayers(false), - mFlinger(flinger), - mType(type), - mHwcDisplayId(hwcId), - mDisplayToken(displayToken), - mNativeWindow(nativeWindow), - mDisplaySurface(displaySurface), - mSurface{std::move(renderSurface)}, - mDisplayWidth(displayWidth), - mDisplayHeight(displayHeight), - mPageFlipCount(0), - mIsSecure(isSecure), - mLayerStack(NO_LAYER_STACK), - mOrientation(), - mViewport(Rect::INVALID_RECT), - mFrame(Rect::INVALID_RECT), - mPowerMode(initialPowerMode), - mActiveConfig(0), - mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY), - mHasWideColorGamut(hasWideColorGamut), - mHasHdr10(false), - mHasHLG(false), - mHasDolbyVision(false), - mSupportedPerFrameMetadata(supportedPerFrameMetadata) -{ - // clang-format on - populateColorModes(hwcColorModes); - - std::vector<Hdr> types = hdrCapabilities.getSupportedHdrTypes(); +DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, + const wp<IBinder>& displayToken, + DisplayDevice::DisplayType type, int32_t id) + : flinger(flinger), displayToken(displayToken), type(type), id(id) {} + +DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args) + : lastCompositionHadVisibleLayers(false), + mFlinger(args.flinger), + mType(args.type), + mId(args.id), + mDisplayToken(args.displayToken), + mNativeWindow(args.nativeWindow), + mDisplaySurface(args.displaySurface), + mSurface{std::move(args.renderSurface)}, + mDisplayWidth(args.displayWidth), + mDisplayHeight(args.displayHeight), + mDisplayInstallOrientation(args.displayInstallOrientation), + mPageFlipCount(0), + mIsSecure(args.isSecure), + mLayerStack(NO_LAYER_STACK), + mOrientation(), + mViewport(Rect::INVALID_RECT), + mFrame(Rect::INVALID_RECT), + mPowerMode(args.initialPowerMode), + mActiveConfig(0), + mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY), + mHasWideColorGamut(args.hasWideColorGamut), + mHasHdr10(false), + mHasHLG(false), + mHasDolbyVision(false), + mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) { + populateColorModes(args.hwcColorModes); + + ALOGE_IF(!mNativeWindow, "No native window was set for display"); + ALOGE_IF(!mDisplaySurface, "No display surface was set for display"); + ALOGE_IF(!mSurface, "No render surface was set for display"); + ALOGE_IF(mDisplayWidth <= 0 || mDisplayHeight <= 0, + "Invalid dimensions of %d x %d were set for display", mDisplayWidth, mDisplayHeight); + + std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes(); for (Hdr hdrType : types) { switch (hdrType) { case Hdr::HDR10: @@ -274,9 +265,9 @@ DisplayDevice::DisplayDevice( } } - float minLuminance = hdrCapabilities.getDesiredMinLuminance(); - float maxLuminance = hdrCapabilities.getDesiredMaxLuminance(); - float maxAverageLuminance = hdrCapabilities.getDesiredMaxAverageLuminance(); + float minLuminance = args.hdrCapabilities.getDesiredMinLuminance(); + float maxLuminance = args.hdrCapabilities.getDesiredMaxLuminance(); + float maxAverageLuminance = args.hdrCapabilities.getDesiredMaxAverageLuminance(); minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance; maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance; @@ -301,9 +292,9 @@ DisplayDevice::DisplayDevice( DisplayDevice::~DisplayDevice() = default; void DisplayDevice::disconnect(HWComposer& hwc) { - if (mHwcDisplayId >= 0) { - hwc.disconnectDisplay(mHwcDisplayId); - mHwcDisplayId = -1; + if (mId >= 0) { + hwc.disconnectDisplay(mId); + mId = -1; } } @@ -319,8 +310,8 @@ int DisplayDevice::getHeight() const { return mDisplayHeight; } -void DisplayDevice::setDisplayName(const String8& displayName) { - if (!displayName.isEmpty()) { +void DisplayDevice::setDisplayName(const std::string& displayName) { + if (!displayName.empty()) { // never override the name with an empty name mDisplayName = displayName; } @@ -340,15 +331,16 @@ status_t DisplayDevice::beginFrame(bool mustRecompose) const { return mDisplaySurface->beginFrame(mustRecompose); } -status_t DisplayDevice::prepareFrame(HWComposer& hwc) { - status_t error = hwc.prepare(*this); +status_t DisplayDevice::prepareFrame(HWComposer& hwc, + std::vector<CompositionInfo>& compositionData) { + status_t error = hwc.prepare(*this, compositionData); if (error != NO_ERROR) { return error; } DisplaySurface::CompositionType compositionType; - bool hasClient = hwc.hasClientComposition(mHwcDisplayId); - bool hasDevice = hwc.hasDeviceComposition(mHwcDisplayId); + bool hasClient = hwc.hasClientComposition(mId); + bool hasDevice = hwc.hasDeviceComposition(mId); if (hasClient && hasDevice) { compositionType = DisplaySurface::COMPOSITION_MIXED; } else if (hasClient) { @@ -365,14 +357,13 @@ status_t DisplayDevice::prepareFrame(HWComposer& hwc) { } void DisplayDevice::swapBuffers(HWComposer& hwc) const { - if (hwc.hasClientComposition(mHwcDisplayId) || hwc.hasFlipClientTargetRequest(mHwcDisplayId)) { + if (hwc.hasClientComposition(mId) || hwc.hasFlipClientTargetRequest(mId)) { mSurface->swapBuffers(); } status_t result = mDisplaySurface->advanceFrame(); if (result != NO_ERROR) { - ALOGE("[%s] failed pushing new frame to HWC: %d", - mDisplayName.string(), result); + ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplayName.c_str(), result); } } @@ -390,8 +381,7 @@ void DisplayDevice::setViewportAndProjection() const { size_t w = mDisplayWidth; size_t h = mDisplayHeight; Rect sourceCrop(0, 0, w, h); - mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, h, - false, Transform::ROT_0); + mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, ui::Transform::ROT_0); } const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const { @@ -421,7 +411,7 @@ Region DisplayDevice::getDirtyRegion(bool repaintEverything) const { if (repaintEverything) { dirty.set(getBounds()); } else { - const Transform& planeTransform(mGlobalTransform); + const ui::Transform& planeTransform(mGlobalTransform); dirty = planeTransform.transform(this->dirtyRegion); dirty.andSelf(getBounds()); } @@ -437,8 +427,8 @@ int DisplayDevice::getPowerMode() const { return mPowerMode; } -bool DisplayDevice::isDisplayOn() const { - return (mPowerMode != HWC_POWER_MODE_OFF); +bool DisplayDevice::isPoweredOn() const { + return mPowerMode != HWC_POWER_MODE_OFF; } // ---------------------------------------------------------------------------- @@ -500,37 +490,37 @@ uint32_t DisplayDevice::getOrientationTransform() const { uint32_t transform = 0; switch (mOrientation) { case DisplayState::eOrientationDefault: - transform = Transform::ROT_0; + transform = ui::Transform::ROT_0; break; case DisplayState::eOrientation90: - transform = Transform::ROT_90; + transform = ui::Transform::ROT_90; break; case DisplayState::eOrientation180: - transform = Transform::ROT_180; + transform = ui::Transform::ROT_180; break; case DisplayState::eOrientation270: - transform = Transform::ROT_270; + transform = ui::Transform::ROT_270; break; } return transform; } status_t DisplayDevice::orientationToTransfrom( - int orientation, int w, int h, Transform* tr) + int orientation, int w, int h, ui::Transform* tr) { uint32_t flags = 0; switch (orientation) { case DisplayState::eOrientationDefault: - flags = Transform::ROT_0; + flags = ui::Transform::ROT_0; break; case DisplayState::eOrientation90: - flags = Transform::ROT_90; + flags = ui::Transform::ROT_90; break; case DisplayState::eOrientation180: - flags = Transform::ROT_180; + flags = ui::Transform::ROT_180; break; case DisplayState::eOrientation270: - flags = Transform::ROT_270; + flags = ui::Transform::ROT_270; break; default: return BAD_VALUE; @@ -548,8 +538,8 @@ void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) { ANativeWindow* const window = mNativeWindow.get(); mSurface->setNativeWindow(window); - mDisplayWidth = mSurface->queryWidth(); - mDisplayHeight = mSurface->queryHeight(); + mDisplayWidth = mSurface->getWidth(); + mDisplayHeight = mSurface->getHeight(); LOG_FATAL_IF(mDisplayWidth != newWidth, "Unable to set new width to %d", newWidth); @@ -565,7 +555,7 @@ void DisplayDevice::setProjection(int orientation, const int w = mDisplayWidth; const int h = mDisplayHeight; - Transform R; + ui::Transform R; DisplayDevice::orientationToTransfrom(orientation, w, h, &R); if (!frame.isValid()) { @@ -580,16 +570,16 @@ void DisplayDevice::setProjection(int orientation, // it's also invalid to have an empty viewport, so we handle that // case in the same way. viewport = Rect(w, h); - if (R.getOrientation() & Transform::ROT_90) { + if (R.getOrientation() & ui::Transform::ROT_90) { // viewport is always specified in the logical orientation // of the display (ie: post-rotation). - swap(viewport.right, viewport.bottom); + std::swap(viewport.right, viewport.bottom); } } dirtyRegion.set(getBounds()); - Transform TL, TP, S; + ui::Transform TL, TP, S; float src_width = viewport.width(); float src_height = viewport.height(); float dst_width = frame.width(); @@ -610,9 +600,8 @@ void DisplayDevice::setProjection(int orientation, // need to take care of primary display rotation for mGlobalTransform // for case if the panel is not installed aligned with device orientation if (mType == DisplayType::DISPLAY_PRIMARY) { - int primaryDisplayOrientation = mFlinger->getPrimaryDisplayOrientation(); DisplayDevice::orientationToTransfrom( - (orientation + primaryDisplayOrientation) % (DisplayState::eOrientation270 + 1), + (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1), w, h, &R); } @@ -623,7 +612,7 @@ void DisplayDevice::setProjection(int orientation, const uint8_t type = mGlobalTransform.getType(); mNeedsFiltering = (!mGlobalTransform.preserveRects() || - (type >= Transform::SCALE)); + (type >= ui::Transform::SCALE)); mScissor = mGlobalTransform.transform(viewport); if (mScissor.isEmpty()) { @@ -631,20 +620,20 @@ void DisplayDevice::setProjection(int orientation, } mOrientation = orientation; - if (mType == DisplayType::DISPLAY_PRIMARY) { + if (isPrimary()) { uint32_t transform = 0; switch (mOrientation) { case DisplayState::eOrientationDefault: - transform = Transform::ROT_0; + transform = ui::Transform::ROT_0; break; case DisplayState::eOrientation90: - transform = Transform::ROT_90; + transform = ui::Transform::ROT_90; break; case DisplayState::eOrientation180: - transform = Transform::ROT_180; + transform = ui::Transform::ROT_180; break; case DisplayState::eOrientation270: - transform = Transform::ROT_270; + transform = ui::Transform::ROT_270; break; } sPrimaryDisplayOrientation = transform; @@ -658,13 +647,13 @@ uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() { } void DisplayDevice::dump(String8& result) const { - const Transform& tr(mGlobalTransform); + const ui::Transform& tr(mGlobalTransform); ANativeWindow* const window = mNativeWindow.get(); - result.appendFormat("+ DisplayDevice: %s\n", mDisplayName.string()); - result.appendFormat(" type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p " + result.appendFormat("+ DisplayDevice: %s\n", mDisplayName.c_str()); + result.appendFormat(" type=%x, ID=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p " "(%d:%d:%d:%d), orient=%2d (type=%08x), " "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n", - mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, window, + mType, mId, mLayerStack, mDisplayWidth, mDisplayHeight, window, mSurface->queryRedSize(), mSurface->queryGreenSize(), mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation, tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig, @@ -704,7 +693,7 @@ void DisplayDevice::addColorMode( const Dataspace dataspace = colorModeToDataspace(mode); const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode); - ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mHwcDisplayId, + ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mId, dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(), decodeRenderIntent(intent).c_str(), dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(), @@ -794,18 +783,6 @@ void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent, } } -std::atomic<int32_t> DisplayDeviceState::nextDisplayId(1); - -DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure) - : type(type), - layerStack(DisplayDevice::NO_LAYER_STACK), - orientation(0), - width(0), - height(0), - isSecure(isSecure) -{ - viewport.makeInvalid(); - frame.makeInvalid(); -} +std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 6c3bd91793..918f7dec89 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -17,40 +17,42 @@ #ifndef ANDROID_DISPLAY_DEVICE_H #define ANDROID_DISPLAY_DEVICE_H -#include "Transform.h" - #include <stdlib.h> -#include <unordered_map> -#include <math/mat4.h> +#include <memory> +#include <string> +#include <unordered_map> #include <binder/IBinder.h> -#include <gui/ISurfaceComposer.h> +#include <gui/LayerState.h> #include <hardware/hwcomposer_defs.h> +#include <math/mat4.h> +#include <renderengine/Surface.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> +#include <ui/Transform.h> #include <utils/RefBase.h> #include <utils/Mutex.h> #include <utils/String8.h> #include <utils/Timers.h> #include "RenderArea.h" -#include "RenderEngine/Surface.h" - -#include <memory> struct ANativeWindow; namespace android { -struct DisplayInfo; class DisplaySurface; class Fence; +class HWComposer; class IGraphicBufferProducer; class Layer; class SurfaceFlinger; -class HWComposer; + +struct CompositionInfo; +struct DisplayDeviceCreationArgs; +struct DisplayInfo; class DisplayDevice : public LightRefBase<DisplayDevice> { @@ -76,24 +78,7 @@ public: NO_LAYER_STACK = 0xFFFFFFFF, }; - // clang-format off - DisplayDevice( - const sp<SurfaceFlinger>& flinger, - DisplayType type, - int32_t hwcId, - bool isSecure, - const wp<IBinder>& displayToken, - const sp<ANativeWindow>& nativeWindow, - const sp<DisplaySurface>& displaySurface, - std::unique_ptr<RE::Surface> renderSurface, - int displayWidth, - int displayHeight, - bool hasWideColorGamut, - const HdrCapabilities& hdrCapabilities, - const int32_t supportedPerFrameMetadata, - const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>>& hwcColorModes, - int initialPowerMode); - // clang-format on + explicit DisplayDevice(DisplayDeviceCreationArgs&& args); ~DisplayDevice(); @@ -111,6 +96,7 @@ public: int getWidth() const; int getHeight() const; + int getInstallOrientation() const { return mDisplayInstallOrientation; } void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers); const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const; @@ -125,7 +111,7 @@ public: int getOrientation() const { return mOrientation; } uint32_t getOrientationTransform() const; static uint32_t getPrimaryDisplayOrientationTransform(); - const Transform& getTransform() const { return mGlobalTransform; } + const ui::Transform& getTransform() const { return mGlobalTransform; } const Rect getViewport() const { return mViewport; } const Rect getFrame() const { return mFrame; } const Rect& getScissor() const { return mScissor; } @@ -134,7 +120,8 @@ public: uint32_t getLayerStack() const { return mLayerStack; } int32_t getDisplayType() const { return mType; } bool isPrimary() const { return mType == DISPLAY_PRIMARY; } - int32_t getHwcDisplayId() const { return mHwcDisplayId; } + bool isVirtual() const { return mType == DISPLAY_VIRTUAL; } + int32_t getId() const { return mId; } const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; } @@ -142,7 +129,7 @@ public: // We pass in mustRecompose so we can keep VirtualDisplaySurface's state // machine happy without actually queueing a buffer if nothing has changed status_t beginFrame(bool mustRecompose) const; - status_t prepareFrame(HWComposer& hwc); + status_t prepareFrame(HWComposer& hwc, std::vector<CompositionInfo>& compositionInfo); bool hasWideColorGamut() const { return mHasWideColorGamut; } // Whether h/w composer has native support for specific HDR type. @@ -179,8 +166,8 @@ public: } inline Rect bounds() const { return getBounds(); } - void setDisplayName(const String8& displayName); - const String8& getDisplayName() const { return mDisplayName; } + void setDisplayName(const std::string& displayName); + const std::string& getDisplayName() const { return mDisplayName; } bool makeCurrent() const; void setViewportAndProjection() const; @@ -192,7 +179,7 @@ public: */ int getPowerMode() const; void setPowerMode(int mode); - bool isDisplayOn() const; + bool isPoweredOn() const; ui::ColorMode getActiveColorMode() const; void setActiveColorMode(ui::ColorMode mode); @@ -224,18 +211,19 @@ private: */ sp<SurfaceFlinger> mFlinger; DisplayType mType; - int32_t mHwcDisplayId; + int32_t mId; wp<IBinder> mDisplayToken; // ANativeWindow this display is rendering into sp<ANativeWindow> mNativeWindow; sp<DisplaySurface> mDisplaySurface; - std::unique_ptr<RE::Surface> mSurface; + std::unique_ptr<renderengine::Surface> mSurface; int mDisplayWidth; int mDisplayHeight; + const int mDisplayInstallOrientation; mutable uint32_t mPageFlipCount; - String8 mDisplayName; + std::string mDisplayName; bool mIsSecure; /* @@ -252,7 +240,7 @@ private: * Transaction state */ static status_t orientationToTransfrom(int orientation, - int w, int h, Transform* tr); + int w, int h, ui::Transform* tr); // The identifier of the active layer stack for this display. Several displays // can use the same layer stack: A z-ordered group of layers (sometimes called @@ -267,7 +255,7 @@ private: Rect mFrame; // pre-computed scissor to apply to the display Rect mScissor; - Transform mGlobalTransform; + ui::Transform mGlobalTransform; bool mNeedsFiltering; // Current power mode int mPowerMode; @@ -313,15 +301,9 @@ private: }; struct DisplayDeviceState { - DisplayDeviceState() = default; - DisplayDeviceState(DisplayDevice::DisplayType type, bool isSecure); - - bool isValid() const { return type >= 0; } - bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; } - bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; } + bool isVirtual() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; } - static std::atomic<int32_t> nextDisplayId; - int32_t displayId = nextDisplayId++; + int32_t sequenceId = sNextSequenceId++; DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_ID_INVALID; sp<IGraphicBufferProducer> surface; uint32_t layerStack = DisplayDevice::NO_LAYER_STACK; @@ -330,30 +312,144 @@ struct DisplayDeviceState { uint8_t orientation = 0; uint32_t width = 0; uint32_t height = 0; - String8 displayName; + std::string displayName; bool isSecure = false; + +private: + static std::atomic<int32_t> sNextSequenceId; +}; + +struct DisplayDeviceCreationArgs { + // We use a constructor to ensure some of the values are set, without + // assuming a default value. + DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken, + DisplayDevice::DisplayType type, int32_t id); + + const sp<SurfaceFlinger> flinger; + const wp<IBinder> displayToken; + const DisplayDevice::DisplayType type; + const int32_t id; + + bool isSecure{false}; + sp<ANativeWindow> nativeWindow; + sp<DisplaySurface> displaySurface; + std::unique_ptr<renderengine::Surface> renderSurface; + int displayWidth{0}; + int displayHeight{0}; + int displayInstallOrientation{DisplayState::eOrientationDefault}; + bool hasWideColorGamut{false}; + HdrCapabilities hdrCapabilities; + int32_t supportedPerFrameMetadata{0}; + std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; + int initialPowerMode{HWC_POWER_MODE_NORMAL}; }; class DisplayRenderArea : public RenderArea { public: DisplayRenderArea(const sp<const DisplayDevice> device, - ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone) - : DisplayRenderArea(device, device->getBounds(), device->getHeight(), device->getWidth(), - rotation) {} - DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqHeight, - uint32_t reqWidth, ISurfaceComposer::Rotation rotation) - : RenderArea(reqHeight, reqWidth, CaptureFill::OPAQUE, rotation), mDevice(device), - mSourceCrop(sourceCrop) {} - - const Transform& getTransform() const override { return mDevice->getTransform(); } + ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) + : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(), + device->getCompositionDataSpace(), rotation) {} + DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth, + uint32_t reqHeight, ui::Dataspace reqDataSpace, + ui::Transform::orientation_flags rotation) + : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, + getDisplayRotation(rotation, device->getInstallOrientation())), + mDevice(device), + mSourceCrop(sourceCrop) {} + + const ui::Transform& getTransform() const override { return mDevice->getTransform(); } Rect getBounds() const override { return mDevice->getBounds(); } int getHeight() const override { return mDevice->getHeight(); } int getWidth() const override { return mDevice->getWidth(); } bool isSecure() const override { return mDevice->isSecure(); } - bool needsFiltering() const override { return mDevice->needsFiltering(); } - Rect getSourceCrop() const override { return mSourceCrop; } + + bool needsFiltering() const override { + // check if the projection from the logical display to the physical + // display needs filtering + if (mDevice->needsFiltering()) { + return true; + } + + // check if the projection from the logical render area (i.e., the + // physical display) to the physical render area requires filtering + const Rect sourceCrop = getSourceCrop(); + int width = sourceCrop.width(); + int height = sourceCrop.height(); + if (getRotationFlags() & ui::Transform::ROT_90) { + std::swap(width, height); + } + return width != getReqWidth() || height != getReqHeight(); + } + + Rect getSourceCrop() const override { + // use the (projected) logical display viewport by default + if (mSourceCrop.isEmpty()) { + return mDevice->getScissor(); + } + + const int orientation = mDevice->getInstallOrientation(); + if (orientation == DisplayState::eOrientationDefault) { + return mSourceCrop; + } + + // Install orientation is transparent to the callers. Apply it now. + uint32_t flags = 0x00; + switch (orientation) { + case DisplayState::eOrientation90: + flags = ui::Transform::ROT_90; + break; + case DisplayState::eOrientation180: + flags = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation270: + flags = ui::Transform::ROT_270; + break; + } + ui::Transform tr; + tr.set(flags, getWidth(), getHeight()); + return tr.transform(mSourceCrop); + } private: + // Install orientation is transparent to the callers. We need to cancel + // it out by modifying rotation flags. + static ui::Transform::orientation_flags getDisplayRotation( + ui::Transform::orientation_flags rotation, int orientation) { + if (orientation == DisplayState::eOrientationDefault) { + return rotation; + } + + // convert hw orientation into flag presentation + // here inverse transform needed + uint8_t hw_rot_90 = 0x00; + uint8_t hw_flip_hv = 0x00; + switch (orientation) { + case DisplayState::eOrientation90: + hw_rot_90 = ui::Transform::ROT_90; + hw_flip_hv = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation180: + hw_flip_hv = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation270: + hw_rot_90 = ui::Transform::ROT_90; + break; + } + + // transform flags operation + // 1) flip H V if both have ROT_90 flag + // 2) XOR these flags + uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90; + uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180; + if (rotation_rot_90 & hw_rot_90) { + rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180; + } + + return static_cast<ui::Transform::orientation_flags>( + (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv)); + } + const sp<const DisplayDevice> mDevice; const Rect mSourceCrop; }; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 37ba4339c3..f0bccaabf1 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -22,7 +22,6 @@ #include "ComposerHal.h" -#include <android/hardware/graphics/composer/2.2/IComposer.h> #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> #include <gui/BufferQueue.h> #include <hidl/HidlTransportUtils.h> @@ -172,22 +171,31 @@ Composer::Composer(const std::string& serviceName) LOG_ALWAYS_FATAL("failed to get hwcomposer service"); } - mComposer->createClient( - [&](const auto& tmpError, const auto& tmpClient) - { - if (tmpError == Error::NONE) { - mClient = tmpClient; - } - }); - if (mClient == nullptr) { - LOG_ALWAYS_FATAL("failed to create composer client"); + if (sp<IComposer> composer_2_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"); + } + }); } - // 2.2 support is optional - sp<IComposer> composer_2_2 = IComposer::castFrom(mComposer); - if (composer_2_2 != nullptr) { - mClient_2_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"); } if (mIsUsingVrComposer) { @@ -346,16 +354,26 @@ Error Composer::getColorModes(Display display, { Error error = kDefaultError; - if (mClient_2_2) { - mClient_2_2->getColorModes_2_2(display, - [&](const auto& tmpError, const auto& tmpModes) { - error = tmpError; - if (error != Error::NONE) { - return; - } + 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; - }); + *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) { @@ -547,8 +565,11 @@ Error Composer::setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) { hardware::Return<Error> ret(kDefaultError); - if (mClient_2_2) { - ret = mClient_2_2->setColorMode_2_2(display, mode, renderIntent); + 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)); @@ -897,23 +918,25 @@ Error Composer::setLayerPerFrameMetadata(Display display, Layer layer, return Error::NONE; } -Error Composer::getPerFrameMetadataKeys( - Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) { +std::vector<IComposerClient::PerFrameMetadataKey> Composer::getPerFrameMetadataKeys( + Display display) { + std::vector<IComposerClient::PerFrameMetadataKey> keys; if (!mClient_2_2) { - return Error::UNSUPPORTED; + return keys; } Error error = kDefaultError; mClient_2_2->getPerFrameMetadataKeys(display, [&](const auto& tmpError, const auto& tmpKeys) { error = tmpError; if (error != Error::NONE) { + ALOGW("getPerFrameMetadataKeys failed with %d", tmpError); return; } - *outKeys = tmpKeys; + keys = tmpKeys; }); - return error; + return keys; } Error Composer::getRenderIntents(Display display, ColorMode colorMode, @@ -924,15 +947,22 @@ Error Composer::getRenderIntents(Display display, ColorMode colorMode, } Error error = kDefaultError; - mClient_2_2->getRenderIntents(display, colorMode, - [&](const auto& tmpError, const auto& tmpKeys) { + + 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; } @@ -945,18 +975,54 @@ Error Composer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatri } Error error = kDefaultError; - mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) { - error = tmpError; - if (error != Error::NONE) { - return; - } + 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()); + }); - *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; +} + CommandReader::~CommandReader() { resetData(); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index beee53966f..4188352a76 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -25,9 +25,9 @@ #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> #include <android/hardware/graphics/common/1.1/types.h> -#include <android/hardware/graphics/composer/2.2/IComposer.h> -#include <android/hardware/graphics/composer/2.2/IComposerClient.h> -#include <composer-command-buffer/2.2/ComposerCommandBuffer.h> +#include <android/hardware/graphics/composer/2.3/IComposer.h> +#include <android/hardware/graphics/composer/2.3/IComposerClient.h> +#include <composer-command-buffer/2.3/ComposerCommandBuffer.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/GraphicBuffer.h> @@ -43,15 +43,16 @@ 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; using types::V1_0::ColorTransform; using types::V1_0::Hdr; using types::V1_0::Transform; -using types::V1_1::ColorMode; -using types::V1_1::Dataspace; using types::V1_1::PixelFormat; using types::V1_1::RenderIntent; +using types::V1_2::ColorMode; +using types::V1_2::Dataspace; using V2_1::Config; using V2_1::Display; @@ -59,10 +60,11 @@ using V2_1::Error; using V2_1::IComposerCallback; using V2_1::Layer; -using V2_2::CommandReaderBase; -using V2_2::CommandWriterBase; -using V2_2::IComposer; -using V2_2::IComposerClient; +using V2_3::CommandReaderBase; +using V2_3::CommandWriterBase; + +using V2_3::IComposer; +using V2_3::IComposerClient; using PerFrameMetadata = IComposerClient::PerFrameMetadata; using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey; @@ -180,11 +182,17 @@ public: virtual Error setLayerPerFrameMetadata( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) = 0; - virtual Error getPerFrameMetadataKeys( - Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) = 0; + virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys( + Display display) = 0; virtual Error getRenderIntents(Display display, ColorMode colorMode, std::vector<RenderIntent>* outRenderIntents) = 0; virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0; + + // Composer HAL 2.3 + virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort, + std::vector<uint8_t>* outData) = 0; + virtual Error setLayerColorTransform(Display display, Layer layer, + const float* matrix) = 0; }; namespace impl { @@ -374,12 +382,17 @@ public: Error setLayerPerFrameMetadata( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) override; - Error getPerFrameMetadataKeys( - Display display, std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) 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; + private: class CommandWriter : public CommandWriterBase { public: @@ -405,7 +418,8 @@ private: sp<V2_1::IComposer> mComposer; sp<V2_1::IComposerClient> mClient; - sp<IComposerClient> mClient_2_2; + sp<V2_2::IComposerClient> mClient_2_2; + sp<IComposerClient> mClient_2_3; // 64KiB minus a small space for metadata such as read/write pointers static constexpr size_t kWriterInitialSize = diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp new file mode 100644 index 0000000000..dcc413863e --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#undef LOG_TAG +#define LOG_TAG "DisplayIdentification" + +#include <algorithm> +#include <cctype> +#include <numeric> +#include <optional> + +#include <log/log.h> + +#include "DisplayIdentification.h" + +namespace android { +namespace { + +using byte_view = std::basic_string_view<uint8_t>; + +constexpr size_t kEdidHeaderLength = 5; + +std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { + if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) { + return {}; + } + + return view[3]; +} + +std::string_view parseEdidText(const byte_view& view) { + std::string_view text(reinterpret_cast<const char*>(view.data()), view.size()); + text = text.substr(0, text.find('\n')); + + if (!std::all_of(text.begin(), text.end(), ::isprint)) { + ALOGW("Invalid EDID: ASCII text is not printable."); + return {}; + } + + return text; +} + +// Big-endian 16-bit value encodes three 5-bit letters where A is 0b00001. +template <size_t I> +char getPnpLetter(uint16_t id) { + static_assert(I < 3); + const char letter = 'A' + (static_cast<uint8_t>(id >> ((2 - I) * 5)) & 0b00011111) - 1; + return letter < 'A' || letter > 'Z' ? '\0' : letter; +} + +DisplayId getEdidDisplayId(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) { + return (static_cast<DisplayId>(manufacturerId) << 40) | + (static_cast<DisplayId>(displayNameHash) << 8) | port; +} + +} // namespace + +bool isEdid(const DisplayIdentificationData& data) { + const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; + return data.size() >= sizeof(kMagic) && + std::equal(std::begin(kMagic), std::end(kMagic), data.begin()); +} + +std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { + constexpr size_t kMinLength = 128; + if (edid.size() < kMinLength) { + ALOGW("Invalid EDID: structure is truncated."); + // Attempt parsing even if EDID is malformed. + } else { + ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported."); + ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)), + "Invalid EDID: structure does not checksum."); + } + + constexpr size_t kManufacturerOffset = 8; + if (edid.size() < kManufacturerOffset + sizeof(uint16_t)) { + ALOGE("Invalid EDID: manufacturer ID is truncated."); + return {}; + } + + // Plug and play ID encoded as big-endian 16-bit value. + const uint16_t manufacturerId = + (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]; + + const auto pnpId = getPnpId(manufacturerId); + if (!pnpId) { + ALOGE("Invalid EDID: manufacturer ID is not a valid PnP ID."); + return {}; + } + + constexpr size_t kDescriptorOffset = 54; + if (edid.size() < kDescriptorOffset) { + ALOGE("Invalid EDID: descriptors are missing."); + return {}; + } + + byte_view view(edid.data(), edid.size()); + view.remove_prefix(kDescriptorOffset); + + std::string_view displayName; + std::string_view serialNumber; + std::string_view asciiText; + + constexpr size_t kDescriptorCount = 4; + constexpr size_t kDescriptorLength = 18; + + for (size_t i = 0; i < kDescriptorCount; i++) { + if (view.size() < kDescriptorLength) { + break; + } + + if (const auto type = getEdidDescriptorType(view)) { + byte_view descriptor(view.data(), kDescriptorLength); + descriptor.remove_prefix(kEdidHeaderLength); + + switch (*type) { + case 0xfc: + displayName = parseEdidText(descriptor); + break; + case 0xfe: + asciiText = parseEdidText(descriptor); + break; + case 0xff: + serialNumber = parseEdidText(descriptor); + break; + } + } + + view.remove_prefix(kDescriptorLength); + } + + if (displayName.empty()) { + ALOGW("Invalid EDID: falling back to serial number due to missing display name."); + displayName = serialNumber; + } + if (displayName.empty()) { + ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number."); + displayName = asciiText; + } + if (displayName.empty()) { + ALOGE("Invalid EDID: display name and fallback descriptors are missing."); + return {}; + } + + return Edid{manufacturerId, *pnpId, displayName}; +} + +std::optional<PnpId> getPnpId(uint16_t manufacturerId) { + const char a = getPnpLetter<0>(manufacturerId); + const char b = getPnpLetter<1>(manufacturerId); + const char c = getPnpLetter<2>(manufacturerId); + return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt; +} + +std::optional<DisplayId> generateDisplayId(uint8_t port, const DisplayIdentificationData& data) { + if (!isEdid(data)) { + ALOGE("Display identification data has unknown format."); + return {}; + } + + const auto edid = parseEdid(data); + if (!edid) { + return {}; + } + + // Hash display name instead of using product code or serial number, since the latter have been + // observed to change on some displays with multiple inputs. + const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName)); + return getEdidDisplayId(port, edid->manufacturerId, hash); +} + +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h new file mode 100644 index 0000000000..379f2d3be3 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h @@ -0,0 +1,45 @@ +/* + * 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 <array> +#include <cstdint> +#include <optional> +#include <string_view> +#include <vector> + +namespace android { + +using DisplayId = uint64_t; +using DisplayIdentificationData = std::vector<uint8_t>; + +// NUL-terminated plug and play ID. +using PnpId = std::array<char, 4>; + +struct Edid { + uint16_t manufacturerId; + PnpId pnpId; + std::string_view displayName; +}; + +bool isEdid(const DisplayIdentificationData&); +std::optional<Edid> parseEdid(const DisplayIdentificationData&); +std::optional<PnpId> getPnpId(uint16_t manufacturerId); + +std::optional<DisplayId> generateDisplayId(uint8_t port, const DisplayIdentificationData&); + +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 1a60c83b01..393041d681 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -124,6 +124,12 @@ uint32_t Device::getMaxVirtualDisplayCount() const return mComposer->getMaxVirtualDisplayCount(); } +Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort, + std::vector<uint8_t>* outData) const { + auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData); + return static_cast<Error>(intError); +} + Error Device::createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, Display** outDisplay) { @@ -403,21 +409,17 @@ Error Display::getColorModes(std::vector<ColorMode>* outModes) const return static_cast<Error>(intError); } -Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadata) const +int32_t Display::getSupportedPerFrameMetadata() const { - *outSupportedPerFrameMetadata = 0; - std::vector<Hwc2::PerFrameMetadataKey> tmpKeys; - auto intError = mComposer.getPerFrameMetadataKeys(mId, &tmpKeys); - auto error = static_cast<Error>(intError); - if (error != Error::None) { - return error; - } + int32_t supportedPerFrameMetadata = 0; + + std::vector<Hwc2::PerFrameMetadataKey> tmpKeys = mComposer.getPerFrameMetadataKeys(mId); + std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end()); // Check whether a specific metadata type is supported. A metadata type is considered // supported if and only if all required fields are supported. // SMPTE2086 - std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end()); if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X) && @@ -428,15 +430,15 @@ Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadat hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::WHITE_POINT_Y) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_LUMINANCE) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MIN_LUMINANCE)) { - *outSupportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086; + supportedPerFrameMetadata |= HdrMetadata::Type::SMPTE2086; } // CTA861_3 if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL)) { - *outSupportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3; + supportedPerFrameMetadata |= HdrMetadata::Type::CTA861_3; } - return Error::None; + return supportedPerFrameMetadata; } Error Display::getRenderIntents(ColorMode colorMode, @@ -765,7 +767,8 @@ Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capabil : mComposer(composer), mCapabilities(capabilities), mDisplayId(displayId), - mId(layerId) + mId(layerId), + mColorMatrix(android::mat4()) { ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId); } @@ -972,4 +975,14 @@ Error Layer::setInfo(uint32_t type, uint32_t appId) return static_cast<Error>(intError); } +// Composer HAL 2.3 +Error Layer::setColorTransform(const android::mat4& matrix) { + if (matrix == mColorMatrix) { + return Error::None; + } + mColorMatrix = matrix; + auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray()); + return static_cast<Error>(intError); +} + } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index e423167a28..d274631f9d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -95,6 +95,9 @@ public: }; uint32_t getMaxVirtualDisplayCount() const; + Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort, + std::vector<uint8_t>* outData) const; + Error createVirtualDisplay(uint32_t width, uint32_t height, android::ui::PixelFormat* format, Display** outDisplay); void destroyDisplay(hwc2_display_t displayId); @@ -215,10 +218,8 @@ public: std::unordered_map<Layer*, Composition>* outTypes); [[clang::warn_unused_result]] Error getColorModes( std::vector<android::ui::ColorMode>* outModes) const; - // outSupportedPerFrameMetadata is an opaque bitmask to the callers - // but contains HdrMetadata::Type::*. - [[clang::warn_unused_result]] Error getSupportedPerFrameMetadata( - int32_t* outSupportedPerFrameMetadata) const; + // Returns a bitmask which contains HdrMetadata::Type::*. + [[clang::warn_unused_result]] int32_t getSupportedPerFrameMetadata() const; [[clang::warn_unused_result]] Error getRenderIntents( android::ui::ColorMode colorMode, std::vector<android::ui::RenderIntent>* outRenderIntents) const; @@ -340,6 +341,9 @@ public: [[clang::warn_unused_result]] Error setZOrder(uint32_t z); [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId); + // Composer HAL 2.3 + [[clang::warn_unused_result]] Error setColorTransform(const android::mat4& matrix); + 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 @@ -352,6 +356,7 @@ private: android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN; android::HdrMetadata mHdrMetadata; std::function<void(Layer*)> mLayerDestroyedListener; + android::mat4 mColorMatrix; }; } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index f5f7a821f0..d827fd23ea 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -54,6 +54,9 @@ #include "../Layer.h" // needed only for debugging #include "../SurfaceFlinger.h" +#define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \ + ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg) + #define LOG_DISPLAY_ERROR(displayId, msg) \ ALOGE("%s failed for display %d: %s", __FUNCTION__, displayId, msg) @@ -96,6 +99,18 @@ void HWComposer::registerCallback(HWC2::ComposerCallback* callback, mHwcDevice->registerCallback(callback, sequenceId); } +bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort, + DisplayIdentificationData* outData) const { + const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData); + if (error != HWC2::Error::None) { + if (error != HWC2::Error::Unsupported) { + LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str()); + } + return false; + } + return true; +} + bool HWComposer::hasCapability(HWC2::Capability capability) const { return mHwcDevice->getCapabilities().count(capability) > 0; @@ -131,53 +146,55 @@ void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { } } -void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType, - HWC2::Connection connection) { +std::optional<DisplayId> HWComposer::onHotplug(hwc2_display_t hwcDisplayId, int32_t displayType, + HWC2::Connection connection) { if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) { ALOGE("Invalid display type of %d", displayType); - return; + return {}; + } + + ALOGV("hotplug: %" PRIu64 ", %s %s", hwcDisplayId, + displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external", + to_string(connection).c_str()); + mHwcDevice->onHotplug(hwcDisplayId, connection); + + std::optional<DisplayId> displayId; + + if (connection == HWC2::Connection::Connected) { + uint8_t port; + DisplayIdentificationData data; + if (getDisplayIdentificationData(hwcDisplayId, &port, &data)) { + displayId = generateDisplayId(port, data); + ALOGE_IF(!displayId, "Failed to generate stable ID for display %" PRIu64, hwcDisplayId); + } } - ALOGV("hotplug: %" PRIu64 ", %s %s", displayId, - displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external", - to_string(connection).c_str()); - mHwcDevice->onHotplug(displayId, connection); // Disconnect is handled through HWComposer::disconnectDisplay via // SurfaceFlinger's onHotplugReceived callback handling if (connection == HWC2::Connection::Connected) { - mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId); - mHwcDisplaySlots[displayId] = displayType; + mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId); + mHwcDisplaySlots[hwcDisplayId] = displayType; } + + return displayId; } -bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp, - int32_t* outDisplay) { - auto display = mHwcDevice->getDisplayById(displayId); - if (!display) { - ALOGE("onVsync Failed to find display %" PRIu64, displayId); - return false; - } - auto displayType = HWC2::DisplayType::Invalid; - auto error = display->getType(&displayType); - if (error != HWC2::Error::None) { - ALOGE("onVsync: Failed to determine type of display %" PRIu64, - display->getId()); +bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp, int32_t* outDisplayId) { + const auto it = mHwcDisplaySlots.find(hwcDisplayId); + if (it == mHwcDisplaySlots.end()) { + LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid display"); return false; } - if (displayType == HWC2::DisplayType::Virtual) { - ALOGE("Virtual display %" PRIu64 " passed to vsync callback", - display->getId()); - return false; - } + const int32_t displayId = it->second; + RETURN_IF_INVALID_DISPLAY(displayId, false); - if (mHwcDisplaySlots.count(display->getId()) == 0) { - ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback", - display->getId()); + const auto& displayData = mDisplayData[displayId]; + if (displayData.isVirtual) { + LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display"); return false; } - int32_t disp = mHwcDisplaySlots[display->getId()]; { Mutex::Autolock _l(mLock); @@ -185,22 +202,22 @@ bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp, // with the same timestamp when turning the display off and on. This // is a bug in the HWC implementation, but filter the extra events // out here so they don't cause havoc downstream. - if (timestamp == mLastHwVSync[disp]) { + if (timestamp == mLastHwVSync[displayId]) { ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", timestamp); return false; } - mLastHwVSync[disp] = timestamp; + mLastHwVSync[displayId] = timestamp; } - if (outDisplay) { - *outDisplay = disp; + if (outDisplayId) { + *outDisplayId = displayId; } char tag[16]; - snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); - ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); + snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", displayId); + ATRACE_INT(tag, ++mVSyncCounts[displayId] & 1); return true; } @@ -208,16 +225,15 @@ bool HWComposer::onVsync(hwc2_display_t displayId, int64_t timestamp, status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, ui::PixelFormat* format, int32_t *outId) { if (mRemainingHwcVirtualDisplays == 0) { - ALOGE("allocateVirtualDisplay: No remaining virtual displays"); + ALOGE("%s: No remaining virtual displays", __FUNCTION__); return NO_MEMORY; } if (SurfaceFlinger::maxVirtualDisplaySize != 0 && (width > SurfaceFlinger::maxVirtualDisplaySize || height > SurfaceFlinger::maxVirtualDisplaySize)) { - ALOGE("createVirtualDisplay: Can't create a virtual display with" - " a dimension > %" PRIu64 " (tried %u x %u)", - SurfaceFlinger::maxVirtualDisplaySize, width, height); + ALOGE("%s: Display size %ux%u exceeds maximum dimension of %" PRIu64, __FUNCTION__, width, + height, SurfaceFlinger::maxVirtualDisplaySize); return INVALID_OPERATION; } @@ -225,7 +241,7 @@ status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, auto error = mHwcDevice->createVirtualDisplay(width, height, format, &display); if (error != HWC2::Error::None) { - ALOGE("allocateVirtualDisplay: Failed to create HWC virtual display"); + ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__); return NO_MEMORY; } @@ -238,11 +254,13 @@ status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, displaySlot = mDisplayData.size(); mDisplayData.resize(displaySlot + 1); } else { - ALOGE("allocateVirtualDisplay: Unable to allocate a display slot"); + ALOGE("%s: Unable to allocate a display slot", __FUNCTION__); return NO_MEMORY; } - mDisplayData[displaySlot].hwcDisplay = display; + auto& displayData = mDisplayData[displaySlot]; + displayData.hwcDisplay = display; + displayData.isVirtual = true; --mRemainingHwcVirtualDisplays; *outId = static_cast<int32_t>(displaySlot); @@ -319,21 +337,19 @@ std::shared_ptr<const HWC2::Display::Config> } int HWComposer::getActiveConfigIndex(int32_t displayId) const { - if (!isValidDisplay(displayId)) { - ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId); - return -1; - } + RETURN_IF_INVALID_DISPLAY(displayId, -1); + int index; auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index); if (error == HWC2::Error::BadConfig) { - ALOGE("getActiveConfigIndex: No config active, returning -1"); - return -1; - } else if (error != HWC2::Error::None) { - ALOGE("getActiveConfigIndex failed for display %d: %s (%d)", displayId, - to_string(error).c_str(), static_cast<int32_t>(error)); + LOG_DISPLAY_ERROR(displayId, "No active config"); return -1; - } else if (index < 0) { - ALOGE("getActiveConfigIndex returned an unknown config for display %d", displayId); + } + + RETURN_IF_HWC_ERROR(error, displayId, -1); + + if (index < 0) { + LOG_DISPLAY_ERROR(displayId, "Unknown config"); return -1; } @@ -365,19 +381,19 @@ status_t HWComposer::setActiveColorMode(int32_t displayId, ui::ColorMode mode, void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) { - if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) { - ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId); + RETURN_IF_INVALID_DISPLAY(displayId); + auto& displayData = mDisplayData[displayId]; + + if (displayData.isVirtual) { + LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display"); return; } - RETURN_IF_INVALID_DISPLAY(displayId); - // 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 // affect other threads. Mutex::Autolock _l(mVsyncLock); - auto& displayData = mDisplayData[displayId]; if (enabled != displayData.vsyncEnabled) { ATRACE_CALL(); auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); @@ -403,11 +419,12 @@ status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot, return NO_ERROR; } -status_t HWComposer::prepare(DisplayDevice& displayDevice) { +status_t HWComposer::prepare(DisplayDevice& display, + std::vector<CompositionInfo>& compositionData) { ATRACE_CALL(); Mutex::Autolock _l(mDisplayLock); - auto displayId = displayDevice.getHwcDisplayId(); + const auto displayId = display.getId(); if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { ALOGV("Skipping HWComposer prepare for non-HWC display"); return NO_ERROR; @@ -474,18 +491,20 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { displayData.hasClientComposition = false; displayData.hasDeviceComposition = false; - for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) { - auto hwcLayer = layer->getHwcLayer(displayId); + for (auto& compositionInfo : compositionData) { + auto hwcLayer = compositionInfo.hwc.hwcLayer; - if (changedTypes.count(hwcLayer) != 0) { + if (changedTypes.count(&*hwcLayer) != 0) { // We pass false so we only update our state and don't call back // into the HWC device - validateChange(layer->getCompositionType(displayId), - changedTypes[hwcLayer]); - layer->setCompositionType(displayId, changedTypes[hwcLayer], false); + validateChange(compositionInfo.compositionType, + changedTypes[&*hwcLayer]); + compositionInfo.compositionType = changedTypes[&*hwcLayer]; + compositionInfo.layer->mLayer->setCompositionType(displayId, + compositionInfo.compositionType, false); } - switch (layer->getCompositionType(displayId)) { + switch (compositionInfo.compositionType) { case HWC2::Composition::Client: displayData.hasClientComposition = true; break; @@ -499,17 +518,19 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { break; } - if (layerRequests.count(hwcLayer) != 0 && - layerRequests[hwcLayer] == + if (layerRequests.count(&*hwcLayer) != 0 && + layerRequests[&*hwcLayer] == HWC2::LayerRequest::ClearClientTarget) { - layer->setClearClientTarget(displayId, true); + compositionInfo.hwc.clearClientTarget = true; + compositionInfo.layer->mLayer->setClearClientTarget(displayId, true); } else { - if (layerRequests.count(hwcLayer) != 0) { + if (layerRequests.count(&*hwcLayer) != 0) { LOG_DISPLAY_ERROR(displayId, - ("Unknown layer request " + to_string(layerRequests[hwcLayer])) + ("Unknown layer request " + to_string(layerRequests[&*hwcLayer])) .c_str()); } - layer->setClearClientTarget(displayId, false); + compositionInfo.hwc.clearClientTarget = false; + compositionInfo.layer->mLayer->setClearClientTarget(displayId, false); } } @@ -601,7 +622,8 @@ status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { ALOGV("setPowerMode(%d, %d)", displayId, intMode); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - if (displayId >= VIRTUAL_DISPLAY_ID_BASE) { + const auto& displayData = mDisplayData[displayId]; + if (displayData.isVirtual) { LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display"); return INVALID_OPERATION; } @@ -611,7 +633,7 @@ status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { setVsyncEnabled(displayId, HWC2::Vsync::Disable); } - auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + auto& hwcDisplay = displayData.hwcDisplay; switch (mode) { case HWC2::PowerMode::Off: case HWC2::PowerMode::On: @@ -680,43 +702,35 @@ status_t HWComposer::setColorTransform(int32_t displayId, return NO_ERROR; } -void HWComposer::disconnectDisplay(int displayId) { - LOG_ALWAYS_FATAL_IF(displayId < 0); +void HWComposer::disconnectDisplay(int32_t displayId) { + RETURN_IF_INVALID_DISPLAY(displayId); auto& displayData = mDisplayData[displayId]; - auto displayType = HWC2::DisplayType::Invalid; - auto error = displayData.hwcDisplay->getType(&displayType); - RETURN_IF_HWC_ERROR_FOR("getType", error, displayId); - // If this was a virtual display, add its slot back for reuse by future // virtual displays - if (displayType == HWC2::DisplayType::Virtual) { + if (displayData.isVirtual) { mFreeDisplaySlots.insert(displayId); ++mRemainingHwcVirtualDisplays; } - auto hwcId = displayData.hwcDisplay->getId(); - mHwcDisplaySlots.erase(hwcId); - displayData.reset(); + const auto hwcDisplayId = displayData.hwcDisplay->getId(); + mHwcDisplaySlots.erase(hwcDisplayId); + displayData = DisplayData(); - mHwcDevice->destroyDisplay(hwcId); + mHwcDevice->destroyDisplay(hwcDisplayId); } status_t HWComposer::setOutputBuffer(int32_t displayId, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto& displayData = mDisplayData[displayId]; - auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; - auto displayType = HWC2::DisplayType::Invalid; - auto error = hwcDisplay->getType(&displayType); - RETURN_IF_HWC_ERROR_FOR("getType", error, displayId, NAME_NOT_FOUND); - - if (displayType != HWC2::DisplayType::Virtual) { + if (!displayData.isVirtual) { LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display"); return INVALID_OPERATION; } - error = hwcDisplay->setOutputBuffer(buffer, acquireFence); + auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence); RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); return NO_ERROR; } @@ -738,12 +752,7 @@ status_t HWComposer::getHdrCapabilities( int32_t HWComposer::getSupportedPerFrameMetadata(int32_t displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, 0); - - int32_t supportedMetadata; - auto error = mDisplayData[displayId].hwcDisplay->getSupportedPerFrameMetadata( - &supportedMetadata); - RETURN_IF_HWC_ERROR(error, displayId, 0); - return supportedMetadata; + return mDisplayData[displayId].hwcDisplay->getSupportedPerFrameMetadata(); } std::vector<ui::RenderIntent> HWComposer::getRenderIntents(int32_t displayId, @@ -805,26 +814,4 @@ HWComposer::getHwcDisplayId(int32_t displayId) const { return mDisplayData[displayId].hwcDisplay->getId(); } -// --------------------------------------------------------------------------- - -HWComposer::DisplayData::DisplayData() - : hasClientComposition(false), - hasDeviceComposition(false), - hwcDisplay(nullptr), - lastPresentFence(Fence::NO_FENCE), - outbufHandle(nullptr), - outbufAcquireFence(Fence::NO_FENCE), - vsyncEnabled(HWC2::Vsync::Disable) { - ALOGV("Created new DisplayData"); -} - -HWComposer::DisplayData::~DisplayData() { -} - -void HWComposer::DisplayData::reset() { - ALOGV("DisplayData reset"); - *this = DisplayData(); -} - -// --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index f9689482cc..4777ca9a9b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -37,6 +37,8 @@ #include <set> #include <vector> +#include "DisplayIdentification.h" + extern "C" int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain); @@ -59,6 +61,7 @@ class NativeHandle; class Region; class String8; class TestableSurfaceFlinger; +struct CompositionInfo; namespace Hwc2 { class Composer; @@ -74,6 +77,9 @@ public: void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId); + bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort, + DisplayIdentificationData* outData) const; + bool hasCapability(HWC2::Capability capability) const; // Attempts to allocate a virtual display. If the virtual display is created @@ -87,7 +93,8 @@ public: void destroyLayer(int32_t displayId, HWC2::Layer* layer); // Asks the HAL what it can do - status_t prepare(DisplayDevice& displayDevice); + status_t prepare(DisplayDevice& display, + std::vector<CompositionInfo>& compositionData); status_t setClientTarget(int32_t displayId, uint32_t slot, const sp<Fence>& acquireFence, @@ -147,9 +154,9 @@ public: // Returns true if successful, false otherwise. The // DisplayDevice::DisplayType of the display is returned as an output param. - bool onVsync(hwc2_display_t displayId, int64_t timestamp, - int32_t* outDisplay); - void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection); + bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp, int32_t* outDisplay); + std::optional<DisplayId> onHotplug(hwc2_display_t hwcDisplayId, int32_t displayType, + HWC2::Connection connection); void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled); @@ -183,31 +190,26 @@ private: // For unit tests friend TestableSurfaceFlinger; - static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; - bool isValidDisplay(int32_t displayId) const; static void validateChange(HWC2::Composition from, HWC2::Composition to); struct cb_context; struct DisplayData { - DisplayData(); - ~DisplayData(); - void reset(); - - bool hasClientComposition; - bool hasDeviceComposition; - HWC2::Display* hwcDisplay; + bool isVirtual = false; + bool hasClientComposition = false; + bool hasDeviceComposition = false; + HWC2::Display* hwcDisplay = nullptr; HWC2::DisplayRequest displayRequests; - sp<Fence> lastPresentFence; // signals when the last set op retires + 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; - sp<Fence> outbufAcquireFence; + buffer_handle_t outbufHandle = nullptr; + sp<Fence> outbufAcquireFence = Fence::NO_FENCE; mutable std::unordered_map<int32_t, std::shared_ptr<const HWC2::Display::Config>> configMap; // protected by mVsyncLock - HWC2::Vsync vsyncEnabled; + HWC2::Vsync vsyncEnabled = HWC2::Vsync::Disable; bool validateWasSkipped; HWC2::Error presentError; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h deleted file mode 100644 index fe7944f635..0000000000 --- a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2010 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_SF_HWCOMPOSER_HWC1_H -#define ANDROID_SF_HWCOMPOSER_HWC1_H - -#include <stdint.h> -#include <sys/types.h> - -#include <hardware/hwcomposer_defs.h> - -#include <system/graphics.h> - -#include <ui/Fence.h> - -#include <utils/BitSet.h> -#include <utils/Condition.h> -#include <utils/Mutex.h> -#include <utils/StrongPointer.h> -#include <utils/Thread.h> -#include <utils/Timers.h> -#include <utils/Vector.h> - -extern "C" int clock_nanosleep(clockid_t clock_id, int flags, - const struct timespec *request, - struct timespec *remain); - -struct hwc_composer_device_1; -struct hwc_display_contents_1; -struct hwc_layer_1; -struct hwc_procs; -struct framebuffer_device_t; - -namespace android { -// --------------------------------------------------------------------------- - -class Fence; -class FloatRect; -class GraphicBuffer; -class NativeHandle; -class Region; -class String8; -class SurfaceFlinger; - -class HWComposer -{ -public: - class EventHandler { - friend class HWComposer; - virtual void onVSyncReceived( - HWComposer* composer, int32_t disp, nsecs_t timestamp) = 0; - virtual void onHotplugReceived(HWComposer* composer, int disp, bool connected) = 0; - virtual void onInvalidateReceived(HWComposer* composer) = 0; - protected: - virtual ~EventHandler() {} - }; - - enum { - NUM_BUILTIN_DISPLAYS = HWC_NUM_PHYSICAL_DISPLAY_TYPES, - MAX_HWC_DISPLAYS = HWC_NUM_DISPLAY_TYPES, - VIRTUAL_DISPLAY_ID_BASE = HWC_DISPLAY_VIRTUAL, - }; - - HWComposer( - const sp<SurfaceFlinger>& flinger, - EventHandler& handler); - - ~HWComposer(); - - status_t initCheck() const; - - // Returns a display ID starting at VIRTUAL_DISPLAY_ID_BASE, this ID is to - // be used with createWorkList (and all other methods requiring an ID - // below). - // IDs below NUM_BUILTIN_DISPLAYS are pre-defined and therefore are - // always valid. - // Returns -1 if an ID cannot be allocated - int32_t allocateDisplayId(); - - // Recycles the given virtual display ID and frees the associated worklist. - // IDs below NUM_BUILTIN_DISPLAYS are not recycled. - status_t freeDisplayId(int32_t id); - - - // Asks the HAL what it can do - status_t prepare(); - - // commits the list - status_t commit(); - - // set power mode - status_t setPowerMode(int disp, int mode); - - // set active config - status_t setActiveConfig(int disp, int mode); - - // reset state when an external, non-virtual display is disconnected - void disconnectDisplay(int disp); - - // create a work list for numLayers layer. sets HWC_GEOMETRY_CHANGED. - status_t createWorkList(int32_t id, size_t numLayers); - - bool supportsFramebufferTarget() const; - - // does this display have layers handled by HWC - bool hasHwcComposition(int32_t id) const; - - // does this display have layers handled by GLES - bool hasGlesComposition(int32_t id) const; - - // get the releaseFence file descriptor for a display's framebuffer layer. - // the release fence is only valid after commit() - sp<Fence> getAndResetReleaseFence(int32_t id); - - // needed forward declarations - class LayerListIterator; - - // return the visual id to be used to find a suitable EGLConfig for - // *ALL* displays. - int getVisualID() const; - - // Forwarding to FB HAL for pre-HWC-1.1 code (see FramebufferSurface). - int fbPost(int32_t id, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); - int fbCompositionComplete(); - void fbDump(String8& result); - - // Set the output buffer and acquire fence for a virtual display. - // Returns INVALID_OPERATION if id is not a virtual display. - status_t setOutputBuffer(int32_t id, const sp<Fence>& acquireFence, - const sp<GraphicBuffer>& buf); - - // Get the retire fence for the last committed frame. This fence will - // signal when the h/w composer is completely finished with the frame. - // For physical displays, it is no longer being displayed. For virtual - // displays, writes to the output buffer are complete. - sp<Fence> getLastRetireFence(int32_t id) const; - - status_t setCursorPositionAsync(int32_t id, const Rect &pos); - - /* - * Interface to hardware composer's layers functionality. - * This abstracts the HAL interface to layers which can evolve in - * incompatible ways from one release to another. - * The idea is that we could extend this interface as we add - * features to h/w composer. - */ - class HWCLayerInterface { - protected: - virtual ~HWCLayerInterface() { } - public: - virtual int32_t getCompositionType() const = 0; - virtual uint32_t getHints() const = 0; - virtual sp<Fence> getAndResetReleaseFence() = 0; - virtual void setDefaultState() = 0; - virtual void setSkip(bool skip) = 0; - virtual void setIsCursorLayerHint(bool isCursor = true) = 0; - virtual void setBlending(uint32_t blending) = 0; - virtual void setTransform(uint32_t transform) = 0; - virtual void setFrame(const Rect& frame) = 0; - virtual void setCrop(const FloatRect& crop) = 0; - virtual void setVisibleRegionScreen(const Region& reg) = 0; - virtual void setSurfaceDamage(const Region& reg) = 0; - virtual void setSidebandStream(const sp<NativeHandle>& stream) = 0; - virtual void setBuffer(const sp<GraphicBuffer>& buffer) = 0; - virtual void setAcquireFenceFd(int fenceFd) = 0; - virtual void setPlaneAlpha(uint8_t alpha) = 0; - virtual void onDisplayed() = 0; - }; - - /* - * Interface used to implement an iterator to a list - * of HWCLayer. - */ - class HWCLayer : public HWCLayerInterface { - friend class LayerListIterator; - // select the layer at the given index - virtual status_t setLayer(size_t index) = 0; - virtual HWCLayer* dup() = 0; - static HWCLayer* copy(HWCLayer *rhs) { - return rhs ? rhs->dup() : nullptr; - } - protected: - virtual ~HWCLayer() { } - }; - - /* - * Iterator through a HWCLayer list. - * This behaves more or less like a forward iterator. - */ - class LayerListIterator { - friend class HWComposer; - HWCLayer* const mLayerList; - size_t mIndex; - - LayerListIterator() : mLayerList(nullptr), mIndex(0) { } - - LayerListIterator(HWCLayer* layer, size_t index) - : mLayerList(layer), mIndex(index) { } - - // we don't allow assignment, because we don't need it for now - LayerListIterator& operator = (const LayerListIterator& rhs); - - public: - // copy operators - LayerListIterator(const LayerListIterator& rhs) - : mLayerList(HWCLayer::copy(rhs.mLayerList)), mIndex(rhs.mIndex) { - } - - ~LayerListIterator() { delete mLayerList; } - - // pre-increment - LayerListIterator& operator++() { - mLayerList->setLayer(++mIndex); - return *this; - } - - // dereference - HWCLayerInterface& operator * () { return *mLayerList; } - HWCLayerInterface* operator -> () { return mLayerList; } - - // comparison - bool operator == (const LayerListIterator& rhs) const { - return mIndex == rhs.mIndex; - } - bool operator != (const LayerListIterator& rhs) const { - return !operator==(rhs); - } - }; - - // Returns an iterator to the beginning of the layer list - LayerListIterator begin(int32_t id); - - // Returns an iterator to the end of the layer list - LayerListIterator end(int32_t id); - - - // Events handling --------------------------------------------------------- - - enum { - EVENT_VSYNC = HWC_EVENT_VSYNC - }; - - void eventControl(int disp, int event, int enabled); - - struct DisplayConfig { - uint32_t width; - uint32_t height; - float xdpi; - float ydpi; - nsecs_t refresh; - android_color_mode_t colorMode; - bool operator==(const DisplayConfig& rhs) const { - return width == rhs.width && - height == rhs.height && - xdpi == rhs.xdpi && - ydpi == rhs.ydpi && - refresh == rhs.refresh && - colorMode == rhs.colorMode; - } - }; - - // Query display parameters. Pass in a display index (e.g. - // HWC_DISPLAY_PRIMARY). - nsecs_t getRefreshTimestamp(int disp) const; - sp<Fence> getDisplayFence(int disp) const; - uint32_t getFormat(int disp) const; - bool isConnected(int disp) const; - - // These return the values for the current config of a given display index. - // To get the values for all configs, use getConfigs below. - uint32_t getWidth(int disp) const; - uint32_t getHeight(int disp) const; - float getDpiX(int disp) const; - float getDpiY(int disp) const; - nsecs_t getRefreshPeriod(int disp) const; - android_color_mode_t getColorMode(int disp) const; - - const Vector<DisplayConfig>& getConfigs(int disp) const; - size_t getCurrentConfig(int disp) const; - - status_t setVirtualDisplayProperties(int32_t id, uint32_t w, uint32_t h, - uint32_t format); - - // this class is only used to fake the VSync event on systems that don't - // have it. - class VSyncThread : public Thread { - HWComposer& mHwc; - mutable Mutex mLock; - Condition mCondition; - bool mEnabled; - mutable nsecs_t mNextFakeVSync; - nsecs_t mRefreshPeriod; - virtual void onFirstRef(); - virtual bool threadLoop(); - public: - VSyncThread(HWComposer& hwc); - void setEnabled(bool enabled); - }; - - friend class VSyncThread; - - // for debugging ---------------------------------------------------------- - void dump(String8& out) const; - -private: - void loadHwcModule(); - int loadFbHalModule(); - - LayerListIterator getLayerIterator(int32_t id, size_t index); - - struct cb_context; - - static void hook_invalidate(const struct hwc_procs* procs); - static void hook_vsync(const struct hwc_procs* procs, int disp, - int64_t timestamp); - static void hook_hotplug(const struct hwc_procs* procs, int disp, - int connected); - - inline void invalidate(); - inline void vsync(int disp, int64_t timestamp); - inline void hotplug(int disp, int connected); - - status_t queryDisplayProperties(int disp); - - status_t setFramebufferTarget(int32_t id, - const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf); - - struct DisplayData { - DisplayData(); - ~DisplayData(); - Vector<DisplayConfig> configs; - size_t currentConfig; - uint32_t format; // pixel format from FB hal, for pre-hwc-1.1 - bool connected; - bool hasFbComp; - bool hasOvComp; - size_t capacity; - hwc_display_contents_1* list; - hwc_layer_1* framebufferTarget; - buffer_handle_t fbTargetHandle; - sp<Fence> lastRetireFence; // signals when the last set op retires - sp<Fence> lastDisplayFence; // signals when the last set op takes - // effect on screen - buffer_handle_t outbufHandle; - sp<Fence> outbufAcquireFence; - - // protected by mEventControlLock - int32_t events; - - // We need to hold "copies" of these for memory management purposes. The - // actual hwc_layer_1_t holds pointers to the memory within. Vector<> - // internally doesn't copy the memory unless one of the copies is - // modified. - Vector<Region> visibleRegions; - Vector<Region> surfaceDamageRegions; - }; - - sp<SurfaceFlinger> mFlinger; - framebuffer_device_t* mFbDev; - struct hwc_composer_device_1* mHwc; - // invariant: mLists[0] != nullptr iff mHwc != nullptr - // mLists[i>0] can be nullptr. that display is to be ignored - struct hwc_display_contents_1* mLists[MAX_HWC_DISPLAYS]; - DisplayData mDisplayData[MAX_HWC_DISPLAYS]; - // protect mDisplayData from races between prepare and dump - mutable Mutex mDisplayLock; - size_t mNumDisplays; - - cb_context* mCBContext; - EventHandler& mEventHandler; - size_t mVSyncCounts[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; - sp<VSyncThread> mVSyncThread; - bool mDebugForceFakeVSync; - BitSet32 mAllocatedDisplayIDs; - - // protected by mLock - mutable Mutex mLock; - mutable nsecs_t mLastHwVSync[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; - - // thread-safe - mutable Mutex mEventControlLock; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_SF_HWCOMPOSER_H diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 9a2817dba1..c111a27f51 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -32,11 +32,11 @@ namespace android { // --------------------------------------------------------------------------- #define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \ - mDisplayName.string(), ##__VA_ARGS__) + mDisplayName.c_str(), ##__VA_ARGS__) #define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \ - mDisplayName.string(), ##__VA_ARGS__) + mDisplayName.c_str(), ##__VA_ARGS__) #define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \ - mDisplayName.string(), ##__VA_ARGS__) + mDisplayName.c_str(), ##__VA_ARGS__) static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) { switch (type) { @@ -52,7 +52,7 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, - const String8& name) + const std::string& name) : ConsumerBase(bqConsumer), mHwc(hwc), mDisplayId(dispId), @@ -102,7 +102,7 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, } mOutputFormat = mDefaultOutputFormat; - ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.string()); + ConsumerBase::mName = String8::format("VDS: %s", mDisplayName.c_str()); mConsumer->setConsumerName(ConsumerBase::mName); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); mConsumer->setDefaultBufferSize(sinkWidth, sinkHeight); diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 5c8aceae92..4bd4d0fe61 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -17,6 +17,8 @@ #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H +#include <string> + #include "DisplaySurface.h" #include "HWComposerBufferCache.h" @@ -77,7 +79,7 @@ public: const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, - const String8& name); + const std::string& name); // // DisplaySurface interface @@ -153,7 +155,7 @@ private: // HWComposer& mHwc; const int32_t mDisplayId; - const String8 mDisplayName; + const std::string mDisplayName; sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_* uint32_t mDefaultOutputFormat; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 90e0e9e0d7..2e564e7d94 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -50,11 +50,10 @@ #include "LayerRejecter.h" #include "MonitoredProducer.h" #include "SurfaceFlinger.h" -#include "clz.h" #include "DisplayHardware/HWComposer.h" -#include "RenderEngine/RenderEngine.h" +#include <renderengine/RenderEngine.h> #include <mutex> #include "LayerProtoHelper.h" @@ -63,92 +62,55 @@ namespace android { -LayerBE::LayerBE() - : mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2) { -} - - -int32_t Layer::sSequence = 1; - -Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags) - : contentDirty(false), - sequence(uint32_t(android_atomic_inc(&sSequence))), - mFlinger(flinger), - mPremultipliedAlpha(true), - mName(name), - mTransactionFlags(0), - mPendingStateMutex(), - mPendingStates(), - mQueuedFrames(0), - mSidebandStreamChanged(false), - mActiveBufferSlot(BufferQueue::INVALID_BUFFER_SLOT), - mCurrentTransform(0), - mOverrideScalingMode(-1), - mCurrentOpacity(true), - mCurrentFrameNumber(0), - mFrameLatencyNeeded(false), - mFiltering(false), - mNeedsFiltering(false), - mProtectedByApp(false), - mClientRef(client), - mPotentialCursor(false), - mQueueItemLock(), - mQueueItemCondition(), - mQueueItems(), - mLastFrameNumberReceived(0), - mAutoRefresh(false), - mFreezeGeometryUpdates(false), - mCurrentChildren(LayerVector::StateSet::Current), - mDrawingChildren(LayerVector::StateSet::Drawing) { +std::atomic<int32_t> Layer::sSequence{1}; + +Layer::Layer(const LayerCreationArgs& args) + : mFlinger(args.flinger), + mName(args.name), + mClientRef(args.client), + mBE{this, args.name.string()} { mCurrentCrop.makeInvalid(); uint32_t layerFlags = 0; - if (flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; - if (flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; - if (flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure; + if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; + if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; + if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure; - mName = name; mTransactionName = String8("TX - ") + mName; - mCurrentState.active.w = w; - mCurrentState.active.h = h; + mCurrentState.active_legacy.w = args.w; + mCurrentState.active_legacy.h = args.h; mCurrentState.flags = layerFlags; - mCurrentState.active.transform.set(0, 0); - mCurrentState.crop.makeInvalid(); - mCurrentState.finalCrop.makeInvalid(); - mCurrentState.requestedFinalCrop = mCurrentState.finalCrop; - mCurrentState.requestedCrop = mCurrentState.crop; + mCurrentState.active_legacy.transform.set(0, 0); + mCurrentState.crop_legacy.makeInvalid(); + mCurrentState.requestedCrop_legacy = mCurrentState.crop_legacy; mCurrentState.z = 0; mCurrentState.color.a = 1.0f; mCurrentState.layerStack = 0; mCurrentState.sequence = 0; - mCurrentState.requested = mCurrentState.active; + mCurrentState.requested_legacy = mCurrentState.active_legacy; mCurrentState.appId = 0; mCurrentState.type = 0; + mCurrentState.active.w = 0; + mCurrentState.active.h = 0; + mCurrentState.active.transform.set(0, 0); + mCurrentState.transform = 0; + mCurrentState.transformToDisplayInverse = false; + mCurrentState.crop.makeInvalid(); + mCurrentState.acquireFence = new Fence(-1); + mCurrentState.dataspace = ui::Dataspace::UNKNOWN; + mCurrentState.hdrMetadata.validTypes = 0; + mCurrentState.surfaceDamageRegion.clear(); + mCurrentState.api = -1; + mCurrentState.hasColorTransform = false; // drawing state & current state are identical mDrawingState = mCurrentState; CompositorTiming compositorTiming; - flinger->getCompositorTiming(&compositorTiming); + args.flinger->getCompositorTiming(&compositorTiming); mFrameEventHistory.initializeCompositorTiming(compositorTiming); -} - -void Layer::onFirstRef() NO_THREAD_SAFETY_ANALYSIS { - if (!isCreatedFromMainThread()) { - // Grab the SF state lock during this since it's the only way to safely access HWC - mFlinger->mStateLock.lock(); - } - - const auto& hwc = mFlinger->getHwComposer(); - const auto& activeConfig = hwc.getActiveConfig(HWC_DISPLAY_PRIMARY); - nsecs_t displayPeriod = activeConfig->getVsyncPeriod(); - mFrameTracker.setDisplayRefreshPeriod(displayPeriod); - - if (!isCreatedFromMainThread()) { - mFlinger->mStateLock.unlock(); - } + mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval); } Layer::~Layer() { @@ -228,33 +190,33 @@ sp<IBinder> Layer::getHandle() { // h/w composer set-up // --------------------------------------------------------------------------- -bool Layer::createHwcLayer(HWComposer* hwc, int32_t hwcId) { - LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0, - "Already have a layer for hwcId %d", hwcId); - HWC2::Layer* layer = hwc->createLayer(hwcId); +bool Layer::createHwcLayer(HWComposer* hwc, int32_t displayId) { + LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(displayId) != 0, + "Already have a layer for display %d", displayId); + auto layer = std::shared_ptr<HWC2::Layer>( + hwc->createLayer(displayId), + [hwc, displayId](HWC2::Layer* layer) { + hwc->destroyLayer(displayId, layer); }); if (!layer) { return false; } - LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[hwcId]; + LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[displayId]; hwcInfo.hwc = hwc; hwcInfo.layer = layer; layer->setLayerDestroyedListener( - [this, hwcId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(hwcId); }); + [this, displayId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(displayId); }); return true; } -bool Layer::destroyHwcLayer(int32_t hwcId) { - if (getBE().mHwcLayers.count(hwcId) == 0) { +bool Layer::destroyHwcLayer(int32_t displayId) { + if (getBE().mHwcLayers.count(displayId) == 0) { return false; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; LOG_ALWAYS_FATAL_IF(hwcInfo.layer == nullptr, "Attempt to destroy null layer"); LOG_ALWAYS_FATAL_IF(hwcInfo.hwc == nullptr, "Missing HWComposer"); - hwcInfo.hwc->destroyLayer(hwcId, hwcInfo.layer); - // The layer destroyed listener should have cleared the entry from - // mHwcLayers. Verify that. - LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.count(hwcId) != 0, - "Stale layer entry in getBE().mHwcLayers"); + hwcInfo.layer = nullptr; + return true; } @@ -304,20 +266,17 @@ static FloatRect reduce(const FloatRect& win, const Region& exclude) { } Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const { - const Layer::State& s(getDrawingState()); - Rect win(s.active.w, s.active.h); + const State& s(getDrawingState()); + Rect win(getActiveWidth(s), getActiveHeight(s)); - if (!s.crop.isEmpty()) { - win.intersect(s.crop, &win); + Rect crop = getCrop(s); + if (!crop.isEmpty()) { + win.intersect(crop, &win); } - Transform t = getTransform(); + ui::Transform t = getTransform(); win = t.transform(win); - if (!s.finalCrop.isEmpty()) { - win.intersect(s.finalCrop, &win); - } - const sp<Layer>& p = mDrawingParent.promote(); // Now we need to calculate the parent bounds, so we can clip ourselves to those. // When calculating the parent bounds for purposes of clipping, @@ -334,7 +293,7 @@ Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const { } if (reduceTransparentRegion) { - auto const screenTransparentRegion = t.transform(s.activeTransparentRegion); + auto const screenTransparentRegion = t.transform(getActiveTransparentRegion(s)); win = reduce(win, screenTransparentRegion); } @@ -342,16 +301,17 @@ Rect Layer::computeScreenBounds(bool reduceTransparentRegion) const { } FloatRect Layer::computeBounds() const { - const Layer::State& s(getDrawingState()); - return computeBounds(s.activeTransparentRegion); + const State& s(getDrawingState()); + return computeBounds(getActiveTransparentRegion(s)); } FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const { - const Layer::State& s(getDrawingState()); - Rect win(s.active.w, s.active.h); + const State& s(getDrawingState()); + Rect win(getActiveWidth(s), getActiveHeight(s)); - if (!s.crop.isEmpty()) { - win.intersect(s.crop, &win); + Rect crop = getCrop(s); + if (!crop.isEmpty()) { + win.intersect(crop, &win); } const auto& p = mDrawingParent.promote(); @@ -363,16 +323,11 @@ FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const { parentBounds = p->computeBounds(Region()); } - Transform t = s.active.transform; - + ui::Transform t = s.active_legacy.transform; - if (p != nullptr || !s.finalCrop.isEmpty()) { + if (p != nullptr) { floatWin = t.transform(floatWin); floatWin = floatWin.intersect(parentBounds); - - if (!s.finalCrop.isEmpty()) { - floatWin = floatWin.intersect(s.finalCrop.toFloatRect()); - } floatWin = t.inverse().transform(floatWin); } @@ -380,7 +335,7 @@ FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const { return reduce(floatWin, activeTransparentRegion); } -Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const { +Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const { // the crop is the area of the window that gets cropped, but not // scaled in any ways. const State& s(getDrawingState()); @@ -392,32 +347,28 @@ Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const { // FIXME: the 3 lines below can produce slightly incorrect clipping when we have // a viewport clipping and a window transform. we should use floating point to fix this. - Rect activeCrop(s.active.w, s.active.h); - if (!s.crop.isEmpty()) { - activeCrop.intersect(s.crop, &activeCrop); + Rect activeCrop(getActiveWidth(s), getActiveHeight(s)); + Rect crop = getCrop(s); + if (!crop.isEmpty()) { + activeCrop.intersect(crop, &activeCrop); } - Transform t = getTransform(); + ui::Transform t = getTransform(); activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) { + if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { activeCrop.clear(); } - if (!s.finalCrop.isEmpty()) { - if (!activeCrop.intersect(s.finalCrop, &activeCrop)) { - activeCrop.clear(); - } - } const auto& p = mDrawingParent.promote(); if (p != nullptr) { - auto parentCrop = p->computeInitialCrop(hw); + auto parentCrop = p->computeInitialCrop(display); activeCrop.intersect(parentCrop, &activeCrop); } return activeCrop; } -FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { +FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const { // the content crop is the area of the content that gets scaled to the // layer's size. This is in buffer space. FloatRect crop = getContentCrop().toFloatRect(); @@ -426,8 +377,8 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { const State& s(getDrawingState()); // Screen space to make reduction to parent crop clearer. - Rect activeCrop = computeInitialCrop(hw); - Transform t = getTransform(); + Rect activeCrop = computeInitialCrop(display); + ui::Transform t = getTransform(); // Back to layer space to work with the content crop. activeCrop = t.inverse().transform(activeCrop); @@ -437,12 +388,12 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { // transform.inverse().transform(transform.transform(Rect)) != Rect // in which case we need to make sure the final rect is clipped to the // display bounds. - if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) { + if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) { activeCrop.clear(); } // subtract the transparent region and snap to the bounds - activeCrop = reduce(activeCrop, s.activeTransparentRegion); + activeCrop = reduce(activeCrop, getActiveTransparentRegion(s)); // Transform the window crop to match the buffer coordinate system, // which means using the inverse of the current transform set on the @@ -459,11 +410,12 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; } // and apply to the current transform - invTransform = (Transform(invTransformOrient) * Transform(invTransform)).getOrientation(); + invTransform = (ui::Transform(invTransformOrient) * + ui::Transform(invTransform)).getOrientation(); } - int winWidth = s.active.w; - int winHeight = s.active.h; + int winWidth = getActiveWidth(s); + int winHeight = getActiveHeight(s); if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { // If the activeCrop has been rotate the ends are rotated but not // the space itself so when transforming ends back we can't rely on @@ -475,10 +427,10 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { if (is_h_flipped == is_v_flipped) { invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; } - winWidth = s.active.h; - winHeight = s.active.w; + winWidth = getActiveHeight(s); + winHeight = getActiveWidth(s); } - const Rect winCrop = activeCrop.transform(invTransform, s.active.w, s.active.h); + const Rect winCrop = activeCrop.transform(invTransform, getActiveWidth(s), getActiveHeight(s)); // below, crop is intersected with winCrop expressed in crop's coordinate space float xScale = crop.getWidth() / float(winWidth); @@ -497,18 +449,19 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { return crop; } -void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z) -{ - const auto hwcId = displayDevice->getHwcDisplayId(); - if (!hasHwcLayer(hwcId)) { +void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { + const auto displayId = display->getId(); + if (!hasHwcLayer(displayId)) { + ALOGE("[%s] failed to setGeometry: no HWC layer found (%d)", + mName.string(), displayId); return; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; // enable this layer hwcInfo.forceClientComposition = false; - if (isSecure() && !displayDevice->isSecure()) { + if (isSecure() && !display->isSecure()) { hwcInfo.forceClientComposition = true; } @@ -527,15 +480,16 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z " %s (%d)", mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.blendMode = blendMode; // apply the layer's transform, followed by the display's global transform // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion(s.activeTransparentRegion); - Transform t = getTransform(); - if (!s.crop.isEmpty()) { - Rect activeCrop(s.crop); + Region activeTransparentRegion(getActiveTransparentRegion(s)); + ui::Transform t = getTransform(); + Rect activeCrop = getCrop(s); + if (!activeCrop.isEmpty()) { activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) { + if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { activeCrop.clear(); } activeCrop = t.inverse().transform(activeCrop, true); @@ -545,29 +499,25 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z // transform.inverse().transform(transform.transform(Rect)) != Rect // in which case we need to make sure the final rect is clipped to the // display bounds. - if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) { + if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) { activeCrop.clear(); } // mark regions outside the crop as transparent - activeTransparentRegion.orSelf(Rect(0, 0, s.active.w, activeCrop.top)); - activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom, s.active.w, s.active.h)); + activeTransparentRegion.orSelf(Rect(0, 0, getActiveWidth(s), activeCrop.top)); + activeTransparentRegion.orSelf( + Rect(0, activeCrop.bottom, getActiveWidth(s), getActiveHeight(s))); activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); activeTransparentRegion.orSelf( - Rect(activeCrop.right, activeCrop.top, s.active.w, activeCrop.bottom)); + Rect(activeCrop.right, activeCrop.top, getActiveWidth(s), activeCrop.bottom)); } // computeBounds returns a FloatRect to provide more accuracy during the // transformation. We then round upon constructing 'frame'. Rect frame{t.transform(computeBounds(activeTransparentRegion))}; - if (!s.finalCrop.isEmpty()) { - if (!frame.intersect(s.finalCrop, &frame)) { - frame.clear(); - } - } - if (!frame.intersect(displayDevice->getViewport(), &frame)) { + if (!frame.intersect(display->getViewport(), &frame)) { frame.clear(); } - const Transform& tr(displayDevice->getTransform()); + const ui::Transform& tr = display->getTransform(); Rect transformedFrame = tr.transform(frame); error = hwcLayer->setDisplayFrame(transformedFrame); if (error != HWC2::Error::None) { @@ -577,8 +527,9 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z } else { hwcInfo.displayFrame = transformedFrame; } + getBE().compositionInfo.hwc.displayFrame = transformedFrame; - FloatRect sourceCrop = computeCrop(displayDevice); + FloatRect sourceCrop = computeCrop(display); error = hwcLayer->setSourceCrop(sourceCrop); if (error != HWC2::Error::None) { ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " @@ -588,6 +539,7 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z } else { hwcInfo.sourceCrop = sourceCrop; } + getBE().compositionInfo.hwc.sourceCrop = sourceCrop; float alpha = static_cast<float>(getAlpha()); error = hwcLayer->setPlaneAlpha(alpha); @@ -595,10 +547,12 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z "[%s] Failed to set plane alpha %.3f: " "%s (%d)", mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.alpha = alpha; error = hwcLayer->setZOrder(z); ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z, to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.z = z; int type = s.type; int appId = s.appId; @@ -615,6 +569,9 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.type = type; + getBE().compositionInfo.hwc.appId = appId; + /* * Transformations are applied in this order: * 1) buffer orientation/flip/mirror @@ -623,8 +580,8 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z * (NOTE: the matrices are multiplied in reverse order) */ - const Transform bufferOrientation(mCurrentTransform); - Transform transform(tr * t * bufferOrientation); + const ui::Transform bufferOrientation(mCurrentTransform); + ui::Transform transform(tr * t * bufferOrientation); if (getTransformToDisplayInverse()) { /* @@ -643,14 +600,15 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z * computation so it's enough to just omit it in the composition. * See comment in onDraw with ref to b/36727915 for why. */ - transform = Transform(invTransform) * tr * bufferOrientation; + transform = ui::Transform(invTransform) * tr * bufferOrientation; } // this gives us only the "orientation" component of the transform const uint32_t orientation = transform.getOrientation(); - if (orientation & Transform::ROT_INVALID) { + if (orientation & ui::Transform::ROT_INVALID) { // we can only handle simple transformation hwcInfo.forceClientComposition = true; + getBE().mHwcLayers[displayId].compositionType = HWC2::Composition::Client; } else { auto transform = static_cast<HWC2::Transform>(orientation); hwcInfo.transform = transform; @@ -660,31 +618,32 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z "%s (%d)", mName.string(), to_string(transform).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.transform = transform; } } -void Layer::forceClientComposition(int32_t hwcId) { - if (getBE().mHwcLayers.count(hwcId) == 0) { - ALOGE("forceClientComposition: no HWC layer found (%d)", hwcId); +void Layer::forceClientComposition(int32_t displayId) { + if (getBE().mHwcLayers.count(displayId) == 0) { + ALOGE("forceClientComposition: no HWC layer found (%d)", displayId); return; } - getBE().mHwcLayers[hwcId].forceClientComposition = true; + getBE().mHwcLayers[displayId].forceClientComposition = true; } -bool Layer::getForceClientComposition(int32_t hwcId) { - if (getBE().mHwcLayers.count(hwcId) == 0) { - ALOGE("getForceClientComposition: no HWC layer found (%d)", hwcId); +bool Layer::getForceClientComposition(int32_t displayId) { + if (getBE().mHwcLayers.count(displayId) == 0) { + ALOGE("getForceClientComposition: no HWC layer found (%d)", displayId); return false; } - return getBE().mHwcLayers[hwcId].forceClientComposition; + return getBE().mHwcLayers[displayId].forceClientComposition; } -void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { - auto hwcId = displayDevice->getHwcDisplayId(); - if (getBE().mHwcLayers.count(hwcId) == 0 || - getCompositionType(hwcId) != HWC2::Composition::Cursor) { +void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) { + const auto displayId = display->getId(); + if (getBE().mHwcLayers.count(displayId) == 0 || + getCompositionType(displayId) != HWC2::Composition::Cursor) { return; } @@ -693,22 +652,21 @@ void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { // Apply the layer's transform, followed by the display's global transform // Here we're guaranteed that the layer's transform preserves rects - Rect win(s.active.w, s.active.h); - if (!s.crop.isEmpty()) { - win.intersect(s.crop, &win); + Rect win(getActiveWidth(s), getActiveHeight(s)); + Rect crop = getCrop(s); + if (!crop.isEmpty()) { + win.intersect(crop, &win); } // Subtract the transparent region and snap to the bounds - Rect bounds = reduce(win, s.activeTransparentRegion); + Rect bounds = reduce(win, getActiveTransparentRegion(s)); Rect frame(getTransform().transform(bounds)); - frame.intersect(displayDevice->getViewport(), &frame); - if (!s.finalCrop.isEmpty()) { - frame.intersect(s.finalCrop, &frame); - } - auto& displayTransform(displayDevice->getTransform()); + frame.intersect(display->getViewport(), &frame); + auto& displayTransform = display->getTransform(); auto position = displayTransform.transform(frame); - auto error = getBE().mHwcLayers[hwcId].layer->setCursorPosition(position.left, - position.top); + auto error = + (getBE().mHwcLayers[displayId].layer)->setCursorPosition( + position.left, position.top); ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set cursor position " "to (%d, %d): %s (%d)", @@ -720,18 +678,14 @@ void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { // drawing... // --------------------------------------------------------------------------- -void Layer::draw(const RenderArea& renderArea, const Region& clip) const { +void Layer::draw(const RenderArea& renderArea, const Region& clip) { onDraw(renderArea, clip, false); } -void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) const { +void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) { onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform); } -void Layer::draw(const RenderArea& renderArea) const { - onDraw(renderArea, Region(renderArea.getBounds()), false); -} - void Layer::clearWithOpenGL(const RenderArea& renderArea, float red, float green, float blue, float alpha) const { auto& engine(mFlinger->getRenderEngine()); @@ -744,20 +698,20 @@ void Layer::clearWithOpenGL(const RenderArea& renderArea) const { clearWithOpenGL(renderArea, 0, 0, 0, 0); } -void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) { - if (getBE().mHwcLayers.count(hwcId) == 0) { +void Layer::setCompositionType(int32_t displayId, HWC2::Composition type, bool callIntoHwc) { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("setCompositionType called without a valid HWC layer"); return; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; auto& hwcLayer = hwcInfo.layer; - ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(), + ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", (hwcLayer)->getId(), to_string(type).c_str(), static_cast<int>(callIntoHwc)); if (hwcInfo.compositionType != type) { ALOGV(" actually setting"); hwcInfo.compositionType = type; if (callIntoHwc) { - auto error = hwcLayer->setCompositionType(type); + auto error = (hwcLayer)->setCompositionType(type); ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set " "composition type %s: %s (%d)", @@ -767,33 +721,29 @@ void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callI } } -HWC2::Composition Layer::getCompositionType(int32_t hwcId) const { - if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { +HWC2::Composition Layer::getCompositionType(int32_t displayId) const { + if (getBE().mHwcLayers.count(displayId) == 0) { // If we're querying the composition type for a display that does not // have a HWC counterpart, then it will always be Client return HWC2::Composition::Client; } - if (getBE().mHwcLayers.count(hwcId) == 0) { - ALOGE("getCompositionType called with an invalid HWC layer"); - return HWC2::Composition::Invalid; - } - return getBE().mHwcLayers.at(hwcId).compositionType; + return getBE().mHwcLayers[displayId].compositionType; } -void Layer::setClearClientTarget(int32_t hwcId, bool clear) { - if (getBE().mHwcLayers.count(hwcId) == 0) { +void Layer::setClearClientTarget(int32_t displayId, bool clear) { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("setClearClientTarget called without a valid HWC layer"); return; } - getBE().mHwcLayers[hwcId].clearClientTarget = clear; + getBE().mHwcLayers[displayId].clearClientTarget = clear; } -bool Layer::getClearClientTarget(int32_t hwcId) const { - if (getBE().mHwcLayers.count(hwcId) == 0) { +bool Layer::getClearClientTarget(int32_t displayId) const { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("getClearClientTarget called without a valid HWC layer"); return false; } - return getBE().mHwcLayers.at(hwcId).clearClientTarget; + return getBE().mHwcLayers.at(displayId).clearClientTarget; } bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { @@ -808,38 +758,14 @@ bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { return true; } -void Layer::setFiltering(bool filtering) { - mFiltering = filtering; -} - -bool Layer::getFiltering() const { - return mFiltering; -} - // ---------------------------------------------------------------------------- // local state // ---------------------------------------------------------------------------- -static void boundPoint(vec2* point, const Rect& crop) { - if (point->x < crop.left) { - point->x = crop.left; - } - if (point->x > crop.right) { - point->x = crop.right; - } - if (point->y < crop.top) { - point->y = crop.top; - } - if (point->y > crop.bottom) { - point->y = crop.bottom; - } -} - -void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh, +void Layer::computeGeometry(const RenderArea& renderArea, + renderengine::Mesh& mesh, bool useIdentityTransform) const { - const Layer::State& s(getDrawingState()); - const Transform renderAreaTransform(renderArea.getTransform()); - const uint32_t height = renderArea.getHeight(); + const ui::Transform renderAreaTransform(renderArea.getTransform()); FloatRect win = computeBounds(); vec2 lt = vec2(win.left, win.top); @@ -847,7 +773,7 @@ void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh, vec2 rb = vec2(win.right, win.bottom); vec2 rt = vec2(win.right, win.top); - Transform layerTransform = getTransform(); + ui::Transform layerTransform = getTransform(); if (!useIdentityTransform) { lt = layerTransform.transform(lt); lb = layerTransform.transform(lb); @@ -855,25 +781,15 @@ void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh, rt = layerTransform.transform(rt); } - if (!s.finalCrop.isEmpty()) { - boundPoint(<, s.finalCrop); - boundPoint(&lb, s.finalCrop); - boundPoint(&rb, s.finalCrop); - boundPoint(&rt, s.finalCrop); - } - - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + renderengine::Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); position[0] = renderAreaTransform.transform(lt); position[1] = renderAreaTransform.transform(lb); position[2] = renderAreaTransform.transform(rb); position[3] = renderAreaTransform.transform(rt); - for (size_t i = 0; i < 4; i++) { - position[i].y = height - position[i].y; - } } bool Layer::isSecure() const { - const Layer::State& s(mDrawingState); + const State& s(mDrawingState); return (s.flags & layer_state_t::eLayerSecure); } @@ -909,22 +825,22 @@ void Layer::pushPendingState() { // If this transaction is waiting on the receipt of a frame, generate a sync // point and send it to the remote layer. - if (mCurrentState.barrierLayer != nullptr) { - sp<Layer> barrierLayer = mCurrentState.barrierLayer.promote(); + if (mCurrentState.barrierLayer_legacy != nullptr) { + sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote(); if (barrierLayer == nullptr) { ALOGE("[%s] Unable to promote barrier Layer.", mName.string()); // If we can't promote the layer we are intended to wait on, // then it is expired or otherwise invalid. Allow this transaction // to be applied as per normal (no synchronization). - mCurrentState.barrierLayer = nullptr; + mCurrentState.barrierLayer_legacy = nullptr; } else { - auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber); + auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy); if (barrierLayer->addSyncPoint(syncPoint)) { mRemoteSyncPoints.push_back(std::move(syncPoint)); } else { // We already missed the frame we're supposed to synchronize // on, so go ahead and apply the state update - mCurrentState.barrierLayer = nullptr; + mCurrentState.barrierLayer_legacy = nullptr; } } @@ -946,7 +862,7 @@ void Layer::popPendingState(State* stateToCommit) { bool Layer::applyPendingStates(State* stateToCommit) { bool stateUpdateAvailable = false; while (!mPendingStates.empty()) { - if (mPendingStates[0].barrierLayer != nullptr) { + if (mPendingStates[0].barrierLayer_legacy != nullptr) { if (mRemoteSyncPoints.empty()) { // If we don't have a sync point for this, apply it anyway. It // will be visually wrong, but it should keep us from getting @@ -957,7 +873,8 @@ bool Layer::applyPendingStates(State* stateToCommit) { continue; } - if (mRemoteSyncPoints.front()->getFrameNumber() != mPendingStates[0].frameNumber) { + if (mRemoteSyncPoints.front()->getFrameNumber() != + mPendingStates[0].frameNumber_legacy) { ALOGE("[%s] Unexpected sync point frame number found", mName.string()); // Signal our end of the sync point and then dispose of it @@ -994,18 +911,11 @@ bool Layer::applyPendingStates(State* stateToCommit) { return stateUpdateAvailable; } -uint32_t Layer::doTransaction(uint32_t flags) { - ATRACE_CALL(); - - pushPendingState(); - Layer::State c = getCurrentState(); - if (!applyPendingStates(&c)) { - return 0; - } - - const Layer::State& s(getDrawingState()); +uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { + const State& s(getDrawingState()); - const bool sizeChanged = (c.requested.w != s.requested.w) || (c.requested.h != s.requested.h); + const bool sizeChanged = (stateToCommit->requested_legacy.w != s.requested_legacy.w) || + (stateToCommit->requested_legacy.h != s.requested_legacy.h); if (sizeChanged) { // the size changed, we need to ask our client to request a new buffer @@ -1015,16 +925,15 @@ uint32_t Layer::doTransaction(uint32_t flags) { " requested={ wh={%4u,%4u} }}\n" " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n" " requested={ wh={%4u,%4u} }}\n", - this, getName().string(), mCurrentTransform, - getEffectiveScalingMode(), c.active.w, c.active.h, c.crop.left, c.crop.top, - c.crop.right, c.crop.bottom, c.crop.getWidth(), c.crop.getHeight(), c.requested.w, - c.requested.h, s.active.w, s.active.h, s.crop.left, s.crop.top, s.crop.right, - s.crop.bottom, s.crop.getWidth(), s.crop.getHeight(), s.requested.w, - s.requested.h); - - // record the new size, form this point on, when the client request - // a buffer, it'll get the new size. - setDefaultBufferSize(c.requested.w, c.requested.h); + this, getName().string(), mCurrentTransform, getEffectiveScalingMode(), + stateToCommit->active_legacy.w, stateToCommit->active_legacy.h, + stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top, + stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom, + stateToCommit->crop_legacy.getWidth(), stateToCommit->crop_legacy.getHeight(), + stateToCommit->requested_legacy.w, stateToCommit->requested_legacy.h, + s.active_legacy.w, s.active_legacy.h, s.crop_legacy.left, s.crop_legacy.top, + s.crop_legacy.right, s.crop_legacy.bottom, s.crop_legacy.getWidth(), + s.crop_legacy.getHeight(), s.requested_legacy.w, s.requested_legacy.h); } // Don't let Layer::doTransaction update the drawing state @@ -1045,7 +954,9 @@ uint32_t Layer::doTransaction(uint32_t flags) { // resizePending state is to avoid applying the state of the new buffer // to the old buffer. However in the state where we don't have an old buffer // there is no such concern but we may still be being used as a parent layer. - const bool resizePending = ((c.requested.w != c.active.w) || (c.requested.h != c.active.h)) && + const bool resizePending = + ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) || + (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) && (getBE().compositionInfo.mBuffer != nullptr); if (!isFixedSize()) { if (resizePending && getBE().compositionInfo.hwc.sidebandStream == nullptr) { @@ -1057,7 +968,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { // latching configuration. See Layer.h for a detailed discussion of // how geometry latching is controlled. if (!(flags & eDontUpdateGeometryState)) { - Layer::State& editCurrentState(getCurrentState()); + State& editCurrentState(getCurrentState()); // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize // mode, which causes attributes which normally latch regardless of scaling mode, @@ -1069,21 +980,37 @@ uint32_t Layer::doTransaction(uint32_t flags) { // being stored in the same data structure while having different latching rules. // b/38182305 // - // Careful that "c" and editCurrentState may not begin as equivalent due to + // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to // applyPendingStates in the presence of deferred transactions. if (mFreezeGeometryUpdates) { - float tx = c.active.transform.tx(); - float ty = c.active.transform.ty(); - c.active = c.requested; - c.active.transform.set(tx, ty); - editCurrentState.active = c.active; + float tx = stateToCommit->active_legacy.transform.tx(); + float ty = stateToCommit->active_legacy.transform.ty(); + stateToCommit->active_legacy = stateToCommit->requested_legacy; + stateToCommit->active_legacy.transform.set(tx, ty); + editCurrentState.active_legacy = stateToCommit->active_legacy; } else { - editCurrentState.active = editCurrentState.requested; - c.active = c.requested; + editCurrentState.active_legacy = editCurrentState.requested_legacy; + stateToCommit->active_legacy = stateToCommit->requested_legacy; } } - if (s.active != c.active) { + return flags; +} + +uint32_t Layer::doTransaction(uint32_t flags) { + ATRACE_CALL(); + + pushPendingState(); + State c = getCurrentState(); + if (!applyPendingStates(&c)) { + return 0; + } + + flags = doTransactionResize(flags, &c); + + const State& s(getDrawingState()); + + if (getActiveGeometry(c) != getActiveGeometry(s)) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } @@ -1094,8 +1021,8 @@ uint32_t Layer::doTransaction(uint32_t flags) { this->contentDirty = true; // we may use linear filtering, if the matrix scales us - const uint8_t type = c.active.transform.getType(); - mNeedsFiltering = (!c.active.transform.preserveRects() || (type >= Transform::SCALE)); + const uint8_t type = getActiveTransform(c).getType(); + mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE); } // If the layer is hidden, signal and clear out all local sync points so @@ -1115,28 +1042,29 @@ void Layer::commitTransaction(const State& stateToCommit) { } uint32_t Layer::getTransactionFlags(uint32_t flags) { - return android_atomic_and(~flags, &mTransactionFlags) & flags; + return mTransactionFlags.fetch_and(~flags) & flags; } uint32_t Layer::setTransactionFlags(uint32_t flags) { - return android_atomic_or(flags, &mTransactionFlags); + return mTransactionFlags.fetch_or(flags); } bool Layer::setPosition(float x, float y, bool immediate) { - if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y) + if (mCurrentState.requested_legacy.transform.tx() == x && + mCurrentState.requested_legacy.transform.ty() == y) return false; mCurrentState.sequence++; // We update the requested and active position simultaneously because // we want to apply the position portion of the transform matrix immediately, // but still delay scaling when resizing a SCALING_MODE_FREEZE layer. - mCurrentState.requested.transform.set(x, y); + mCurrentState.requested_legacy.transform.set(x, y); if (immediate && !mFreezeGeometryUpdates) { // Here we directly update the active state // unlike other setters, because we store it within // the transform, but use different latching rules. // b/38182305 - mCurrentState.active.transform.set(x, y); + mCurrentState.active_legacy.transform.set(x, y); } mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; @@ -1236,11 +1164,16 @@ bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relati } bool Layer::setSize(uint32_t w, uint32_t h) { - if (mCurrentState.requested.w == w && mCurrentState.requested.h == h) return false; - mCurrentState.requested.w = w; - mCurrentState.requested.h = h; + if (mCurrentState.requested_legacy.w == w && mCurrentState.requested_legacy.h == h) + return false; + mCurrentState.requested_legacy.w = w; + mCurrentState.requested_legacy.h = h; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); + + // record the new size, from this point on, when the client request + // a buffer, it'll get the new size. + setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h); return true; } bool Layer::setAlpha(float alpha) { @@ -1268,7 +1201,7 @@ bool Layer::setColor(const half3& color) { bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms) { - Transform t; + ui::Transform t; t.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); if (!allowNonRectPreservingTransforms && !t.preserveRects()) { @@ -1276,13 +1209,15 @@ bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, return false; } mCurrentState.sequence++; - mCurrentState.requested.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, + matrix.dsdy); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } + bool Layer::setTransparentRegionHint(const Region& transparent) { - mCurrentState.requestedTransparentRegion = transparent; + mCurrentState.requestedTransparentRegion_legacy = transparent; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -1297,26 +1232,12 @@ bool Layer::setFlags(uint8_t flags, uint8_t mask) { return true; } -bool Layer::setCrop(const Rect& crop, bool immediate) { - if (mCurrentState.requestedCrop == crop) return false; +bool Layer::setCrop_legacy(const Rect& crop, bool immediate) { + if (mCurrentState.requestedCrop_legacy == crop) return false; mCurrentState.sequence++; - mCurrentState.requestedCrop = crop; + mCurrentState.requestedCrop_legacy = crop; if (immediate && !mFreezeGeometryUpdates) { - mCurrentState.crop = crop; - } - mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; - - mCurrentState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFinalCrop(const Rect& crop, bool immediate) { - if (mCurrentState.requestedFinalCrop == crop) return false; - mCurrentState.sequence++; - mCurrentState.requestedFinalCrop = crop; - if (immediate && !mFreezeGeometryUpdates) { - mCurrentState.finalCrop = crop; + mCurrentState.crop_legacy = crop; } mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; @@ -1356,30 +1277,29 @@ uint32_t Layer::getLayerStack() const { return p->getLayerStack(); } -void Layer::deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber) { - mCurrentState.barrierLayer = barrierLayer; - mCurrentState.frameNumber = frameNumber; +void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) { + mCurrentState.barrierLayer_legacy = barrierLayer; + mCurrentState.frameNumber_legacy = frameNumber; // We don't set eTransactionNeeded, because just receiving a deferral // request without any other state updates shouldn't actually induce a delay mCurrentState.modified = true; pushPendingState(); - mCurrentState.barrierLayer = nullptr; - mCurrentState.frameNumber = 0; + mCurrentState.barrierLayer_legacy = nullptr; + mCurrentState.frameNumber_legacy = 0; mCurrentState.modified = false; } -void Layer::deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber) { +void Layer::deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber) { sp<Handle> handle = static_cast<Handle*>(barrierHandle.get()); - deferTransactionUntil(handle->owner.promote(), frameNumber); + deferTransactionUntil_legacy(handle->owner.promote(), frameNumber); } - // ---------------------------------------------------------------------------- // pageflip handling... // ---------------------------------------------------------------------------- bool Layer::isHiddenByPolicy() const { - const Layer::State& s(mDrawingState); + const State& s(mDrawingState); const auto& parent = mDrawingParent.promote(); if (parent != nullptr && parent->isHiddenByPolicy()) { return true; @@ -1400,15 +1320,15 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const { return usage; } -void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { +void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const { uint32_t orientation = 0; if (!mFlinger->mDebugDisableTransformHint) { // The transform hint is used to improve performance, but we can // only have a single transform hint, it cannot // apply to all displays. - const Transform& planeTransform(hw->getTransform()); + const ui::Transform& planeTransform = display->getTransform(); orientation = planeTransform.getOrientation(); - if (orientation & Transform::ROT_INVALID) { + if (orientation & ui::Transform::ROT_INVALID) { orientation = 0; } } @@ -1419,34 +1339,34 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- +// TODO(marissaw): add new layer state info to layer debugging LayerDebugInfo Layer::getLayerDebugInfo() const { LayerDebugInfo info; - const Layer::State& ds = getDrawingState(); + const State& ds = getDrawingState(); info.mName = getName(); sp<Layer> parent = getParent(); info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string()); info.mType = String8(getTypeId()); - info.mTransparentRegion = ds.activeTransparentRegion; + info.mTransparentRegion = ds.activeTransparentRegion_legacy; info.mVisibleRegion = visibleRegion; info.mSurfaceDamageRegion = surfaceDamageRegion; info.mLayerStack = getLayerStack(); - info.mX = ds.active.transform.tx(); - info.mY = ds.active.transform.ty(); + info.mX = ds.active_legacy.transform.tx(); + info.mY = ds.active_legacy.transform.ty(); info.mZ = ds.z; - info.mWidth = ds.active.w; - info.mHeight = ds.active.h; - info.mCrop = ds.crop; - info.mFinalCrop = ds.finalCrop; + info.mWidth = ds.active_legacy.w; + info.mHeight = ds.active_legacy.h; + info.mCrop = ds.crop_legacy; info.mColor = ds.color; info.mFlags = ds.flags; info.mPixelFormat = getPixelFormat(); info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace); - info.mMatrix[0][0] = ds.active.transform[0][0]; - info.mMatrix[0][1] = ds.active.transform[0][1]; - info.mMatrix[1][0] = ds.active.transform[1][0]; - info.mMatrix[1][1] = ds.active.transform[1][1]; + info.mMatrix[0][0] = ds.active_legacy.transform[0][0]; + info.mMatrix[0][1] = ds.active_legacy.transform[0][1]; + info.mMatrix[1][0] = ds.active_legacy.transform[1][0]; + info.mMatrix[1][1] = ds.active_legacy.transform[1][1]; { - sp<const GraphicBuffer> buffer = getBE().compositionInfo.mBuffer; + sp<const GraphicBuffer> buffer = mActiveBuffer; if (buffer != 0) { info.mActiveBufferWidth = buffer->getWidth(); info.mActiveBufferHeight = buffer->getHeight(); @@ -1467,19 +1387,22 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { } void Layer::miniDumpHeader(String8& result) { - result.append("----------------------------------------"); - result.append("---------------------------------------\n"); + result.append("-------------------------------"); + result.append("-------------------------------"); + result.append("-----------------------------\n"); result.append(" Layer name\n"); result.append(" Z | "); result.append(" Comp Type | "); + result.append(" Transform | "); result.append(" Disp Frame (LTRB) | "); result.append(" Source Crop (LTRB)\n"); - result.append("----------------------------------------"); - result.append("---------------------------------------\n"); + result.append("-------------------------------"); + result.append("-------------------------------"); + result.append("-----------------------------\n"); } -void Layer::miniDump(String8& result, int32_t hwcId) const { - if (getBE().mHwcLayers.count(hwcId) == 0) { +void Layer::miniDump(String8& result, int32_t displayId) const { + if (getBE().mHwcLayers.count(displayId) == 0) { return; } @@ -1496,21 +1419,29 @@ void Layer::miniDump(String8& result, int32_t hwcId) const { result.appendFormat(" %s\n", name.string()); - const Layer::State& layerState(getDrawingState()); - const LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(hwcId); + const State& layerState(getDrawingState()); + const LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(displayId); if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) { result.appendFormat(" rel %6d | ", layerState.z); } else { result.appendFormat(" %10d | ", layerState.z); } - result.appendFormat("%10s | ", to_string(getCompositionType(hwcId)).c_str()); + result.appendFormat("%10s | ", to_string(getCompositionType(displayId)).c_str()); + result.appendFormat("%10s | ", to_string(hwcInfo.transform).c_str()); const Rect& frame = hwcInfo.displayFrame; result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom); const FloatRect& crop = hwcInfo.sourceCrop; result.appendFormat("%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right, crop.bottom); - result.append("- - - - - - - - - - - - - - - - - - - - "); - result.append("- - - - - - - - - - - - - - - - - - - -\n"); + result.append("- - - - - - - - - - - - - - - -\n"); + + std::string compositionInfoStr; + getBE().compositionInfo.dump(compositionInfoStr, "compositionInfo"); + result.append(compositionInfoStr.c_str()); + + result.append("- - - - - - - - - - - - - - - -"); + result.append("- - - - - - - - - - - - - - - -"); + result.append("- - - - - - - - - - - - - - -\n"); } void Layer::dumpFrameStats(String8& result) const { @@ -1539,13 +1470,13 @@ void Layer::dumpFrameEvents(String8& result) { void Layer::onDisconnect() { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.onDisconnect(); - mTimeStats.onDisconnect(getName().c_str()); + mTimeStats.onDisconnect(getSequence()); } void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { if (newTimestamps) { - mTimeStats.setPostTime(getName().c_str(), newTimestamps->frameNumber, + mTimeStats.setPostTime(getSequence(), newTimestamps->frameNumber, getName().c_str(), newTimestamps->postedTime); } @@ -1657,6 +1588,28 @@ bool Layer::detachChildren() { return true; } +bool Layer::setColorTransform(const mat4& matrix) { + static const mat4 identityMatrix = mat4(); + + if (mCurrentState.colorTransform == matrix) { + return false; + } + ++mCurrentState.sequence; + mCurrentState.colorTransform = matrix; + mCurrentState.hasColorTransform = matrix != identityMatrix; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +const mat4& Layer::getColorTransform() const { + return getDrawingState().colorTransform; +} + +bool Layer::hasColorTransform() const { + return getDrawingState().hasColorTransform; +} + bool Layer::isLegacyDataSpace() const { // return true when no higher bits are set return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK | @@ -1867,8 +1820,8 @@ void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet, traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); } -Transform Layer::getTransform() const { - Transform t; +ui::Transform Layer::getTransform() const { + ui::Transform t; const auto& p = mDrawingParent.promote(); if (p != nullptr) { t = p->getTransform(); @@ -1888,14 +1841,14 @@ Transform Layer::getTransform() const { bufferHeight = p->getBE().compositionInfo.mBuffer->getWidth(); bufferWidth = p->getBE().compositionInfo.mBuffer->getHeight(); } - float sx = p->getDrawingState().active.w / static_cast<float>(bufferWidth); - float sy = p->getDrawingState().active.h / static_cast<float>(bufferHeight); - Transform extraParentScaling; + float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth); + float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight); + ui::Transform extraParentScaling; extraParentScaling.set(sx, 0, 0, sy); t = t * extraParentScaling; } } - return t * getDrawingState().active.transform; + return t * getActiveTransform(getDrawingState()); } half Layer::getAlpha() const { @@ -1924,8 +1877,8 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; const State& state = useDrawing ? mDrawingState : mCurrentState; - Transform requestedTransform = state.active.transform; - Transform transform = getTransform(); + ui::Transform requestedTransform = state.active_legacy.transform; + ui::Transform transform = getTransform(); layerInfo->set_id(sequence); layerInfo->set_name(getName().c_str()); @@ -1942,7 +1895,7 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) } } - LayerProtoHelper::writeToProto(state.activeTransparentRegion, + LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, layerInfo->mutable_transparent_region()); LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region()); LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region()); @@ -1959,11 +1912,10 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) requestedPosition->set_y(requestedTransform.ty()); SizeProto* size = layerInfo->mutable_size(); - size->set_w(state.active.w); - size->set_h(state.active.h); + size->set_w(state.active_legacy.w); + size->set_h(state.active_legacy.h); - LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop()); - LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop()); + LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop()); layerInfo->set_is_opaque(isOpaque(state)); layerInfo->set_invalidate(contentDirty); @@ -1993,6 +1945,8 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) auto buffer = getBE().compositionInfo.mBuffer; if (buffer != nullptr) { LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer()); + LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), + layerInfo->mutable_buffer_transform()); } layerInfo->set_queued_frames(getQueuedFrameCount()); @@ -2002,22 +1956,23 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) layerInfo->set_curr_frame(mCurrentFrameNumber); for (const auto& pendingState : mPendingStates) { - auto barrierLayer = pendingState.barrierLayer.promote(); + auto barrierLayer = pendingState.barrierLayer_legacy.promote(); if (barrierLayer != nullptr) { BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer(); barrierLayerProto->set_id(barrierLayer->sequence); - barrierLayerProto->set_frame_number(pendingState.frameNumber); + barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy); } } } -void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) { - if (!hasHwcLayer(hwcId)) { +void Layer::writeToProto(LayerProto* layerInfo, int32_t displayId) { + if (!hasHwcLayer(displayId)) { return; } + writeToProto(layerInfo, LayerVector::StateSet::Drawing); - const auto& hwcInfo = getBE().mHwcLayers.at(hwcId); + const auto& hwcInfo = getBE().mHwcLayers.at(displayId); const Rect& frame = hwcInfo.displayFrame; LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame()); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 22396796ed..5d05f0530b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -19,40 +19,38 @@ #include <sys/types.h> -#include <utils/RefBase.h> -#include <utils/String8.h> -#include <utils/Timers.h> - +#include <gui/BufferQueue.h> +#include <gui/ISurfaceComposerClient.h> +#include <gui/LayerState.h> +#include <layerproto/LayerProtoHeader.h> +#include <math/vec4.h> +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> #include <ui/FloatRect.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> #include <ui/Region.h> +#include <ui/Transform.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/Timers.h> -#include <gui/ISurfaceComposerClient.h> -#include <gui/LayerState.h> -#include <gui/BufferQueue.h> - -#include <list> #include <cstdint> +#include <list> +#include <vector> #include "Client.h" #include "FrameTracker.h" +#include "LayerBE.h" #include "LayerVector.h" #include "MonitoredProducer.h" #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" -#include "Transform.h" -#include <layerproto/LayerProtoHeader.h> #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/HWComposerBufferCache.h" #include "RenderArea.h" -#include "RenderEngine/Mesh.h" -#include "RenderEngine/Texture.h" - -#include <math/vec4.h> -#include <vector> using namespace android::surfaceflinger; @@ -74,77 +72,27 @@ class SurfaceInterceptor; // --------------------------------------------------------------------------- -struct CompositionInfo { - HWC2::Composition compositionType; - sp<GraphicBuffer> mBuffer = nullptr; - int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT; - struct { - HWComposer* hwc; - sp<Fence> fence; - HWC2::BlendMode blendMode; - Rect displayFrame; - float alpha; - FloatRect sourceCrop; - HWC2::Transform transform; - int z; - int type; - int appId; - Region visibleRegion; - Region surfaceDamage; - sp<NativeHandle> sidebandStream; - android_dataspace dataspace; - hwc_color_t color; - } hwc; - struct { - RE::RenderEngine* renderEngine; - Mesh* mesh; - } renderEngine; -}; - -class LayerBE { -public: - LayerBE(); - - // The mesh used to draw the layer in GLES composition mode - Mesh mMesh; - - // HWC items, accessed from the main thread - struct HWCInfo { - HWCInfo() - : hwc(nullptr), - layer(nullptr), - forceClientComposition(false), - compositionType(HWC2::Composition::Invalid), - clearClientTarget(false), - transform(HWC2::Transform::None) {} - - HWComposer* hwc; - HWC2::Layer* layer; - bool forceClientComposition; - HWC2::Composition compositionType; - bool clearClientTarget; - Rect displayFrame; - FloatRect sourceCrop; - HWComposerBufferCache bufferCache; - HWC2::Transform transform; - }; - - // A layer can be attached to multiple displays when operating in mirror mode - // (a.k.a: when several displays are attached with equal layerStack). In this - // case we need to keep track. In non-mirror mode, a layer will have only one - // HWCInfo. This map key is a display layerStack. - std::unordered_map<int32_t, HWCInfo> mHwcLayers; - - CompositionInfo compositionInfo; +struct LayerCreationArgs { + LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags) + : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {} + + SurfaceFlinger* flinger; + const sp<Client>& client; + const String8& name; + uint32_t w; + uint32_t h; + uint32_t flags; }; class Layer : public virtual RefBase { - static int32_t sSequence; + static std::atomic<int32_t> sSequence; public: + friend class LayerBE; LayerBE& getBE() { return mBE; } LayerBE& getBE() const { return mBE; } - mutable bool contentDirty; + mutable bool contentDirty{false}; // regions below are in window-manager space Region visibleRegion; Region coveredRegion; @@ -154,7 +102,7 @@ public: // 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; + int32_t sequence{sSequence++}; enum { // flags for doTransaction() eDontUpdateGeometryState = 0x00000001, @@ -164,7 +112,7 @@ public: struct Geometry { uint32_t w; uint32_t h; - Transform transform; + ui::Transform transform; inline bool operator==(const Geometry& rhs) const { return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) && @@ -174,8 +122,8 @@ public: }; struct State { - Geometry active; - Geometry requested; + Geometry active_legacy; + Geometry requested_legacy; int32_t z; // The identifier of the layer stack this layer belongs to. A layer can @@ -191,23 +139,19 @@ public: bool modified; // Crop is expressed in layer space coordinate. - Rect crop; - Rect requestedCrop; - - // finalCrop is expressed in display space coordinate. - Rect finalCrop; - Rect requestedFinalCrop; + Rect crop_legacy; + Rect requestedCrop_legacy; // If set, defers this state update until the identified Layer // receives a frame with the given frameNumber - wp<Layer> barrierLayer; - uint64_t frameNumber; + wp<Layer> barrierLayer_legacy; + uint64_t frameNumber_legacy; // the transparentRegion hint is a bit special, it's latched only // when we receive a buffer -- this is because it's "content" // dependent. - Region activeTransparentRegion; - Region requestedTransparentRegion; + Region activeTransparentRegion_legacy; + Region requestedTransparentRegion_legacy; int32_t appId; int32_t type; @@ -219,10 +163,29 @@ public: SortedVector<wp<Layer>> zOrderRelatives; half4 color; + + // The fields below this point are only used by BufferStateLayer + Geometry active; + + uint32_t transform; + bool transformToDisplayInverse; + + Rect crop; + Region transparentRegionHint; + + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + + sp<NativeHandle> sidebandStream; + mat4 colorTransform; + bool hasColorTransform; }; - Layer(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags); + explicit Layer(const LayerCreationArgs& args); virtual ~Layer(); void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; } @@ -254,11 +217,12 @@ public: // also the rendered size of the layer prior to any transformations. Parent // or local matrix transformations will not affect the size of the buffer, // but may affect it's on-screen size or clipping. - bool setSize(uint32_t w, uint32_t h); + virtual bool setSize(uint32_t w, uint32_t h); // Set a 2x2 transformation matrix on the layer. This transform // will be applied after parent transforms, but before any final // producer specified transform. - bool setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms); + virtual bool setMatrix(const layer_state_t::matrix22_t& matrix, + bool allowNonRectPreservingTransforms); // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be @@ -268,32 +232,46 @@ public: // setPosition operates in parent buffer space (pre parent-transform) or display // space for top-level layers. - bool setPosition(float x, float y, bool immediate); + virtual bool setPosition(float x, float y, bool immediate); // Buffer space - bool setCrop(const Rect& crop, bool immediate); - // Parent buffer space/display space - bool setFinalCrop(const Rect& crop, bool immediate); + virtual bool setCrop_legacy(const Rect& crop, bool immediate); // TODO(b/38182121): Could we eliminate the various latching modes by // using the layer hierarchy? // ----------------------------------------------------------------------- - bool setLayer(int32_t z); - bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ); - - bool setAlpha(float alpha); - bool setColor(const half3& color); - bool setTransparentRegionHint(const Region& transparent); - bool setFlags(uint8_t flags, uint8_t mask); - bool setLayerStack(uint32_t layerStack); - uint32_t getLayerStack() const; - void deferTransactionUntil(const sp<IBinder>& barrierHandle, uint64_t frameNumber); - void deferTransactionUntil(const sp<Layer>& barrierLayer, uint64_t frameNumber); - bool setOverrideScalingMode(int32_t overrideScalingMode); - void setInfo(int32_t type, int32_t appId); - bool reparentChildren(const sp<IBinder>& layer); - void setChildrenDrawingParent(const sp<Layer>& layer); - bool reparent(const sp<IBinder>& newParentHandle); - bool detachChildren(); + virtual bool setLayer(int32_t z); + virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ); + + virtual bool setAlpha(float alpha); + virtual bool setColor(const half3& color); + virtual bool setTransparentRegionHint(const Region& transparent); + virtual bool setFlags(uint8_t flags, uint8_t mask); + virtual bool setLayerStack(uint32_t layerStack); + virtual uint32_t getLayerStack() const; + virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, + uint64_t frameNumber); + virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber); + virtual bool setOverrideScalingMode(int32_t overrideScalingMode); + virtual void setInfo(int32_t type, int32_t appId); + virtual bool reparentChildren(const sp<IBinder>& layer); + virtual void setChildrenDrawingParent(const sp<Layer>& layer); + virtual bool reparent(const sp<IBinder>& newParentHandle); + virtual bool detachChildren(); + virtual bool setColorTransform(const mat4& matrix); + virtual const mat4& getColorTransform() const; + virtual bool hasColorTransform() const; + + // Used only to set BufferStateLayer state + virtual bool setTransform(uint32_t /*transform*/) { return false; }; + virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; }; + virtual bool setCrop(const Rect& /*crop*/) { return false; }; + virtual bool setBuffer(sp<GraphicBuffer> /*buffer*/) { 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; }; + virtual bool setApi(int32_t /*api*/) { return false; }; + virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; }; ui::Dataspace getDataSpace() const { return mCurrentDataSpace; } @@ -310,6 +288,7 @@ public: virtual void useSurfaceDamage() {} virtual void useEmptyDamage() {} + uint32_t getTransactionFlags() const { return mTransactionFlags; } uint32_t getTransactionFlags(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags); @@ -317,7 +296,8 @@ public: return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay); } - void computeGeometry(const RenderArea& renderArea, Mesh& mesh, bool useIdentityTransform) const; + void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh, + bool useIdentityTransform) const; FloatRect computeBounds(const Region& activeTransparentRegion) const; FloatRect computeBounds() const; @@ -371,32 +351,43 @@ public: void writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing); - void writeToProto(LayerProto* layerInfo, int32_t hwcId); + void writeToProto(LayerProto* layerInfo, int32_t displayId); + + virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; } + virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; } + virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; } + virtual ui::Transform getActiveTransform(const Layer::State& s) const { + return s.active_legacy.transform; + } + virtual Region getActiveTransparentRegion(const Layer::State& s) const { + return s.activeTransparentRegion_legacy; + } + virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } protected: /* * onDraw - draws the surface. */ virtual void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const = 0; + bool useIdentityTransform) = 0; public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } - void setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z); - void forceClientComposition(int32_t hwcId); - bool getForceClientComposition(int32_t hwcId); - virtual void setPerFrameData(const sp<const DisplayDevice>& displayDevice) = 0; + void setGeometry(const sp<const DisplayDevice>& display, uint32_t z); + void forceClientComposition(int32_t displayId); + bool getForceClientComposition(int32_t displayId); + virtual void setPerFrameData(const sp<const DisplayDevice>& display) = 0; // callIntoHwc exists so we can update our local state and call // acceptDisplayChanges without unnecessarily updating the device's state - void setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc = true); - HWC2::Composition getCompositionType(int32_t hwcId) const; - void setClearClientTarget(int32_t hwcId, bool clear); - bool getClearClientTarget(int32_t hwcId) const; - void updateCursorPosition(const sp<const DisplayDevice>& hw); + void setCompositionType(int32_t displayId, HWC2::Composition type, bool callIntoHwc = true); + HWC2::Composition getCompositionType(int32_t displayId) const; + void setClearClientTarget(int32_t displayId, bool clear); + bool getClearClientTarget(int32_t displayId) const; + void updateCursorPosition(const sp<const DisplayDevice>& display); /* * called after page-flip @@ -405,7 +396,7 @@ public: virtual void abandon() {} - virtual bool shouldPresentNow(const DispSync& /*dispSync*/) const { return false; } + virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } virtual void setTransformHint(uint32_t /*orientation*/) const { } /* @@ -432,9 +423,8 @@ public: * draw - performs some global clipping optimizations * and calls onDraw(). */ - void draw(const RenderArea& renderArea, const Region& clip) const; - void draw(const RenderArea& renderArea, bool useIdentityTransform) const; - void draw(const RenderArea& renderArea) const; + void draw(const RenderArea& renderArea, const Region& clip); + void draw(const RenderArea& renderArea, bool useIdentityTransform); /* * doTransaction - process the transaction. This is a good place to figure @@ -472,13 +462,13 @@ public: * operation, so this should be set only if needed). Typically this is used * to figure out if the content or size of a surface has changed. */ - virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) { + virtual Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, + const sp<Fence>& /*releaseFence*/) { return {}; } virtual bool isBufferLatched() const { return false; } - bool isPotentialCursor() const { return mPotentialCursor; } /* * called with the state lock from a binder thread when the layer is * removed from the current list to the pending removal list @@ -493,7 +483,7 @@ public: // Updates the transform hint in our SurfaceFlingerConsumer to match // the current orientation of the display device. - void updateTransformHint(const sp<const DisplayDevice>& hw) const; + void updateTransformHint(const sp<const DisplayDevice>& display) const; /* * returns the rectangle that crops the content of the layer and scales it @@ -502,37 +492,37 @@ public: Rect getContentCrop() const; /* - * Returns if a frame is queued. + * Returns if a frame is ready */ - bool hasQueuedFrame() const { - return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; - } + virtual bool hasReadyFrame() const { return false; } - int32_t getQueuedFrameCount() const { return mQueuedFrames; } + virtual int32_t getQueuedFrameCount() const { return 0; } // ----------------------------------------------------------------------- - bool createHwcLayer(HWComposer* hwc, int32_t hwcId); - bool destroyHwcLayer(int32_t hwcId); + bool createHwcLayer(HWComposer* hwc, int32_t displayId); + bool destroyHwcLayer(int32_t displayId); void destroyAllHwcLayers(); - bool hasHwcLayer(int32_t hwcId) { - return getBE().mHwcLayers.count(hwcId) > 0; + bool hasHwcLayer(int32_t displayId) { return getBE().mHwcLayers.count(displayId) > 0; } + + HWC2::Layer* getHwcLayer(int32_t displayId) { + if (getBE().mHwcLayers.count(displayId) == 0) { + return nullptr; + } + return getBE().mHwcLayers[displayId].layer.get(); } - HWC2::Layer* getHwcLayer(int32_t hwcId) { + bool setHwcLayer(int32_t hwcId) { if (getBE().mHwcLayers.count(hwcId) == 0) { - return nullptr; + return false; } - return getBE().mHwcLayers[hwcId].layer; + getBE().compositionInfo.hwc.hwcLayer = getBE().mHwcLayers[hwcId].layer; + return true; } // ----------------------------------------------------------------------- - void clearWithOpenGL(const RenderArea& renderArea) const; - void setFiltering(bool filtering); - bool getFiltering() const; - inline const State& getDrawingState() const { return mDrawingState; } inline const State& getCurrentState() const { return mCurrentState; } @@ -542,7 +532,7 @@ public: /* always call base class first */ static void miniDumpHeader(String8& result); - void miniDump(String8& result, int32_t hwcId) const; + void miniDump(String8& result, int32_t displayId) const; void dumpFrameStats(String8& result) const; void dumpFrameEvents(String8& result); void clearFrameStats(); @@ -559,7 +549,7 @@ public: virtual bool getTransformToDisplayInverse() const { return false; } - Transform getTransform() const; + ui::Transform getTransform() const; // Returns the Alpha of the Surface, accounting for the Alpha // of parent Surfaces in the hierarchy (alpha's will be multiplied @@ -594,7 +584,7 @@ public: // SurfaceFlinger to complete a transaction. void commitChildList(); int32_t getZ() const; - void pushPendingState(); + virtual void pushPendingState(); protected: // constant @@ -618,20 +608,21 @@ protected: : mFlinger(flinger), mLayer(layer) {} }; - virtual void onFirstRef(); - friend class impl::SurfaceInterceptor; + // For unit tests + friend class TestableSurfaceFlinger; + void commitTransaction(const State& stateToCommit); uint32_t getEffectiveUsage(uint32_t usage) const; - FloatRect computeCrop(const sp<const DisplayDevice>& hw) const; + virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const; // Compute the initial crop as specified by parent layers and the // SurfaceControl for this layer. Does not include buffer crop from the // IGraphicBufferProducer client, as that should not affect child clipping. // Returns in screen space. - Rect computeInitialCrop(const sp<const DisplayDevice>& hw) const; + Rect computeInitialCrop(const sp<const DisplayDevice>& display) const; // drawing void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b, @@ -678,7 +669,8 @@ protected: bool addSyncPoint(const std::shared_ptr<SyncPoint>& point); void popPendingState(State* stateToCommit); - bool applyPendingStates(State* stateToCommit); + virtual bool applyPendingStates(State* stateToCommit); + virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit); void clearSyncPoints(); @@ -714,7 +706,7 @@ protected: // ----------------------------------------------------------------------- bool usingRelativeZ(LayerVector::StateSet stateSet); - bool mPremultipliedAlpha; + bool mPremultipliedAlpha{true}; String8 mName; String8 mTransactionName; // A cached version of "TX - " + mName for systraces @@ -723,16 +715,12 @@ protected: // these are protected by an external lock State mCurrentState; State mDrawingState; - volatile int32_t mTransactionFlags; + std::atomic<uint32_t> mTransactionFlags{0}; // Accessed from main thread and binder threads Mutex mPendingStateMutex; Vector<State> mPendingStates; - // thread-safe - volatile int32_t mQueuedFrames; - volatile int32_t mSidebandStreamChanged; // used like an atomic boolean - // Timestamp history for UIAutomation. Thread safe. FrameTracker mFrameTracker; @@ -746,26 +734,21 @@ protected: TimeStats& mTimeStats = TimeStats::getInstance(); // main thread - int mActiveBufferSlot; sp<GraphicBuffer> mActiveBuffer; - sp<NativeHandle> mSidebandStream; ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN; Rect mCurrentCrop; - uint32_t mCurrentTransform; + uint32_t mCurrentTransform{0}; // We encode unset as -1. - int32_t mOverrideScalingMode; - bool mCurrentOpacity; - std::atomic<uint64_t> mCurrentFrameNumber; - bool mFrameLatencyNeeded; - // Whether filtering is forced on or not - bool mFiltering; + int32_t mOverrideScalingMode{-1}; + std::atomic<uint64_t> mCurrentFrameNumber{0}; + bool mFrameLatencyNeeded{false}; // Whether filtering is needed b/c of the drawingstate - bool mNeedsFiltering; + bool mNeedsFiltering{false}; - bool mPendingRemoval = false; + bool mPendingRemoval{false}; // page-flip thread (currently main thread) - bool mProtectedByApp; // application requires protected path to external sink + bool mProtectedByApp{false}; // application requires protected path to external sink // protected by mLock mutable Mutex mLock; @@ -773,20 +756,14 @@ protected: const wp<Client> mClientRef; // This layer can be a cursor on some displays. - bool mPotentialCursor; + bool mPotentialCursor{false}; - // Local copy of the queued contents of the incoming BufferQueue - mutable Mutex mQueueItemLock; - Condition mQueueItemCondition; - Vector<BufferItem> mQueueItems; - std::atomic<uint64_t> mLastFrameNumberReceived; - bool mAutoRefresh; - bool mFreezeGeometryUpdates; + bool mFreezeGeometryUpdates{false}; // Child list about to be committed/used for editing. - LayerVector mCurrentChildren; + LayerVector mCurrentChildren{LayerVector::StateSet::Current}; // Child list used for rendering. - LayerVector mDrawingChildren; + LayerVector mDrawingChildren{LayerVector::StateSet::Drawing}; wp<Layer> mCurrentParent; wp<Layer> mDrawingParent; diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp new file mode 100644 index 0000000000..70b00dd22a --- /dev/null +++ b/services/surfaceflinger/LayerBE.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 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. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "LayerBE" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "Layer.h" + +#include <android-base/stringprintf.h> +#include <renderengine/RenderEngine.h> + +#include <string> + +namespace { + +const char* getCompositionName(HWC2::Composition compositionType) { + switch (compositionType) { + case HWC2::Composition::Invalid: + return "Invalid"; + case HWC2::Composition::Client: + return "Client"; + case HWC2::Composition::Device: + return "Device"; + case HWC2::Composition::SolidColor: + return "Solid Color"; + case HWC2::Composition::Cursor: + return "Cursor"; + case HWC2::Composition::Sideband: + return "Sideband"; + } + return "Invalid"; +} + +} // namespace anonymous + +namespace android { + +LayerBE::LayerBE(Layer* layer, std::string layerName) + : mLayer(layer), + mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) { + compositionInfo.layer = std::make_shared<LayerBE>(*this); + compositionInfo.layerName = layerName; +} + +LayerBE::LayerBE(const LayerBE& layer) + : mLayer(layer.mLayer), + mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) { + compositionInfo.layer = layer.compositionInfo.layer; + compositionInfo.layerName = layer.mLayer->getName().string(); +} + +void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) { + mLayer->onLayerDisplayed(releaseFence); +} + +void LayerBE::clear(renderengine::RenderEngine& engine) { + engine.setupFillWithColor(0, 0, 0, 0); + engine.drawMesh(mMesh); +} + +void CompositionInfo::dump(const char* tag) const +{ + std::string logString; + dump(logString, tag); + ALOGV("%s", logString.c_str()); +} + +void CompositionInfo::dumpHwc(std::string& result, const char* tag) const { + if (tag == nullptr) { + result += base::StringPrintf("HWC parameters\n"); + } else { + result += base::StringPrintf("[%s]HWC parameters\n", tag); + } + + result += base::StringPrintf("\thwcLayer=%p\n", static_cast<HWC2::Layer*>(&*hwc.hwcLayer)); + result += base::StringPrintf("\tfence=%p\n", hwc.fence.get()); + result += base::StringPrintf("\tblendMode=%d\n", hwc.blendMode); + result += base::StringPrintf("\ttransform=%d\n", hwc.transform); + result += base::StringPrintf("\tz=%d\n", hwc.z); + result += base::StringPrintf("\ttype=%d\n", hwc.type); + result += base::StringPrintf("\tappId=%d\n", hwc.appId); + result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left, + hwc.displayFrame.top, hwc.displayFrame.right, + hwc.displayFrame.bottom); + result += base::StringPrintf("\talpha=%.3f", hwc.alpha); + result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left, + hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom); + + { + // + // Keep a conversion from std::string to String8 and back until Region can use std::string + // + String8 regionString; + hwc.visibleRegion.dump(regionString, "visibleRegion"); + hwc.surfaceDamage.dump(regionString, "surfaceDamage"); + result += regionString.string(); + } + + result += base::StringPrintf("\tcolor transform matrix:\n" + "\t\t[%f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f]\n", + hwc.colorTransform[0][0], hwc.colorTransform[1][0], + hwc.colorTransform[2][0], hwc.colorTransform[3][0], + hwc.colorTransform[0][1], hwc.colorTransform[1][1], + hwc.colorTransform[2][1], hwc.colorTransform[3][1], + hwc.colorTransform[0][2], hwc.colorTransform[1][2], + hwc.colorTransform[2][2], hwc.colorTransform[3][2], + hwc.colorTransform[0][3], hwc.colorTransform[1][3], + hwc.colorTransform[2][3], hwc.colorTransform[3][3]); +} + +void CompositionInfo::dumpRe(std::string& result, const char* tag) const { + if (tag == nullptr) { + result += base::StringPrintf("RenderEngine parameters:\n"); + } else { + result += base::StringPrintf("[%s]RenderEngine parameters:\n", tag); + } + + result += base::StringPrintf("\tblackoutLayer=%d\n", re.blackoutLayer); + result += base::StringPrintf("\tclearArea=%d\n", re.clearArea); + result += base::StringPrintf("\tpreMultipliedAlpha=%d\n", re.preMultipliedAlpha); + result += base::StringPrintf("\topaque=%d\n", re.opaque); + result += base::StringPrintf("\tdisableTexture=%d\n", re.disableTexture); + result += base::StringPrintf("\tuseIdentityTransform=%d\n", re.useIdentityTransform); +} + +void CompositionInfo::dump(std::string& result, const char* tag) const +{ + if (tag == nullptr) { + result += base::StringPrintf("CompositionInfo\n"); + } else { + result += base::StringPrintf("[%s]CompositionInfo\n", tag); + } + result += base::StringPrintf("\tLayerName: %s\n", layerName.c_str()); + result += base::StringPrintf("\tCompositionType: %s\n", + getCompositionName(compositionType)); + result += base::StringPrintf("\tmBuffer = %p\n", mBuffer.get()); + result += base::StringPrintf("\tmBufferSlot=%d\n", mBufferSlot); + result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left, + hwc.displayFrame.top, hwc.displayFrame.right, + hwc.displayFrame.bottom); + result += base::StringPrintf("\talpha=%f\n", hwc.alpha); + result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left, + hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom); + + switch (compositionType) { + case HWC2::Composition::Device: + dumpHwc(result, tag); + break; + case HWC2::Composition::Client: + dumpRe(result, tag); + break; + default: + break; + } +} + +}; // namespace android diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h new file mode 100644 index 0000000000..463c46c285 --- /dev/null +++ b/services/surfaceflinger/LayerBE.h @@ -0,0 +1,137 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#include <renderengine/Mesh.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/Texture.h> +#include <ui/Region.h> + +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/HWComposerBufferCache.h" +#include "SurfaceFlinger.h" + +namespace android { + +class LayerBE; + +struct CompositionInfo { + std::string layerName; + HWC2::Composition compositionType; + bool firstClear = false; + sp<GraphicBuffer> mBuffer = nullptr; + int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT; + std::shared_ptr<LayerBE> layer; + struct { + std::shared_ptr<HWC2::Layer> hwcLayer; + int32_t displayId = -1; + sp<Fence> fence; + HWC2::BlendMode blendMode = HWC2::BlendMode::Invalid; + Rect displayFrame; + float alpha; + FloatRect sourceCrop; + HWC2::Transform transform = HWC2::Transform::None; + int z; + int type; + int appId; + Region visibleRegion; + Region surfaceDamage; + sp<NativeHandle> sidebandStream; + ui::Dataspace dataspace; + hwc_color_t color; + bool clearClientTarget = false; + bool supportedPerFrameMetadata = false; + HdrMetadata hdrMetadata; + mat4 colorTransform; + } hwc; + struct { + bool blackoutLayer = false; + bool clearArea = false; + bool preMultipliedAlpha = false; + bool opaque = false; + bool disableTexture = false; + half4 color; + bool useIdentityTransform = false; + bool Y410BT2020 = false; + } re; + + void dump(const char* tag) const; + void dump(std::string& result, const char* tag = nullptr) const; + void dumpHwc(std::string& result, const char* tag = nullptr) const; + void dumpRe(std::string& result, const char* tag = nullptr) const; +}; + +class LayerBE { +public: + friend class Layer; + friend class BufferLayer; + friend class BufferQueueLayer; + friend class BufferStateLayer; + friend class ColorLayer; + friend class SurfaceFlinger; + + // For unit tests + friend class TestableSurfaceFlinger; + + LayerBE(Layer* layer, std::string layerName); + explicit LayerBE(const LayerBE& layer); + + void onLayerDisplayed(const sp<Fence>& releaseFence); + void clear(renderengine::RenderEngine& renderEngine); + renderengine::Mesh& getMesh() { return mMesh; } + + Layer*const mLayer; +private: + // The mesh used to draw the layer in GLES composition mode + renderengine::Mesh mMesh; + + // HWC items, accessed from the main thread + struct HWCInfo { + HWCInfo() + : hwc(nullptr), + layer(nullptr), + forceClientComposition(false), + compositionType(HWC2::Composition::Invalid), + clearClientTarget(false), + transform(HWC2::Transform::None) {} + + HWComposer* hwc; + std::shared_ptr<HWC2::Layer> layer; + bool forceClientComposition; + HWC2::Composition compositionType; + bool clearClientTarget; + Rect displayFrame; + FloatRect sourceCrop; + HWComposerBufferCache bufferCache; + HWC2::Transform transform; + }; + + + // A layer can be attached to multiple displays when operating in mirror mode + // (a.k.a: when several displays are attached with equal layerStack). In this + // case we need to keep track. In non-mirror mode, a layer will have only one + // HWCInfo. This map key is a display layerStack. + std::unordered_map<int32_t, HWCInfo> mHwcLayers; + + CompositionInfo compositionInfo; +}; + +}; // namespace android + diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index cc3955087a..3289e8f583 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -51,7 +51,8 @@ void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) { colorProto->set_a(color.a); } -void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) { +void LayerProtoHelper::writeToProto(const ui::Transform& transform, + TransformProto* transformProto) { transformProto->set_dsdx(transform[0][0]); transformProto->set_dtdx(transform[0][1]); transformProto->set_dsdy(transform[1][0]); diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 860da63ed2..6df5aeaebf 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -16,13 +16,11 @@ #include <layerproto/LayerProtoHeader.h> +#include <math/vec4.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <ui/Region.h> - -#include <Transform.h> - -#include <math/vec4.h> +#include <ui/Transform.h> namespace android { namespace surfaceflinger { @@ -32,7 +30,7 @@ public: static void writeToProto(const FloatRect& rect, FloatRectProto* rectProto); static void writeToProto(const Region& region, RegionProto* regionProto); static void writeToProto(const half4 color, ColorProto* colorProto); - static void writeToProto(const Transform& transform, TransformProto* transformProto); + static void writeToProto(const ui::Transform& transform, TransformProto* transformProto); static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto); }; diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp index a5f0b9878c..72abea8b2d 100644 --- a/services/surfaceflinger/LayerRejecter.cpp +++ b/services/surfaceflinger/LayerRejecter.cpp @@ -19,8 +19,6 @@ #include <gui/BufferItem.h> #include <system/window.h> -#include "clz.h" - #define DEBUG_RESIZE 0 namespace android { @@ -31,6 +29,7 @@ LayerRejecter::LayerRejecter(Layer::State& front, bool stickySet, const char* name, int32_t overrideScalingMode, + bool transformToDisplayInverse, bool& freezePositionUpdates) : mFront(front), mCurrent(current), @@ -38,6 +37,7 @@ LayerRejecter::LayerRejecter(Layer::State& front, mStickyTransformSet(stickySet), mName(name), mOverrideScalingMode(overrideScalingMode), + mTransformToDisplayInverse(transformToDisplayInverse), mFreezeGeometryUpdates(freezePositionUpdates) {} bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) { @@ -50,26 +50,34 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) // check that we received a buffer of the right size // (Take the buffer's orientation into account) - if (item.mTransform & Transform::ROT_90) { - swap(bufWidth, bufHeight); + if (item.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + if (mTransformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } } int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode; bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE; - if (mFront.active != mFront.requested) { - if (isFixedSize || (bufWidth == mFront.requested.w && bufHeight == mFront.requested.h)) { + if (mFront.active_legacy != mFront.requested_legacy) { + if (isFixedSize || + (bufWidth == mFront.requested_legacy.w && bufHeight == mFront.requested_legacy.h)) { // Here we pretend the transaction happened by updating the // current and drawing states. Drawing state is only accessed // in this thread, no need to have it locked - mFront.active = mFront.requested; + mFront.active_legacy = mFront.requested_legacy; // We also need to update the current state so that // we don't end-up overwriting the drawing state with // this stale current state during the next transaction // // NOTE: We don't need to hold the transaction lock here - // because State::active is only accessed from this thread. - mCurrent.active = mFront.active; + // because State::active_legacy is only accessed from this thread. + mCurrent.active_legacy = mFront.active_legacy; mCurrent.modified = true; // recompute visible region @@ -77,35 +85,32 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) mFreezeGeometryUpdates = false; - if (mFront.crop != mFront.requestedCrop) { - mFront.crop = mFront.requestedCrop; - mCurrent.crop = mFront.requestedCrop; - mRecomputeVisibleRegions = true; - } - if (mFront.finalCrop != mFront.requestedFinalCrop) { - mFront.finalCrop = mFront.requestedFinalCrop; - mCurrent.finalCrop = mFront.requestedFinalCrop; + if (mFront.crop_legacy != mFront.requestedCrop_legacy) { + mFront.crop_legacy = mFront.requestedCrop_legacy; + mCurrent.crop_legacy = mFront.requestedCrop_legacy; mRecomputeVisibleRegions = true; } } ALOGD_IF(DEBUG_RESIZE, "[%s] latchBuffer/reject: buffer (%ux%u, tr=%02x), scalingMode=%d\n" - " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) " + " drawing={ active_legacy ={ wh={%4u,%4u} crop_legacy={%4d,%4d,%4d,%4d} " + "(%4d,%4d) " "}\n" - " requested={ wh={%4u,%4u} }}\n", - mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, mFront.active.w, - mFront.active.h, mFront.crop.left, mFront.crop.top, mFront.crop.right, - mFront.crop.bottom, mFront.crop.getWidth(), mFront.crop.getHeight(), - mFront.requested.w, mFront.requested.h); + " requested_legacy={ wh={%4u,%4u} }}\n", + mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode, + mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left, + mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom, + mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(), + mFront.requested_legacy.w, mFront.requested_legacy.h); } if (!isFixedSize && !mStickyTransformSet) { - if (mFront.active.w != bufWidth || mFront.active.h != bufHeight) { + if (mFront.active_legacy.w != bufWidth || mFront.active_legacy.h != bufHeight) { // reject this buffer ALOGE("[%s] rejecting buffer: " - "bufWidth=%d, bufHeight=%d, front.active.{w=%d, h=%d}", - mName, bufWidth, bufHeight, mFront.active.w, mFront.active.h); + "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}", + mName, bufWidth, bufHeight, mFront.active_legacy.w, mFront.active_legacy.h); return true; } } @@ -118,16 +123,17 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) // We latch the transparent region here, instead of above where we latch // the rest of the geometry because it is only content but not necessarily // resize dependent. - if (!mFront.activeTransparentRegion.isTriviallyEqual(mFront.requestedTransparentRegion)) { - mFront.activeTransparentRegion = mFront.requestedTransparentRegion; + if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual( + mFront.requestedTransparentRegion_legacy)) { + mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy; // We also need to update the current state so that // we don't end-up overwriting the drawing state with // this stale current state during the next transaction // // NOTE: We don't need to hold the transaction lock here - // because State::active is only accessed from this thread. - mCurrent.activeTransparentRegion = mFront.activeTransparentRegion; + // because State::active_legacy is only accessed from this thread. + mCurrent.activeTransparentRegion_legacy = mFront.activeTransparentRegion_legacy; // recompute visible region mRecomputeVisibleRegions = true; diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h index 40972aac87..63d51de0ca 100644 --- a/services/surfaceflinger/LayerRejecter.h +++ b/services/surfaceflinger/LayerRejecter.h @@ -29,6 +29,7 @@ namespace android { bool stickySet, const char *name, int32_t overrideScalingMode, + bool transformToDisplayInverse, bool &freezePositionUpdates); virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item); @@ -40,6 +41,7 @@ namespace android { bool mStickyTransformSet; const char *mName; int32_t mOverrideScalingMode; + bool mTransformToDisplayInverse; bool &mFreezeGeometryUpdates; }; } // namespace android diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp index 2a679552eb..c0174aec60 100644 --- a/services/surfaceflinger/LayerStats.cpp +++ b/services/surfaceflinger/LayerStats.cpp @@ -57,7 +57,7 @@ bool LayerStats::isEnabled() { } void LayerStats::traverseLayerTreeStatsLocked( - const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree, + const std::vector<LayerProtoParser::Layer*>& layerTree, const LayerProtoParser::LayerGlobal& layerGlobal, std::vector<std::string>* const outLayerShapeVec) { for (const auto& layer : layerTree) { @@ -82,7 +82,7 @@ void LayerStats::traverseLayerTreeStatsLocked( base::StringAppendF(&key, ",%s", destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top, layerGlobal.resolution[1], false)); - base::StringAppendF(&key, ",%s", scaleRatioWH(layer.get()).c_str()); + base::StringAppendF(&key, ",%s", scaleRatioWH(layer).c_str()); base::StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a))); outLayerShapeVec->push_back(key); @@ -98,7 +98,7 @@ void LayerStats::logLayerStats(const LayersProto& layersProto) { std::vector<std::string> layerShapeVec; std::lock_guard<std::mutex> lock(mMutex); - traverseLayerTreeStatsLocked(layerTree, layerGlobal, &layerShapeVec); + traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec); std::string layerShapeKey = base::StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()), diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h index bd17d82781..9de9cce8bf 100644 --- a/services/surfaceflinger/LayerStats.h +++ b/services/surfaceflinger/LayerStats.h @@ -38,7 +38,7 @@ public: private: // Traverse layer tree to get all visible layers' stats void traverseLayerTreeStatsLocked( - const std::vector<std::unique_ptr<LayerProtoParser::Layer>>& layerTree, + const std::vector<LayerProtoParser::Layer*>& layerTree, const LayerProtoParser::LayerGlobal& layerGlobal, std::vector<std::string>* const outLayerShapeVec); // Convert layer's top-left position into 8x8 percentage of the display diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index 389fbd23e3..06e3d9c154 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "MessageQueue.h" #include "MonitoredProducer.h" -#include "SurfaceFlinger.h" #include "Layer.h" +#include "SurfaceFlinger.h" + +#include "Scheduler/MessageQueue.h" namespace android { diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp new file mode 100644 index 0000000000..3fff9283ee --- /dev/null +++ b/services/surfaceflinger/NativeWindowSurface.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeWindowSurface.h" + +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> + +namespace android::surfaceflinger { + +NativeWindowSurface::~NativeWindowSurface() = default; + +namespace impl { + +std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) { + class NativeWindowSurface final : public surfaceflinger::NativeWindowSurface { + public: + explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer) + : mSurface(new Surface(producer, /* controlledByApp */ false)) {} + + ~NativeWindowSurface() override = default; + + sp<ANativeWindow> getNativeWindow() const override { return mSurface; } + + void preallocateBuffers() override { mSurface->allocateBuffers(); } + + private: + sp<Surface> mSurface; + }; + + return std::make_unique<NativeWindowSurface>(producer); +} + +} // namespace impl +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/NativeWindowSurface.h b/services/surfaceflinger/NativeWindowSurface.h new file mode 100644 index 0000000000..f34a45abed --- /dev/null +++ b/services/surfaceflinger/NativeWindowSurface.h @@ -0,0 +1,50 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> + +#include <utils/StrongPointer.h> + +struct ANativeWindow; + +namespace android { + +class IGraphicBufferProducer; + +namespace surfaceflinger { + +// A thin interface to abstract creating instances of Surface (gui/Surface.h) to +// use as a NativeWindow. +class NativeWindowSurface { +public: + virtual ~NativeWindowSurface(); + + // Gets the NativeWindow to use for the surface. + virtual sp<ANativeWindow> getNativeWindow() const = 0; + + // Indicates that the surface should allocate its buffers now. + virtual void preallocateBuffers() = 0; +}; + +namespace impl { + +std::unique_ptr<NativeWindowSurface> createNativeWindowSurface(const sp<IGraphicBufferProducer>&); + +} // namespace impl +} // namespace surfaceflinger +} // namespace android diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp index 1a8edf3e79..93759e8ad7 100644 --- a/services/surfaceflinger/RenderArea.cpp +++ b/services/surfaceflinger/RenderArea.cpp @@ -1,7 +1,5 @@ #include "RenderArea.h" -#include <gui/LayerState.h> - namespace android { float RenderArea::getCaptureFillValue(CaptureFill captureFill) { @@ -13,37 +11,5 @@ float RenderArea::getCaptureFillValue(CaptureFill captureFill) { return 1.0f; } } -/* - * Checks that the requested width and height are valid and updates them to the render area - * dimensions if they are set to 0 - */ -status_t RenderArea::updateDimensions(int displayRotation) { - // get screen geometry - - uint32_t width = getWidth(); - uint32_t height = getHeight(); - - if (mRotationFlags & Transform::ROT_90) { - std::swap(width, height); - } - - if (displayRotation & DisplayState::eOrientationSwapMask) { - std::swap(width, height); - } - - if ((mReqWidth > width) || (mReqHeight > height)) { - ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height); - return BAD_VALUE; - } - - if (mReqWidth == 0) { - mReqWidth = width; - } - if (mReqHeight == 0) { - mReqHeight = height; - } - - return NO_ERROR; -} } // namespace android diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 96e4b5f48b..9bad6dee04 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -1,50 +1,87 @@ #pragma once #include <ui/GraphicTypes.h> - -#include "Transform.h" +#include <ui/Transform.h> #include <functional> namespace android { +// RenderArea describes a rectangular area that layers can be rendered to. +// +// There is a logical render area and a physical render area. When a layer is +// rendered to the render area, it is first transformed and clipped to the logical +// render area. The transformed and clipped layer is then projected onto the +// physical render area. class RenderArea { - public: enum class CaptureFill {CLEAR, OPAQUE}; static float getCaptureFillValue(CaptureFill captureFill); - RenderArea(uint32_t reqHeight, uint32_t reqWidth, CaptureFill captureFill, - ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone) - : mReqHeight(reqHeight), mReqWidth(reqWidth), mCaptureFill(captureFill) { - mRotationFlags = Transform::fromRotation(rotation); - } + RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill, + ui::Dataspace reqDataSpace, + ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) + : mReqWidth(reqWidth), + mReqHeight(reqHeight), + mReqDataSpace(reqDataSpace), + mCaptureFill(captureFill), + mRotationFlags(rotation) {} virtual ~RenderArea() = default; - virtual const Transform& getTransform() const = 0; - virtual Rect getBounds() const = 0; - virtual int getHeight() const = 0; - virtual int getWidth() const = 0; + // Invoke drawLayers to render layers into the render area. + virtual void render(std::function<void()> drawLayers) { drawLayers(); } + + // Returns true if the render area is secure. A secure layer should be + // blacked out / skipped when rendered to an insecure render area. virtual bool isSecure() const = 0; + + // Returns true if the otherwise disabled layer filtering should be + // enabled when rendering to this render area. virtual bool needsFiltering() const = 0; + + // Returns the transform to be applied on layers to transform them into + // the logical render area. + virtual const ui::Transform& getTransform() const = 0; + + // Returns the size of the logical render area. Layers are clipped to the + // logical render area. + virtual int getWidth() const = 0; + virtual int getHeight() const = 0; + virtual Rect getBounds() const = 0; + + // Returns the source crop of the render area. The source crop defines + // how layers are projected from the logical render area onto the physical + // render area. It can be larger than the logical render area. It can + // also be optionally rotated. + // + // Layers are first clipped to the source crop (in addition to being + // clipped to the logical render area already). The source crop and the + // layers are then rotated around the center of the source crop, and + // scaled to the physical render area linearly. virtual Rect getSourceCrop() const = 0; - virtual void render(std::function<void()> drawLayers) { drawLayers(); } + // Returns the rotation of the source crop and the layers. + ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; }; - int getReqHeight() const { return mReqHeight; }; + // Returns the size of the physical render area. int getReqWidth() const { return mReqWidth; }; - Transform::orientation_flags getRotationFlags() const { return mRotationFlags; }; - status_t updateDimensions(int displayRotation); + int getReqHeight() const { return mReqHeight; }; + + // Returns the composition data space of the render area. + ui::Dataspace getReqDataSpace() const { return mReqDataSpace; } + // Returns the fill color of the physical render area. Regions not + // covered by any rendered layer should be filled with this color. CaptureFill getCaptureFill() const { return mCaptureFill; }; private: - uint32_t mReqHeight; - uint32_t mReqWidth; - Transform::orientation_flags mRotationFlags; - CaptureFill mCaptureFill; + const uint32_t mReqWidth; + const uint32_t mReqHeight; + const ui::Dataspace mReqDataSpace; + const CaptureFill mCaptureFill; + const ui::Transform::orientation_flags mRotationFlags; }; } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Description.cpp b/services/surfaceflinger/RenderEngine/Description.cpp deleted file mode 100644 index c218e4da50..0000000000 --- a/services/surfaceflinger/RenderEngine/Description.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <string.h> - -#include <utils/TypeHelpers.h> - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include "Description.h" - -namespace android { - -void Description::setPremultipliedAlpha(bool premultipliedAlpha) { - mPremultipliedAlpha = premultipliedAlpha; -} - -void Description::setOpaque(bool opaque) { - mOpaque = opaque; -} - -void Description::setTexture(const Texture& texture) { - mTexture = texture; - mTextureEnabled = true; -} - -void Description::disableTexture() { - mTextureEnabled = false; -} - -void Description::setColor(const half4& color) { - mColor = color; -} - -void Description::setProjectionMatrix(const mat4& mtx) { - mProjectionMatrix = mtx; -} - -void Description::setColorMatrix(const mat4& mtx) { - mColorMatrix = mtx; -} - -void Description::setInputTransformMatrix(const mat3& matrix) { - mInputTransformMatrix = matrix; -} - -void Description::setOutputTransformMatrix(const mat4& matrix) { - mOutputTransformMatrix = matrix; -} - -bool Description::hasInputTransformMatrix() const { - const mat3 identity; - return mInputTransformMatrix != identity; -} - -bool Description::hasOutputTransformMatrix() const { - const mat4 identity; - return mOutputTransformMatrix != identity; -} - -bool Description::hasColorMatrix() const { - const mat4 identity; - return mColorMatrix != identity; -} - -const mat4& Description::getColorMatrix() const { - return mColorMatrix; -} - -void Description::setY410BT2020(bool enable) { - mY410BT2020 = enable; -} - -void Description::setInputTransferFunction(TransferFunction transferFunction) { - mInputTransferFunction = transferFunction; -} - -void Description::setOutputTransferFunction(TransferFunction transferFunction) { - mOutputTransferFunction = transferFunction; -} - -void Description::setDisplayMaxLuminance(const float maxLuminance) { - mDisplayMaxLuminance = maxLuminance; -} - -} /* namespace android */ diff --git a/services/surfaceflinger/RenderEngine/Description.h b/services/surfaceflinger/RenderEngine/Description.h deleted file mode 100644 index 6ebb34018d..0000000000 --- a/services/surfaceflinger/RenderEngine/Description.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <GLES2/gl2.h> -#include "Texture.h" - -#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ -#define SF_RENDER_ENGINE_DESCRIPTION_H_ - -namespace android { - -class Program; - -/* - * This holds the state of the rendering engine. This class is used - * to generate a corresponding GLSL program and set the appropriate - * uniform. - * - * Program and ProgramCache are friends and access the state directly - */ -class Description { -public: - Description() = default; - ~Description() = default; - - void setPremultipliedAlpha(bool premultipliedAlpha); - void setOpaque(bool opaque); - void setTexture(const Texture& texture); - void disableTexture(); - void setColor(const half4& color); - void setProjectionMatrix(const mat4& mtx); - void setColorMatrix(const mat4& mtx); - void setInputTransformMatrix(const mat3& matrix); - void setOutputTransformMatrix(const mat4& matrix); - bool hasInputTransformMatrix() const; - bool hasOutputTransformMatrix() const; - bool hasColorMatrix() const; - const mat4& getColorMatrix() const; - - void setY410BT2020(bool enable); - - enum class TransferFunction : int { - LINEAR, - SRGB, - ST2084, - HLG, // Hybrid Log-Gamma for HDR. - }; - void setInputTransferFunction(TransferFunction transferFunction); - void setOutputTransferFunction(TransferFunction transferFunction); - void setDisplayMaxLuminance(const float maxLuminance); - -private: - friend class Program; - friend class ProgramCache; - - // whether textures are premultiplied - bool mPremultipliedAlpha = false; - // whether this layer is marked as opaque - bool mOpaque = true; - - // Texture this layer uses - Texture mTexture; - bool mTextureEnabled = false; - - // color used when texturing is disabled or when setting alpha. - half4 mColor; - - // true if the sampled pixel values are in Y410/BT2020 rather than RGBA - bool mY410BT2020 = false; - - // transfer functions for the input/output - TransferFunction mInputTransferFunction = TransferFunction::LINEAR; - TransferFunction mOutputTransferFunction = TransferFunction::LINEAR; - - float mDisplayMaxLuminance; - - // projection matrix - mat4 mProjectionMatrix; - mat4 mColorMatrix; - mat3 mInputTransformMatrix; - mat4 mOutputTransformMatrix; -}; - -} /* namespace android */ - -#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp deleted file mode 100644 index 744a70c66c..0000000000 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "RenderEngine" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include <ui/ColorSpace.h> -#include <ui/DebugUtils.h> -#include <ui/Rect.h> - -#include <utils/String8.h> -#include <utils/Trace.h> - -#include <cutils/compiler.h> -#include <gui/ISurfaceComposer.h> -#include <math.h> - -#include "Description.h" -#include "GLES20RenderEngine.h" -#include "Mesh.h" -#include "Program.h" -#include "ProgramCache.h" -#include "Texture.h" - -#include <fstream> -#include <sstream> - -// --------------------------------------------------------------------------- -bool checkGlError(const char* op, int lineNumber) { - bool errorFound = false; - GLint error = glGetError(); - while (error != GL_NO_ERROR) { - errorFound = true; - error = glGetError(); - ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); - } - return errorFound; -} - -static constexpr bool outputDebugPPMs = false; - -void writePPM(const char* basename, GLuint width, GLuint height) { - ALOGV("writePPM #%s: %d x %d", basename, width, height); - - std::vector<GLubyte> pixels(width * height * 4); - std::vector<GLubyte> outBuffer(width * height * 3); - - // TODO(courtneygo): We can now have float formats, need - // to remove this code or update to support. - // Make returned pixels fit in uint32_t, one byte per component - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); - if (checkGlError(__FUNCTION__, __LINE__)) { - return; - } - - std::string filename(basename); - filename.append(".ppm"); - std::ofstream file(filename.c_str(), std::ios::binary); - if (!file.is_open()) { - ALOGE("Unable to open file: %s", filename.c_str()); - ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " - "surfaceflinger to write debug images"); - return; - } - - file << "P6\n"; - file << width << "\n"; - file << height << "\n"; - file << 255 << "\n"; - - auto ptr = reinterpret_cast<char*>(pixels.data()); - auto outPtr = reinterpret_cast<char*>(outBuffer.data()); - for (int y = height - 1; y >= 0; y--) { - char* data = ptr + y * width * sizeof(uint32_t); - - for (GLuint x = 0; x < width; x++) { - // Only copy R, G and B components - outPtr[0] = data[0]; - outPtr[1] = data[1]; - outPtr[2] = data[2]; - data += sizeof(uint32_t); - outPtr += 3; - } - } - file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); -} - -// --------------------------------------------------------------------------- -namespace android { -namespace RE { -namespace impl { -// --------------------------------------------------------------------------- - -using ui::Dataspace; - -GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags) - : RenderEngine(featureFlags), - mVpWidth(0), - mVpHeight(0), - mPlatformHasWideColor((featureFlags & WIDE_COLOR_SUPPORT) != 0) { - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); - glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - - const uint16_t protTexData[] = {0}; - glGenTextures(1, &mProtectedTexName); - glBindTexture(GL_TEXTURE_2D, mProtectedTexName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); - - // mColorBlindnessCorrection = M; - - if (mPlatformHasWideColor) { - ColorSpace srgb(ColorSpace::sRGB()); - ColorSpace displayP3(ColorSpace::DisplayP3()); - ColorSpace bt2020(ColorSpace::BT2020()); - - // Compute sRGB to Display P3 transform matrix. - // NOTE: For now, we are limiting output wide color space support to - // Display-P3 only. - mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform()); - - // Compute Display P3 to sRGB transform matrix. - mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform()); - - // no chromatic adaptation needed since all color spaces use D65 for their white points. - mSrgbToXyz = srgb.getRGBtoXYZ(); - mDisplayP3ToXyz = displayP3.getRGBtoXYZ(); - mBt2020ToXyz = bt2020.getRGBtoXYZ(); - mXyzToSrgb = mat4(srgb.getXYZtoRGB()); - mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); - mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); - } -} - -GLES20RenderEngine::~GLES20RenderEngine() {} - -size_t GLES20RenderEngine::getMaxTextureSize() const { - return mMaxTextureSize; -} - -size_t GLES20RenderEngine::getMaxViewportDims() const { - return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; -} - -void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, - size_t hwh, bool yswap, - Transform::orientation_flags rotation) { - int32_t l = sourceCrop.left; - int32_t r = sourceCrop.right; - - // In GL, (0, 0) is the bottom-left corner, so flip y coordinates - int32_t t = hwh - sourceCrop.top; - int32_t b = hwh - sourceCrop.bottom; - - mat4 m; - if (yswap) { - m = mat4::ortho(l, r, t, b, 0, 1); - } else { - m = mat4::ortho(l, r, b, t, 0, 1); - } - - // Apply custom rotation to the projection. - float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; - switch (rotation) { - case Transform::ROT_0: - break; - case Transform::ROT_90: - m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; - break; - case Transform::ROT_180: - m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; - break; - case Transform::ROT_270: - m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; - break; - default: - break; - } - - glViewport(0, 0, vpw, vph); - mState.setProjectionMatrix(m); - mVpWidth = vpw; - mVpHeight = vph; -} - -void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, - bool disableTexture, const half4& color) { - mState.setPremultipliedAlpha(premultipliedAlpha); - mState.setOpaque(opaque); - mState.setColor(color); - - if (disableTexture) { - mState.disableTexture(); - } - - if (color.a < 1.0f || !opaque) { - glEnable(GL_BLEND); - glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisable(GL_BLEND); - } -} - -void GLES20RenderEngine::setSourceY410BT2020(bool enable) { - mState.setY410BT2020(enable); -} - -void GLES20RenderEngine::setSourceDataSpace(Dataspace source) { - mDataSpace = source; -} - -void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) { - mOutputDataSpace = dataspace; -} - -void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) { - mState.setDisplayMaxLuminance(maxLuminance); -} - -void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) { - GLuint target = texture.getTextureTarget(); - glBindTexture(target, texture.getTextureName()); - GLenum filter = GL_NEAREST; - if (texture.getFiltering()) { - filter = GL_LINEAR; - } - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); - - mState.setTexture(texture); -} - -void GLES20RenderEngine::setupLayerBlackedOut() { - glBindTexture(GL_TEXTURE_2D, mProtectedTexName); - Texture texture(Texture::TEXTURE_2D, mProtectedTexName); - texture.setDimensions(1, 1); // FIXME: we should get that from somewhere - mState.setTexture(texture); -} - -void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) { - mState.setColorMatrix(colorTransform); -} - -void GLES20RenderEngine::disableTexturing() { - mState.disableTexture(); -} - -void GLES20RenderEngine::disableBlending() { - glDisable(GL_BLEND); -} - -void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, - uint32_t* fbName, uint32_t* status) { - GLuint tname, name; - // turn our EGLImage into a texture - glGenTextures(1, &tname); - glBindTexture(GL_TEXTURE_2D, tname); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); - - // create a Framebuffer Object to render into - glGenFramebuffers(1, &name); - glBindFramebuffer(GL_FRAMEBUFFER, name); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0); - - *status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - *texName = tname; - *fbName = name; -} - -void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &fbName); - glDeleteTextures(1, &texName); -} - -void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) { - mState.setPremultipliedAlpha(true); - mState.setOpaque(false); - mState.setColor(half4(r, g, b, a)); - mState.disableTexture(); - glDisable(GL_BLEND); -} - -void GLES20RenderEngine::drawMesh(const Mesh& mesh) { - ATRACE_CALL(); - if (mesh.getTexCoordsSize()) { - glEnableVertexAttribArray(Program::texCoords); - glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getTexCoords()); - } - - glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getPositions()); - - // By default, DISPLAY_P3 is the only supported wide color output. However, - // when HDR content is present, hardware composer may be able to handle - // BT2020 data space, in that case, the output data space is set to be - // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need - // to respect this and convert non-HDR content to HDR format. - if (mPlatformHasWideColor) { - Description wideColorState = mState; - Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); - Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); - Dataspace outputStandard = static_cast<Dataspace>(mOutputDataSpace & - Dataspace::STANDARD_MASK); - Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace & - Dataspace::TRANSFER_MASK); - bool needsXYZConversion = needsXYZTransformMatrix(); - - if (needsXYZConversion) { - // The supported input color spaces are standard RGB, Display P3 and BT2020. - switch (inputStandard) { - case Dataspace::STANDARD_DCI_P3: - wideColorState.setInputTransformMatrix(mDisplayP3ToXyz); - break; - case Dataspace::STANDARD_BT2020: - wideColorState.setInputTransformMatrix(mBt2020ToXyz); - break; - default: - wideColorState.setInputTransformMatrix(mSrgbToXyz); - break; - } - - // The supported output color spaces are BT2020, Display P3 and standard RGB. - switch (outputStandard) { - case Dataspace::STANDARD_BT2020: - wideColorState.setOutputTransformMatrix(mXyzToBt2020); - break; - case Dataspace::STANDARD_DCI_P3: - wideColorState.setOutputTransformMatrix(mXyzToDisplayP3); - break; - default: - wideColorState.setOutputTransformMatrix(mXyzToSrgb); - break; - } - } else if (inputStandard != outputStandard) { - // At this point, the input data space and output data space could be both - // HDR data spaces, but they match each other, we do nothing in this case. - // In addition to the case above, the input data space could be - // - scRGB linear - // - scRGB non-linear - // - sRGB - // - Display P3 - // The output data spaces could be - // - sRGB - // - Display P3 - if (outputStandard == Dataspace::STANDARD_BT709) { - wideColorState.setOutputTransformMatrix(mDisplayP3ToSrgb); - } else if (outputStandard == Dataspace::STANDARD_DCI_P3) { - wideColorState.setOutputTransformMatrix(mSrgbToDisplayP3); - } - } - - // we need to convert the RGB value to linear space and convert it back when: - // - there is a color matrix that is not an identity matrix, or - // - there is an output transform matrix that is not an identity matrix, or - // - the input transfer function doesn't match the output transfer function. - if (wideColorState.hasColorMatrix() || wideColorState.hasOutputTransformMatrix() || - inputTransfer != outputTransfer) { - switch (inputTransfer) { - case Dataspace::TRANSFER_ST2084: - wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084); - break; - case Dataspace::TRANSFER_HLG: - wideColorState.setInputTransferFunction(Description::TransferFunction::HLG); - break; - case Dataspace::TRANSFER_LINEAR: - wideColorState.setInputTransferFunction(Description::TransferFunction::LINEAR); - break; - default: - wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB); - break; - } - - switch (outputTransfer) { - case Dataspace::TRANSFER_ST2084: - wideColorState.setOutputTransferFunction(Description::TransferFunction::ST2084); - break; - case Dataspace::TRANSFER_HLG: - wideColorState.setOutputTransferFunction(Description::TransferFunction::HLG); - break; - default: - wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB); - break; - } - } - - ProgramCache::getInstance().useProgram(wideColorState); - - glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); - - if (outputDebugPPMs) { - static uint64_t wideColorFrameCount = 0; - std::ostringstream out; - out << "/data/texture_out" << wideColorFrameCount++; - writePPM(out.str().c_str(), mVpWidth, mVpHeight); - } - } else { - ProgramCache::getInstance().useProgram(mState); - - glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); - } - - if (mesh.getTexCoordsSize()) { - glDisableVertexAttribArray(Program::texCoords); - } -} - -void GLES20RenderEngine::dump(String8& result) { - RenderEngine::dump(result); - result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n", - dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), - dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); -} - -bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { - const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); - const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); - return standard == Dataspace::STANDARD_BT2020 && - (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); -} - -// For convenience, we want to convert the input color space to XYZ color space first, -// and then convert from XYZ color space to output color space when -// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or -// HDR content will be tone-mapped to SDR; Or, -// - there are HDR PQ and HLG contents presented at the same time, where we want to convert -// HLG content to PQ content. -// In either case above, we need to operate the Y value in XYZ color space. Thus, when either -// input data space or output data space is HDR data space, and the input transfer function -// doesn't match the output transfer function, we would enable an intermediate transfrom to -// XYZ color space. -bool GLES20RenderEngine::needsXYZTransformMatrix() const { - const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); - const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); - const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); - const Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace & - Dataspace::TRANSFER_MASK); - - return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; -} - -// --------------------------------------------------------------------------- -} // namespace impl -} // namespace RE -} // namespace android -// --------------------------------------------------------------------------- - -#if defined(__gl_h_) -#error "don't include gl/gl.h in this file" -#endif diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h deleted file mode 100644 index cc8eb1dfab..0000000000 --- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_GLES20RENDERENGINE_H_ -#define SF_GLES20RENDERENGINE_H_ - -#include <stdint.h> -#include <sys/types.h> - -#include <GLES2/gl2.h> -#include <Transform.h> - -#include "Description.h" -#include "ProgramCache.h" -#include "RenderEngine.h" - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -class String8; -class Mesh; -class Texture; - -namespace RE { -namespace impl { - -class GLES20RenderEngine : public RenderEngine { - GLuint mProtectedTexName; - GLint mMaxViewportDims[2]; - GLint mMaxTextureSize; - GLuint mVpWidth; - GLuint mVpHeight; - - struct Group { - GLuint texture; - GLuint fbo; - GLuint width; - GLuint height; - mat4 colorTransform; - }; - - Description mState; - Vector<Group> mGroupStack; - - virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName, - uint32_t* status); - virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName); - -public: - GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag - virtual ~GLES20RenderEngine(); - -protected: - virtual void dump(String8& result); - virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, - bool yswap, Transform::orientation_flags rotation); - virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color) override; - - // Color management related functions and state - void setSourceY410BT2020(bool enable) override; - void setSourceDataSpace(ui::Dataspace source) override; - void setOutputDataSpace(ui::Dataspace dataspace) override; - void setDisplayMaxLuminance(const float maxLuminance) override; - - virtual void setupLayerTexturing(const Texture& texture); - virtual void setupLayerBlackedOut(); - virtual void setupFillWithColor(float r, float g, float b, float a); - virtual void setupColorTransform(const mat4& colorTransform); - virtual void disableTexturing(); - virtual void disableBlending(); - - virtual void drawMesh(const Mesh& mesh); - - virtual size_t getMaxTextureSize() const; - virtual size_t getMaxViewportDims() const; - - // Current dataspace of layer being rendered - ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; - - // Current output dataspace of the render engine - ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; - - // Currently only supporting sRGB, BT2020 and DisplayP3 color spaces - const bool mPlatformHasWideColor = false; - mat4 mSrgbToDisplayP3; - mat4 mDisplayP3ToSrgb; - mat3 mSrgbToXyz; - mat3 mBt2020ToXyz; - mat3 mDisplayP3ToXyz; - mat4 mXyzToSrgb; - mat4 mXyzToDisplayP3; - mat4 mXyzToBt2020; - -private: - // A data space is considered HDR data space if it has BT2020 color space - // with PQ or HLG transfer function. - bool isHdrDataSpace(const ui::Dataspace dataSpace) const; - bool needsXYZTransformMatrix() const; -}; - -// --------------------------------------------------------------------------- -} // namespace impl -} // namespace RE -} // namespace android -// --------------------------------------------------------------------------- - -#endif /* SF_GLES20RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp deleted file mode 100644 index d745770bb7..0000000000 --- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <log/log.h> -#include <ui/Rect.h> -#include <ui/Region.h> - -#include "GLES20RenderEngine.h" -#include "GLExtensions.h" -#include "Image.h" -#include "Mesh.h" -#include "RenderEngine.h" - -#include <SurfaceFlinger.h> -#include <vector> - -#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> -#include <configstore/Utils.h> - -using namespace android::hardware::configstore; -using namespace android::hardware::configstore::V1_0; - -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - -// --------------------------------------------------------------------------- -namespace android { -namespace RE { -// --------------------------------------------------------------------------- - -RenderEngine::~RenderEngine() = default; - -namespace impl { - -std::unique_ptr<RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) { - // initialize EGL for the default display - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(display, nullptr, nullptr)) { - LOG_ALWAYS_FATAL("failed to initialize EGL"); - } - - GLExtensions& extensions = GLExtensions::getInstance(); - extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), - eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); - - // The code assumes that ES2 or later is available if this extension is - // supported. - EGLConfig config = EGL_NO_CONFIG; - if (!extensions.hasNoConfigContext()) { - config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); - } - - EGLint renderableType = 0; - if (config == EGL_NO_CONFIG) { - renderableType = EGL_OPENGL_ES2_BIT; - } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { - LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); - } - EGLint contextClientVersion = 0; - if (renderableType & EGL_OPENGL_ES2_BIT) { - contextClientVersion = 2; - } else if (renderableType & EGL_OPENGL_ES_BIT) { - contextClientVersion = 1; - } else { - LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); - } - - std::vector<EGLint> contextAttributes; - contextAttributes.reserve(6); - contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); - contextAttributes.push_back(contextClientVersion); - bool useContextPriority = overrideUseContextPriorityFromConfig(extensions.hasContextPriority()); - if (useContextPriority) { - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); - } - contextAttributes.push_back(EGL_NONE); - - EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data()); - - // if can't create a GL context, we can only abort. - LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); - - // now figure out what version of GL did we actually get - // NOTE: a dummy surface is not needed if KHR_create_context is supported - - EGLConfig dummyConfig = config; - if (dummyConfig == EGL_NO_CONFIG) { - dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); - } - EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE}; - EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs); - LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); - EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); - LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); - - extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), - glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); - - GlesVersion version = parseGlesVersion(extensions.getVersion()); - - // initialize the renderer while GL is current - - std::unique_ptr<RenderEngine> engine; - switch (version) { - case GLES_VERSION_1_0: - case GLES_VERSION_1_1: - LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); - break; - case GLES_VERSION_2_0: - case GLES_VERSION_3_0: - engine = std::make_unique<GLES20RenderEngine>(featureFlags); - break; - } - engine->setEGLHandles(display, config, ctxt); - - ALOGI("OpenGL ES informations:"); - ALOGI("vendor : %s", extensions.getVendor()); - ALOGI("renderer : %s", extensions.getRenderer()); - ALOGI("version : %s", extensions.getVersion()); - ALOGI("extensions: %s", extensions.getExtensions()); - ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); - ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); - - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(display, dummy); - - return engine; -} - -bool RenderEngine::overrideUseContextPriorityFromConfig(bool useContextPriority) { - OptionalBool ret; - ISurfaceFlingerConfigs::getService()->useContextPriority([&ret](OptionalBool b) { ret = b; }); - if (ret.specified) { - return ret.value; - } else { - return useContextPriority; - } -} - -RenderEngine::RenderEngine(uint32_t featureFlags) - : mEGLDisplay(EGL_NO_DISPLAY), - mEGLConfig(nullptr), - mEGLContext(EGL_NO_CONTEXT), - mFeatureFlags(featureFlags) {} - -RenderEngine::~RenderEngine() { - eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglTerminate(mEGLDisplay); -} - -void RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) { - mEGLDisplay = display; - mEGLConfig = config; - mEGLContext = ctxt; -} - -EGLDisplay RenderEngine::getEGLDisplay() const { - return mEGLDisplay; -} - -EGLConfig RenderEngine::getEGLConfig() const { - return mEGLConfig; -} - -bool RenderEngine::supportsImageCrop() const { - return GLExtensions::getInstance().hasImageCrop(); -} - -bool RenderEngine::isCurrent() const { - return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); -} - -std::unique_ptr<RE::Surface> RenderEngine::createSurface() { - return std::make_unique<Surface>(*this); -} - -std::unique_ptr<RE::Image> RenderEngine::createImage() { - return std::make_unique<Image>(*this); -} - -bool RenderEngine::setCurrentSurface(const android::RE::Surface& surface) { - // Note: RE::Surface is an abstract interface. This implementation only ever - // creates RE::impl::Surface's, so it is safe to just cast to the actual - // type. - return setCurrentSurface(static_cast<const android::RE::impl::Surface&>(surface)); -} - -bool RenderEngine::setCurrentSurface(const android::RE::impl::Surface& surface) { - bool success = true; - EGLSurface eglSurface = surface.getEGLSurface(); - if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) { - success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE; - if (success && surface.getAsync()) { - eglSwapInterval(mEGLDisplay, 0); - } - } - - return success; -} - -void RenderEngine::resetCurrentSurface() { - eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); -} - -base::unique_fd RenderEngine::flush() { - if (!GLExtensions::getInstance().hasNativeFenceSync()) { - return base::unique_fd(); - } - - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); - return base::unique_fd(); - } - - // native fence fd will not be populated until flush() is done. - glFlush(); - - // get the fence fd - base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); - eglDestroySyncKHR(mEGLDisplay, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); - } - - return fenceFd; -} - -bool RenderEngine::finish() { - if (!GLExtensions::getInstance().hasFenceSync()) { - ALOGW("no synchronization support"); - return false; - } - - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGW("failed to create EGL fence sync: %#x", eglGetError()); - return false; - } - - EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, - 2000000000 /*2 sec*/); - EGLint error = eglGetError(); - eglDestroySyncKHR(mEGLDisplay, sync); - if (result != EGL_CONDITION_SATISFIED_KHR) { - if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGW("fence wait timed out"); - } else { - ALOGW("error waiting on EGL fence: %#x", error); - } - return false; - } - - return true; -} - -bool RenderEngine::waitFence(base::unique_fd fenceFd) { - if (!GLExtensions::getInstance().hasNativeFenceSync() || - !GLExtensions::getInstance().hasWaitSync()) { - return false; - } - - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); - return false; - } - - // fenceFd is now owned by EGLSync - (void)fenceFd.release(); - - // XXX: The spec draft is inconsistent as to whether this should return an - // EGLint or void. Ignore the return value for now, as it's not strictly - // needed. - eglWaitSyncKHR(mEGLDisplay, sync, 0); - EGLint error = eglGetError(); - eglDestroySyncKHR(mEGLDisplay, sync); - if (error != EGL_SUCCESS) { - ALOGE("failed to wait for EGL native fence sync: %#x", error); - return false; - } - - return true; -} - -void RenderEngine::checkErrors() const { - do { - // there could be more than one error flag - GLenum error = glGetError(); - if (error == GL_NO_ERROR) break; - ALOGE("GL error 0x%04x", int(error)); - } while (true); -} - -RenderEngine::GlesVersion RenderEngine::parseGlesVersion(const char* str) { - int major, minor; - if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { - if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { - ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); - return GLES_VERSION_1_0; - } - } - - if (major == 1 && minor == 0) return GLES_VERSION_1_0; - if (major == 1 && minor >= 1) return GLES_VERSION_1_1; - if (major == 2 && minor >= 0) return GLES_VERSION_2_0; - if (major == 3 && minor >= 0) return GLES_VERSION_3_0; - - ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); - return GLES_VERSION_1_0; -} - -void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height, float red, - float green, float blue, float alpha) { - size_t c; - Rect const* r = region.getArray(&c); - Mesh mesh(Mesh::TRIANGLES, c * 6, 2); - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - for (size_t i = 0; i < c; i++, r++) { - position[i * 6 + 0].x = r->left; - position[i * 6 + 0].y = height - r->top; - position[i * 6 + 1].x = r->left; - position[i * 6 + 1].y = height - r->bottom; - position[i * 6 + 2].x = r->right; - position[i * 6 + 2].y = height - r->bottom; - position[i * 6 + 3].x = r->left; - position[i * 6 + 3].y = height - r->top; - position[i * 6 + 4].x = r->right; - position[i * 6 + 4].y = height - r->bottom; - position[i * 6 + 5].x = r->right; - position[i * 6 + 5].y = height - r->top; - } - setupFillWithColor(red, green, blue, alpha); - drawMesh(mesh); -} - -void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) { - glClearColor(red, green, blue, alpha); - glClear(GL_COLOR_BUFFER_BIT); -} - -void RenderEngine::setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) { - glScissor(left, bottom, right, top); - glEnable(GL_SCISSOR_TEST); -} - -void RenderEngine::disableScissor() { - glDisable(GL_SCISSOR_TEST); -} - -void RenderEngine::genTextures(size_t count, uint32_t* names) { - glGenTextures(count, names); -} - -void RenderEngine::deleteTextures(size_t count, uint32_t const* names) { - glDeleteTextures(count, names); -} - -void RenderEngine::bindExternalTextureImage(uint32_t texName, const android::RE::Image& image) { - // Note: RE::Image is an abstract interface. This implementation only ever - // creates RE::impl::Image's, so it is safe to just cast to the actual type. - return bindExternalTextureImage(texName, static_cast<const android::RE::impl::Image&>(image)); -} - -void RenderEngine::bindExternalTextureImage(uint32_t texName, - const android::RE::impl::Image& image) { - const GLenum target = GL_TEXTURE_EXTERNAL_OES; - - glBindTexture(target, texName); - if (image.getEGLImage() != EGL_NO_IMAGE_KHR) { - glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(image.getEGLImage())); - } -} - -void RenderEngine::readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) { - glReadPixels(l, b, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -} - -void RenderEngine::dump(String8& result) { - const GLExtensions& extensions = GLExtensions::getInstance(); - - result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion()); - result.appendFormat("%s\n", extensions.getEGLExtensions()); - - result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), - extensions.getVersion()); - result.appendFormat("%s\n", extensions.getExtensions()); -} - -// --------------------------------------------------------------------------- - -void RenderEngine::bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer, - RE::BindNativeBufferAsFramebuffer* bindHelper) { - bindHelper->mImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer, nullptr); - if (bindHelper->mImage == EGL_NO_IMAGE_KHR) { - bindHelper->mStatus = NO_MEMORY; - return; - } - - uint32_t glStatus; - bindImageAsFramebuffer(bindHelper->mImage, &bindHelper->mTexName, &bindHelper->mFbName, - &glStatus); - - ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", - glStatus); - - bindHelper->mStatus = glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; -} - -void RenderEngine::unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) { - if (bindHelper->mImage == EGL_NO_IMAGE_KHR) { - return; - } - - // back to main framebuffer - unbindFramebuffer(bindHelper->mTexName, bindHelper->mFbName); - eglDestroyImageKHR(mEGLDisplay, bindHelper->mImage); - - // Workaround for b/77935566 to force the EGL driver to release the - // screenshot buffer - setScissor(0, 0, 0, 0); - clearWithColor(0.0, 0.0, 0.0, 0.0); - disableScissor(); -} - -// --------------------------------------------------------------------------- - -static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, - EGLint wanted, EGLConfig* outConfig) { - EGLint numConfigs = -1, n = 0; - eglGetConfigs(dpy, nullptr, 0, &numConfigs); - EGLConfig* const configs = new EGLConfig[numConfigs]; - eglChooseConfig(dpy, attrs, configs, numConfigs, &n); - - if (n) { - if (attribute != EGL_NONE) { - for (int i = 0; i < n; i++) { - EGLint value = 0; - eglGetConfigAttrib(dpy, configs[i], attribute, &value); - if (wanted == value) { - *outConfig = configs[i]; - delete[] configs; - return NO_ERROR; - } - } - } else { - // just pick the first one - *outConfig = configs[0]; - delete[] configs; - return NO_ERROR; - } - } - delete[] configs; - return NAME_NOT_FOUND; -} - -class EGLAttributeVector { - struct Attribute; - class Adder; - friend class Adder; - KeyedVector<Attribute, EGLint> mList; - struct Attribute { - Attribute() : v(0){}; - explicit Attribute(EGLint v) : v(v) {} - EGLint v; - bool operator<(const Attribute& other) const { - // this places EGL_NONE at the end - EGLint lhs(v); - EGLint rhs(other.v); - if (lhs == EGL_NONE) lhs = 0x7FFFFFFF; - if (rhs == EGL_NONE) rhs = 0x7FFFFFFF; - return lhs < rhs; - } - }; - class Adder { - friend class EGLAttributeVector; - EGLAttributeVector& v; - EGLint attribute; - Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {} - - public: - void operator=(EGLint value) { - if (attribute != EGL_NONE) { - v.mList.add(Attribute(attribute), value); - } - } - operator EGLint() const { return v.mList[attribute]; } - }; - -public: - EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); } - void remove(EGLint attribute) { - if (attribute != EGL_NONE) { - mList.removeItem(Attribute(attribute)); - } - } - Adder operator[](EGLint attribute) { return Adder(*this, attribute); } - EGLint operator[](EGLint attribute) const { return mList[attribute]; } - // cast-operator to (EGLint const*) - operator EGLint const*() const { return &mList.keyAt(0).v; } -}; - -static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, - EGLConfig* config) { - // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if - // it is to be used with WIFI displays - status_t err; - EGLint wantedAttribute; - EGLint wantedAttributeValue; - - EGLAttributeVector attribs; - if (renderableType) { - attribs[EGL_RENDERABLE_TYPE] = renderableType; - attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; - attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; - attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; - attribs[EGL_RED_SIZE] = 8; - attribs[EGL_GREEN_SIZE] = 8; - attribs[EGL_BLUE_SIZE] = 8; - attribs[EGL_ALPHA_SIZE] = 8; - wantedAttribute = EGL_NONE; - wantedAttributeValue = EGL_NONE; - } else { - // if no renderable type specified, fallback to a simplified query - wantedAttribute = EGL_NATIVE_VISUAL_ID; - wantedAttributeValue = format; - } - - err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config); - if (err == NO_ERROR) { - EGLint caveat; - if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) - ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); - } - - return err; -} - -EGLConfig RenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { - status_t err; - EGLConfig config; - - // First try to get an ES2 config - err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); - if (err != NO_ERROR) { - // If ES2 fails, try ES1 - err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config); - if (err != NO_ERROR) { - // still didn't work, probably because we're on the emulator... - // try a simplified query - ALOGW("no suitable EGLConfig found, trying a simpler query"); - err = selectEGLConfig(display, format, 0, &config); - if (err != NO_ERROR) { - // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); - } - } - } - - if (logConfig) { - // print some debugging info - EGLint r, g, b, a; - eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); - eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); - eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); - eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); - ALOGI("EGL information:"); - ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); - ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); - ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); - ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); - ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); - } - - return config; -} - -void RenderEngine::primeCache() const { - ProgramCache::getInstance().primeCache(mFeatureFlags & WIDE_COLOR_SUPPORT); -} - -// --------------------------------------------------------------------------- - -} // namespace impl -} // namespace RE -} // namespace android diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h deleted file mode 100644 index 1786155486..0000000000 --- a/services/surfaceflinger/RenderEngine/RenderEngine.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDERENGINE_H_ -#define SF_RENDERENGINE_H_ - -#include <memory> - -#include <stdint.h> -#include <sys/types.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <Transform.h> -#include <android-base/unique_fd.h> -#include <gui/SurfaceControl.h> -#include <math/mat4.h> - -#define EGL_NO_CONFIG ((EGLConfig)0) - -struct ANativeWindowBuffer; - -// --------------------------------------------------------------------------- -namespace android { -// --------------------------------------------------------------------------- - -class String8; -class Rect; -class Region; -class Mesh; -class Texture; - -namespace RE { - -class Image; -class Surface; -class BindNativeBufferAsFramebuffer; - -namespace impl { -class RenderEngine; -} - -class RenderEngine { -public: - enum FeatureFlag { - WIDE_COLOR_SUPPORT = 1 << 0 // Platform has a wide color display - }; - - virtual ~RenderEngine() = 0; - - virtual std::unique_ptr<RE::Surface> createSurface() = 0; - virtual std::unique_ptr<RE::Image> createImage() = 0; - - virtual void primeCache() const = 0; - - // dump the extension strings. always call the base class. - virtual void dump(String8& result) = 0; - - virtual bool supportsImageCrop() const = 0; - - virtual bool isCurrent() const = 0; - virtual bool setCurrentSurface(const RE::Surface& surface) = 0; - virtual void resetCurrentSurface() = 0; - - // helpers - // flush submits RenderEngine command stream for execution and returns a - // native fence fd that is signaled when the execution has completed. It - // returns -1 on errors. - virtual base::unique_fd flush() = 0; - // finish waits until RenderEngine command stream has been executed. It - // returns false on errors. - virtual bool finish() = 0; - // waitFence inserts a wait on an external fence fd to RenderEngine - // command stream. It returns false on errors. - virtual bool waitFence(base::unique_fd fenceFd) = 0; - - virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; - virtual void fillRegionWithColor(const Region& region, uint32_t height, float red, float green, - float blue, float alpha) = 0; - - // common to all GL versions - virtual void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) = 0; - virtual void disableScissor() = 0; - virtual void genTextures(size_t count, uint32_t* names) = 0; - virtual void deleteTextures(size_t count, uint32_t const* names) = 0; - virtual void bindExternalTextureImage(uint32_t texName, const RE::Image& image) = 0; - virtual void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) = 0; - virtual void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer, - RE::BindNativeBufferAsFramebuffer* bindHelper) = 0; - virtual void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) = 0; - - // set-up - virtual void checkErrors() const; - virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, - bool yswap, Transform::orientation_flags rotation) = 0; - virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color) = 0; - virtual void setupLayerTexturing(const Texture& texture) = 0; - virtual void setupLayerBlackedOut() = 0; - virtual void setupFillWithColor(float r, float g, float b, float a) = 0; - - virtual void setupColorTransform(const mat4& /* colorTransform */) = 0; - - virtual void disableTexturing() = 0; - virtual void disableBlending() = 0; - - // HDR and wide color gamut support - virtual void setSourceY410BT2020(bool enable) = 0; - virtual void setSourceDataSpace(ui::Dataspace source) = 0; - virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; - virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; - - // drawing - virtual void drawMesh(const Mesh& mesh) = 0; - - // queries - virtual size_t getMaxTextureSize() const = 0; - virtual size_t getMaxViewportDims() const = 0; -}; - -class BindNativeBufferAsFramebuffer { -public: - BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer) - : mEngine(engine) { - mEngine.bindNativeBufferAsFrameBuffer(buffer, this); - } - ~BindNativeBufferAsFramebuffer() { mEngine.unbindNativeBufferAsFrameBuffer(this); } - status_t getStatus() const { return mStatus; } - -protected: - friend impl::RenderEngine; - - RenderEngine& mEngine; - EGLImageKHR mImage; - uint32_t mTexName, mFbName; - status_t mStatus; -}; - -namespace impl { - -class Image; -class Surface; - -class RenderEngine : public RE::RenderEngine { - enum GlesVersion { - GLES_VERSION_1_0 = 0x10000, - GLES_VERSION_1_1 = 0x10001, - GLES_VERSION_2_0 = 0x20000, - GLES_VERSION_3_0 = 0x30000, - }; - static GlesVersion parseGlesVersion(const char* str); - - EGLDisplay mEGLDisplay; - EGLConfig mEGLConfig; - EGLContext mEGLContext; - void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt); - - static bool overrideUseContextPriorityFromConfig(bool useContextPriority); - -protected: - RenderEngine(uint32_t featureFlags); - - const uint32_t mFeatureFlags; - -public: - virtual ~RenderEngine() = 0; - - static std::unique_ptr<RenderEngine> create(int hwcFormat, uint32_t featureFlags); - - static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); - - // RenderEngine interface implementation - - std::unique_ptr<RE::Surface> createSurface() override; - std::unique_ptr<RE::Image> createImage() override; - - void primeCache() const override; - - // dump the extension strings. always call the base class. - void dump(String8& result) override; - - bool supportsImageCrop() const override; - - bool isCurrent() const; - bool setCurrentSurface(const RE::Surface& surface) override; - void resetCurrentSurface() override; - - // synchronization - - // flush submits RenderEngine command stream for execution and returns a - // native fence fd that is signaled when the execution has completed. It - // returns -1 on errors. - base::unique_fd flush() override; - // finish waits until RenderEngine command stream has been executed. It - // returns false on errors. - bool finish() override; - // waitFence inserts a wait on an external fence fd to RenderEngine - // command stream. It returns false on errors. - bool waitFence(base::unique_fd fenceFd) override; - - // helpers - void clearWithColor(float red, float green, float blue, float alpha) override; - void fillRegionWithColor(const Region& region, uint32_t height, float red, float green, - float blue, float alpha) override; - - // common to all GL versions - void setScissor(uint32_t left, uint32_t bottom, uint32_t right, uint32_t top) override; - void disableScissor() override; - void genTextures(size_t count, uint32_t* names) override; - void deleteTextures(size_t count, uint32_t const* names) override; - void bindExternalTextureImage(uint32_t texName, const RE::Image& image) override; - void readPixels(size_t l, size_t b, size_t w, size_t h, uint32_t* pixels) override; - - void checkErrors() const override; - - void setupColorTransform(const mat4& /* colorTransform */) override {} - - // internal to RenderEngine - EGLDisplay getEGLDisplay() const; - EGLConfig getEGLConfig() const; - - // Common implementation - bool setCurrentSurface(const RE::impl::Surface& surface); - void bindExternalTextureImage(uint32_t texName, const RE::impl::Image& image); - - void bindNativeBufferAsFrameBuffer(ANativeWindowBuffer* buffer, - RE::BindNativeBufferAsFramebuffer* bindHelper) override; - void unbindNativeBufferAsFrameBuffer(RE::BindNativeBufferAsFramebuffer* bindHelper) override; - - // Overriden by each specialization - virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName, - uint32_t* status) = 0; - virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName) = 0; -}; - -} // namespace impl -} // namespace RE -} // namespace android - -#endif /* SF_RENDERENGINE_H_ */ diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp index 37dc27d80d..172c4184f9 100644 --- a/services/surfaceflinger/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -25,10 +25,10 @@ #include <algorithm> #include <log/log.h> +#include <cutils/properties.h> #include <utils/String8.h> #include <utils/Thread.h> #include <utils/Trace.h> -#include <utils/Vector.h> #include <ui/FenceTime.h> @@ -41,9 +41,9 @@ using std::min; namespace android { -// Setting this to true enables verbose tracing that can be used to debug -// vsync event model or phase issues. -static const bool kTraceDetailedInfo = false; +DispSync::~DispSync() = default; + +namespace impl { // Setting this to true adds a zero-phase tracer for correlating with hardware // vsync events @@ -59,19 +59,20 @@ static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared #define LOG_TAG "DispSyncThread" class DispSyncThread : public Thread { public: - explicit DispSyncThread(const char* name) + DispSyncThread(const char* name, bool showTraceDetailedInfo) : mName(name), mStop(false), mPeriod(0), mPhase(0), mReferenceTime(0), mWakeupLatency(0), - mFrameNumber(0) {} + mFrameNumber(0), + mTraceDetailedInfo(showTraceDetailedInfo) {} virtual ~DispSyncThread() {} void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); mPeriod = period; mPhase = phase; @@ -83,7 +84,7 @@ public: } void stop() { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); mStop = true; mCond.signal(); @@ -94,14 +95,14 @@ public: nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); while (true) { - Vector<CallbackInvocation> callbackInvocations; + std::vector<CallbackInvocation> callbackInvocations; nsecs_t targetTime = 0; { // Scope for lock Mutex::Autolock lock(mMutex); - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Frame", mFrameNumber); } ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber); @@ -125,7 +126,7 @@ public: bool isWakeup = false; if (now < targetTime) { - if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting"); + if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting"); if (targetTime == INT64_MAX) { ALOGV("[%s] Waiting forever", mName); @@ -151,7 +152,7 @@ public: if (isWakeup) { mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:WakeupLat", now - targetTime); ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency); } @@ -169,7 +170,7 @@ public: } status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); for (size_t i = 0; i < mEventListeners.size(); i++) { @@ -187,7 +188,7 @@ public: // allowing any past events to fire listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; - mEventListeners.push(listener); + mEventListeners.push_back(listener); mCond.signal(); @@ -195,12 +196,13 @@ public: } status_t removeEventListener(DispSync::Callback* callback) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); - for (size_t i = 0; i < mEventListeners.size(); i++) { - if (mEventListeners[i].mCallback == callback) { - mEventListeners.removeAt(i); + for (std::vector<EventListener>::iterator it = mEventListeners.begin(); + it != mEventListeners.end(); ++it) { + if (it->mCallback == callback) { + mEventListeners.erase(it); mCond.signal(); return NO_ERROR; } @@ -210,14 +212,13 @@ public: } status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); - for (size_t i = 0; i < mEventListeners.size(); i++) { - if (mEventListeners[i].mCallback == callback) { - EventListener& listener = mEventListeners.editItemAt(i); - const nsecs_t oldPhase = listener.mPhase; - listener.mPhase = phase; + for (auto& eventListener : mEventListeners) { + if (eventListener.mCallback == callback) { + const nsecs_t oldPhase = eventListener.mPhase; + eventListener.mPhase = phase; // Pretend that the last time this event was handled at the same frame but with the // new offset to allow for a seamless offset change without double-firing or @@ -228,7 +229,7 @@ public: } else if (diff < -mPeriod / 2) { diff += mPeriod; } - listener.mLastEventTime -= diff; + eventListener.mLastEventTime -= diff; mCond.signal(); return NO_ERROR; } @@ -237,14 +238,6 @@ public: return BAD_VALUE; } - // This method is only here to handle the !SurfaceFlinger::hasSyncFramework - // case. - bool hasAnyEventListeners() { - if (kTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - return !mEventListeners.empty(); - } - private: struct EventListener { const char* mName; @@ -259,7 +252,7 @@ private: }; nsecs_t computeNextEventTimeLocked(nsecs_t now) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); ALOGV("[%s] computeNextEventTimeLocked", mName); nsecs_t nextEventTime = INT64_MAX; for (size_t i = 0; i < mEventListeners.size(); i++) { @@ -274,23 +267,23 @@ private: return nextEventTime; } - Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { - if (kTraceDetailedInfo) ATRACE_CALL(); + std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { + if (mTraceDetailedInfo) ATRACE_CALL(); ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); - Vector<CallbackInvocation> callbackInvocations; + std::vector<CallbackInvocation> callbackInvocations; nsecs_t onePeriodAgo = now - mPeriod; - for (size_t i = 0; i < mEventListeners.size(); i++) { - nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], onePeriodAgo); + for (auto& eventListener : mEventListeners) { + nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); if (t < now) { CallbackInvocation ci; - ci.mCallback = mEventListeners[i].mCallback; + ci.mCallback = eventListener.mCallback; ci.mEventTime = t; - ALOGV("[%s] [%s] Preparing to fire", mName, mEventListeners[i].mName); - callbackInvocations.push(ci); - mEventListeners.editItemAt(i).mLastEventTime = t; + ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName); + callbackInvocations.push_back(ci); + eventListener.mLastEventTime = t; } } @@ -298,7 +291,7 @@ private: } nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName, ns2us(baseTime)); @@ -348,8 +341,8 @@ private: return t; } - void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) { - if (kTraceDetailedInfo) ATRACE_CALL(); + void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) { + if (mTraceDetailedInfo) ATRACE_CALL(); for (size_t i = 0; i < callbacks.size(); i++) { callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); } @@ -366,10 +359,13 @@ private: int64_t mFrameNumber; - Vector<EventListener> mEventListeners; + std::vector<EventListener> mEventListeners; Mutex mMutex; Condition mCond; + + // Flag to turn on logging in systrace. + const bool mTraceDetailedInfo; }; #undef LOG_TAG @@ -388,8 +384,13 @@ private: bool mParity; }; -DispSync::DispSync(const char* name) - : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {} +DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) { + // This flag offers the ability to turn on systrace logging from the shell. + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.dispsync_trace_detailed_info", value, "0"); + mTraceDetailedInfo = atoi(value); + mThread = new DispSyncThread(name, mTraceDetailedInfo); +} DispSync::~DispSync() {} @@ -408,22 +409,18 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { reset(); beginResync(); - if (kTraceDetailedInfo) { - // If we're not getting present fences then the ZeroPhaseTracer - // would prevent HW vsync event from ever being turned off. - // Even if we're just ignoring the fences, the zero-phase tracing is - // not needed because any time there is an event registered we will - // turn on the HW vsync events. - if (!mIgnorePresentFences && kEnableZeroPhaseTracer) { - mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>(); - addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); - } + if (mTraceDetailedInfo && kEnableZeroPhaseTracer) { + mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>(); + addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); } } void DispSync::reset() { Mutex::Autolock lock(mMutex); + resetLocked(); +} +void DispSync::resetLocked() { mPhase = 0; mReferenceTime = 0; mModelUpdated = false; @@ -436,6 +433,10 @@ void DispSync::reset() { bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { Mutex::Autolock lock(mMutex); + if (mIgnorePresentFences) { + return true; + } + mPresentFences[mPresentSampleOffset] = fenceTime; mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; mNumResyncSamplesSincePresent = 0; @@ -481,12 +482,10 @@ bool DispSync::addResyncSample(nsecs_t timestamp) { } if (mIgnorePresentFences) { - // If we don't have the sync framework we will never have - // addPresentFence called. This means we have no way to know whether + // If we're ignoring the present fences we have no way to know whether // or not we're synchronized with the HW vsyncs, so we just request - // that the HW vsync events be turned on whenever we need to generate - // SW vsync events. - return mThread->hasAnyEventListeners(); + // that the HW vsync events be turned on. + return true; } // Check against kErrorThreshold / 2 to add some hysteresis before having to @@ -580,7 +579,7 @@ void DispSync::updateModelLocked() { ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); } - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Period", mPeriod); ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); } @@ -639,7 +638,7 @@ void DispSync::updateErrorLocked() { "No present times for model error."); } - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Error", mError); } } @@ -660,6 +659,14 @@ nsecs_t DispSync::computeNextRefresh(int periodOffset) const { return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; } +void DispSync::setIgnorePresentFences(bool ignore) { + Mutex::Autolock lock(mMutex); + if (mIgnorePresentFences != ignore) { + mIgnorePresentFences = ignore; + resetLocked(); + } +} + void DispSync::dump(String8& result) const { Mutex::Autolock lock(mMutex); result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used"); @@ -710,4 +717,54 @@ void DispSync::dump(String8& result) const { result.appendFormat("current monotonic time: %" PRId64 "\n", now); } +// TODO(b/113612090): Figure out how much of this is still relevant. +// We need to determine the time when a buffer acquired now will be +// displayed. This can be calculated: +// time when previous buffer's actual-present fence was signaled +// + current display refresh rate * HWC latency +// + a little extra padding +// +// Buffer producers are expected to set their desired presentation time +// based on choreographer time stamps, which (coming from vsync events) +// will be slightly later then the actual-present timing. If we get a +// desired-present time that is unintentionally a hair after the next +// vsync, we'll hold the frame when we really want to display it. We +// need to take the offset between actual-present and reported-vsync +// into account. +// +// If the system is configured without a DispSync phase offset for the app, +// we also want to throw in a bit of padding to avoid edge cases where we +// just barely miss. We want to do it here, not in every app. A major +// source of trouble is the app's use of the display's ideal refresh time +// (via Display.getRefreshRate()), which could be off of the actual refresh +// by a few percent, with the error multiplied by the number of frames +// between now and when the buffer should be displayed. +// +// If the refresh reported to the app has a phase offset, we shouldn't need +// to tweak anything here. +nsecs_t DispSync::expectedPresentTime() { + // The HWC doesn't currently have a way to report additional latency. + // Assume that whatever we submit now will appear right after the flip. + // For a smart panel this might be 1. This is expressed in frames, + // rather than time, because we expect to have a constant frame delay + // regardless of the refresh rate. + const uint32_t hwcLatency = 0; + + // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC). + const nsecs_t nextRefresh = computeNextRefresh(hwcLatency); + + // The DispSync time is already adjusted for the difference between + // vsync and reported-vsync (SurfaceFlinger::dispSyncPresentTimeOffset), so + // we don't need to factor that in here. Pad a little to avoid + // weird effects if apps might be requesting times right on the edge. + nsecs_t extraPadding = 0; + if (SurfaceFlinger::vsyncPhaseOffsetNs == 0) { + extraPadding = 1000000; // 1ms (6% of 60Hz) + } + + return nextRefresh + extraPadding; +} + +} // namespace impl + } // namespace android diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 077256ac11..5d19093edb 100644 --- a/services/surfaceflinger/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -31,6 +31,37 @@ namespace android { class String8; class FenceTime; + +class DispSync { +public: + class Callback { + public: + virtual ~Callback() = default; + virtual void onDispSyncEvent(nsecs_t when) = 0; + }; + + virtual ~DispSync(); + + virtual void reset() = 0; + virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0; + virtual void beginResync() = 0; + virtual bool addResyncSample(nsecs_t timestamp) = 0; + virtual void endResync() = 0; + virtual void setPeriod(nsecs_t period) = 0; + virtual nsecs_t getPeriod() = 0; + virtual void setRefreshSkipCount(int count) = 0; + virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; + virtual status_t removeEventListener(Callback* callback) = 0; + virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; + virtual nsecs_t computeNextRefresh(int periodOffset) const = 0; + virtual void setIgnorePresentFences(bool ignore) = 0; + virtual nsecs_t expectedPresentTime() = 0; + + virtual void dump(String8& result) const = 0; +}; + +namespace impl { + class DispSyncThread; // DispSync maintains a model of the periodic hardware-based vsync events of a @@ -46,21 +77,15 @@ class DispSyncThread; // current model accurately represents the hardware event times it will return // false to indicate that a resynchronization (via addResyncSample) is not // needed. -class DispSync { +class DispSync : public android::DispSync { public: - class Callback { - public: - virtual ~Callback(){}; - virtual void onDispSyncEvent(nsecs_t when) = 0; - }; - explicit DispSync(const char* name); - ~DispSync(); + ~DispSync() override; void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset); // reset clears the resync samples and error value. - void reset(); + void reset() override; // addPresentFence adds a fence for use in validating the current vsync // event model. The fence need not be signaled at the time @@ -71,7 +96,7 @@ public: // // This method should be called with the retire fence from each HWComposer // set call that affects the display. - bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); + bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override; // The beginResync, addResyncSample, and endResync methods are used to re- // synchronize the DispSync's model to the hardware vsync events. The re- @@ -84,52 +109,64 @@ public: // is turned on (i.e. once immediately after it's turned on) and whenever // addPresentFence returns true indicating that the model has drifted away // from the hardware vsync events. - void beginResync(); - bool addResyncSample(nsecs_t timestamp); - void endResync(); + void beginResync() override; + bool addResyncSample(nsecs_t timestamp) override; + void endResync() override; // The setPeriod method sets the vsync event model's period to a specific // value. This should be used to prime the model when a display is first // turned on. It should NOT be used after that. - void setPeriod(nsecs_t period); + void setPeriod(nsecs_t period) override; // The getPeriod method returns the current vsync period. - nsecs_t getPeriod(); + nsecs_t getPeriod() override; // setRefreshSkipCount specifies an additional number of refresh // cycles to skip. For example, on a 60Hz display, a skip count of 1 // will result in events happening at 30Hz. Default is zero. The idea // is to sacrifice smoothness for battery life. - void setRefreshSkipCount(int count); + void setRefreshSkipCount(int count) override; // addEventListener registers a callback to be called repeatedly at the // given phase offset from the hardware vsync events. The callback is // called from a separate thread and it should return reasonably quickly // (i.e. within a few hundred microseconds). - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback); + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override; // removeEventListener removes an already-registered event callback. Once // this method returns that callback will no longer be called by the // DispSync object. - status_t removeEventListener(Callback* callback); + status_t removeEventListener(Callback* callback) override; // changePhaseOffset changes the phase offset of an already-registered event callback. The // method will make sure that there is no skipping or double-firing on the listener per frame, // even when changing the offsets multiple times. - status_t changePhaseOffset(Callback* callback, nsecs_t phase); + status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; // computeNextRefresh computes when the next refresh is expected to begin. // The periodOffset value can be used to move forward or backward; an // offset of zero is the next refresh, -1 is the previous refresh, 1 is // the refresh after next. etc. - nsecs_t computeNextRefresh(int periodOffset) const; + nsecs_t computeNextRefresh(int periodOffset) const override; + + // In certain situations the present fences aren't a good indicator of vsync + // time, e.g. when vr flinger is active, or simply aren't available, + // e.g. when the sync framework isn't present. Use this method to toggle + // whether or not DispSync ignores present fences. If present fences are + // ignored, DispSync will always ask for hardware vsync events by returning + // true from addPresentFence() and addResyncSample(). + void setIgnorePresentFences(bool ignore) override; + + // Determine the expected present time when a buffer acquired now will be displayed. + nsecs_t expectedPresentTime(); // dump appends human-readable debug info to the result string. - void dump(String8& result) const; + void dump(String8& result) const override; private: void updateModelLocked(); void updateErrorLocked(); + void resetLocked(); void resetErrorLocked(); enum { MAX_RESYNC_SAMPLES = 32 }; @@ -195,8 +232,13 @@ private: bool mIgnorePresentFences; std::unique_ptr<Callback> mZeroPhaseTracer; + + // Flag to turn on logging in systrace. + bool mTraceDetailedInfo = false; }; +} // namespace impl + } // namespace android #endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp new file mode 100644 index 0000000000..697d6344fa --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "DispSyncSource.h" + +#include <android-base/stringprintf.h> +#include <utils/Trace.h> +#include <mutex> + +#include "DispSync.h" +#include "EventThread.h" + +namespace android { + +DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, + const char* name) + : mName(name), + mTraceVsync(traceVsync), + mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), + mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)), + mDispSync(dispSync), + mPhaseOffset(phaseOffset) {} + +void DispSyncSource::setVSyncEnabled(bool enable) { + std::lock_guard lock(mVsyncMutex); + if (enable) { + status_t err = mDispSync->addEventListener(mName, mPhaseOffset, + static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err); + } + // ATRACE_INT(mVsyncOnLabel.c_str(), 1); + } else { + status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this)); + if (err != NO_ERROR) { + ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err); + } + // ATRACE_INT(mVsyncOnLabel.c_str(), 0); + } + mEnabled = enable; +} + +void DispSyncSource::setCallback(VSyncSource::Callback* callback) { + std::lock_guard lock(mCallbackMutex); + mCallback = callback; +} + +void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { + std::lock_guard lock(mVsyncMutex); + + // Normalize phaseOffset to [0, period) + auto period = mDispSync->getPeriod(); + phaseOffset %= period; + if (phaseOffset < 0) { + // If we're here, then phaseOffset is in (-period, 0). After this + // operation, it will be in (0, period) + phaseOffset += period; + } + mPhaseOffset = phaseOffset; + + // If we're not enabled, we don't need to mess with the listeners + if (!mEnabled) { + return; + } + + status_t err = + mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset); + if (err != NO_ERROR) { + ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err); + } +} + +void DispSyncSource::onDispSyncEvent(nsecs_t when) { + VSyncSource::Callback* callback; + { + std::lock_guard lock(mCallbackMutex); + callback = mCallback; + + if (mTraceVsync) { + mValue = (mValue + 1) % 2; + ATRACE_INT(mVsyncEventLabel.c_str(), mValue); + } + } + + if (callback != nullptr) { + callback->onVSyncEvent(when); + } +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h new file mode 100644 index 0000000000..0fd84a2321 --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <mutex> +#include <string> + +#include "DispSync.h" +#include "EventThread.h" + +namespace android { + +class DispSyncSource final : public VSyncSource, private DispSync::Callback { +public: + DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name); + + ~DispSyncSource() override = default; + + // The following methods are implementation of VSyncSource. + void setVSyncEnabled(bool enable) override; + void setCallback(VSyncSource::Callback* callback) override; + void setPhaseOffset(nsecs_t phaseOffset) override; + +private: + // The following method is the implementation of the DispSync::Callback. + virtual void onDispSyncEvent(nsecs_t when); + + const char* const mName; + int mValue = 0; + + const bool mTraceVsync; + const std::string mVsyncOnLabel; + const std::string mVsyncEventLabel; + + DispSync* mDispSync; + + std::mutex mCallbackMutex; + VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; + + std::mutex mVsyncMutex; + nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex); + bool mEnabled GUARDED_BY(mVsyncMutex) = false; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp index fb6cff5705..fb6cff5705 100644 --- a/services/surfaceflinger/EventControlThread.cpp +++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h index cafae53400..cafae53400 100644 --- a/services/surfaceflinger/EventControlThread.h +++ b/services/surfaceflinger/Scheduler/EventControlThread.h diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index bc271c8ec5..9bee9a3499 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -45,11 +45,27 @@ EventThread::~EventThread() = default; namespace impl { +EventThread::EventThread(std::unique_ptr<VSyncSource> src, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : EventThread(nullptr, std::move(src), resyncWithRateLimitCallback, interceptVSyncsCallback, + threadName) {} + EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : EventThread(src, nullptr, resyncWithRateLimitCallback, interceptVSyncsCallback, + threadName) {} + +EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) : mVSyncSource(src), + mVSyncSourceUnique(std::move(uniqueSrc)), mResyncWithRateLimitCallback(resyncWithRateLimitCallback), mInterceptVSyncsCallback(interceptVSyncsCallback) { + if (src == nullptr) { + mVSyncSource = mVSyncSourceUnique.get(); + } for (auto& event : mVSyncEvent) { event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; event.header.id = 0; @@ -95,13 +111,28 @@ sp<BnDisplayEventConnection> EventThread::createEventConnection() const { status_t EventThread::registerDisplayEventConnection( const sp<EventThread::Connection>& connection) { std::lock_guard<std::mutex> lock(mMutex); - mDisplayEventConnections.add(connection); + + // this should never happen + auto it = std::find(mDisplayEventConnections.cbegin(), + mDisplayEventConnections.cend(), connection); + if (it != mDisplayEventConnections.cend()) { + ALOGW("DisplayEventConnection %p already exists", connection.get()); + mCondition.notify_all(); + return ALREADY_EXISTS; + } + + mDisplayEventConnections.push_back(connection); mCondition.notify_all(); return NO_ERROR; } -void EventThread::removeDisplayEventConnectionLocked(const wp<EventThread::Connection>& connection) { - mDisplayEventConnections.remove(connection); +void EventThread::removeDisplayEventConnectionLocked( + const wp<EventThread::Connection>& connection) { + auto it = std::find(mDisplayEventConnections.cbegin(), + mDisplayEventConnections.cend(), connection); + if (it != mDisplayEventConnections.cend()) { + mDisplayEventConnections.erase(it); + } } void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection>& connection) { @@ -155,33 +186,28 @@ void EventThread::onVSyncEvent(nsecs_t timestamp) { mCondition.notify_all(); } -void EventThread::onHotplugReceived(int type, bool connected) { - ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES, - "received hotplug event for an invalid display (id=%d)", type); - +void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { std::lock_guard<std::mutex> lock(mMutex); - if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { - DisplayEventReceiver::Event event; - event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; - event.header.id = type; - event.header.timestamp = systemTime(); - event.hotplug.connected = connected; - mPendingEvents.add(event); - mCondition.notify_all(); - } + + DisplayEventReceiver::Event event; + event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; + event.header.id = displayType == DisplayType::Primary ? 0 : 1; + event.header.timestamp = systemTime(); + event.hotplug.connected = connected; + + mPendingEvents.push(event); + mCondition.notify_all(); } void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { std::unique_lock<std::mutex> lock(mMutex); while (mKeepRunning) { DisplayEventReceiver::Event event; - Vector<sp<EventThread::Connection> > signalConnections; + std::vector<sp<EventThread::Connection>> signalConnections; signalConnections = waitForEventLocked(&lock, &event); // dispatch events to listeners... - const size_t count = signalConnections.size(); - for (size_t i = 0; i < count; i++) { - const sp<Connection>& conn(signalConnections[i]); + for (const sp<Connection>& conn : signalConnections) { // now see if we still need to report this event status_t err = conn->postEvent(event); if (err == -EAGAIN || err == -EWOULDBLOCK) { @@ -196,7 +222,7 @@ void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { // handle any other error on the pipe as fatal. the only // reasonable thing to do is to clean-up this connection. // The most common error we'll get here is -EPIPE. - removeDisplayEventConnectionLocked(signalConnections[i]); + removeDisplayEventConnectionLocked(conn); } } } @@ -204,44 +230,44 @@ void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { // This will return when (1) a vsync event has been received, and (2) there was // at least one connection interested in receiving it when we started waiting. -Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( - std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) { - Vector<sp<EventThread::Connection> > signalConnections; +std::vector<sp<EventThread::Connection>> EventThread::waitForEventLocked( + std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) { + std::vector<sp<EventThread::Connection>> signalConnections; - while (signalConnections.isEmpty() && mKeepRunning) { + while (signalConnections.empty() && mKeepRunning) { bool eventPending = false; bool waitForVSync = false; size_t vsyncCount = 0; nsecs_t timestamp = 0; - for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) { - timestamp = mVSyncEvent[i].header.timestamp; + for (auto& event : mVSyncEvent) { + timestamp = event.header.timestamp; if (timestamp) { // we have a vsync event to dispatch if (mInterceptVSyncsCallback) { mInterceptVSyncsCallback(timestamp); } - *event = mVSyncEvent[i]; - mVSyncEvent[i].header.timestamp = 0; - vsyncCount = mVSyncEvent[i].vsync.count; + *outEvent = event; + event.header.timestamp = 0; + vsyncCount = event.vsync.count; break; } } if (!timestamp) { // no vsync event, see if there are some other event - eventPending = !mPendingEvents.isEmpty(); + eventPending = !mPendingEvents.empty(); if (eventPending) { // we have some other event to dispatch - *event = mPendingEvents[0]; - mPendingEvents.removeAt(0); + *outEvent = mPendingEvents.front(); + mPendingEvents.pop(); } } // find out connections waiting for events - size_t count = mDisplayEventConnections.size(); - for (size_t i = 0; i < count;) { - sp<Connection> connection(mDisplayEventConnections[i].promote()); + auto it = mDisplayEventConnections.begin(); + while (it != mDisplayEventConnections.end()) { + sp<Connection> connection(it->promote()); if (connection != nullptr) { bool added = false; if (connection->count >= 0) { @@ -254,12 +280,12 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( if (connection->count == 0) { // fired this time around connection->count = -1; - signalConnections.add(connection); + signalConnections.push_back(connection); added = true; } else if (connection->count == 1 || (vsyncCount % connection->count) == 0) { // continuous event, and time to report it - signalConnections.add(connection); + signalConnections.push_back(connection); added = true; } } @@ -269,14 +295,13 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( // we don't have a vsync event to process // (timestamp==0), but we have some pending // messages. - signalConnections.add(connection); + signalConnections.push_back(connection); } - ++i; + ++it; } else { // we couldn't promote this reference, the connection has // died, so clean-up! - mDisplayEventConnections.removeAt(i); - --count; + it = mDisplayEventConnections.erase(it); } } @@ -319,7 +344,7 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( // FIXME: how do we decide which display id the fake // vsync came from ? mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; - mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY; + mVSyncEvent[0].header.id = 0; mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); mVSyncEvent[0].vsync.count++; } @@ -364,13 +389,13 @@ void EventThread::dump(String8& result) const { result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n", - mDisplayEventConnections.size(), - mVSyncEvent[DisplayDevice::DISPLAY_PRIMARY].vsync.count); - for (size_t i = 0; i < mDisplayEventConnections.size(); i++) { - sp<Connection> connection = mDisplayEventConnections.itemAt(i).promote(); + mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); + for (const wp<Connection>& weak : mDisplayEventConnections) { + sp<Connection> connection = weak.promote(); result.appendFormat(" %p: count=%d\n", connection.get(), connection != nullptr ? connection->count : 0); } + result.appendFormat(" other-events-pending: %zu\n", mPendingEvents.size()); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 9c13ed2755..5e7ed13d92 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -16,11 +16,15 @@ #pragma once -#include <stdint.h> #include <sys/types.h> + +#include <array> #include <condition_variable> +#include <cstdint> #include <mutex> +#include <queue> #include <thread> +#include <vector> #include <android-base/thread_annotations.h> @@ -29,9 +33,6 @@ #include <private/gui/BitTube.h> #include <utils/Errors.h> -#include <utils/SortedVector.h> - -#include "DisplayDevice.h" // --------------------------------------------------------------------------- namespace android { @@ -59,6 +60,9 @@ public: class EventThread { public: + // TODO: Remove once stable display IDs are plumbed through SF/WM interface. + enum class DisplayType { Primary, External }; + virtual ~EventThread(); virtual sp<BnDisplayEventConnection> createEventConnection() const = 0; @@ -70,7 +74,7 @@ public: virtual void onScreenAcquired() = 0; // called when receiving a hotplug event - virtual void onHotplugReceived(int type, bool connected) = 0; + virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0; virtual void dump(String8& result) const = 0; @@ -105,12 +109,15 @@ public: using ResyncWithRateLimitCallback = std::function<void()>; using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete. EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + EventThread(std::unique_ptr<VSyncSource> src, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); ~EventThread(); sp<BnDisplayEventConnection> createEventConnection() const override; - status_t registerDisplayEventConnection(const sp<Connection>& connection); void setVsyncRate(uint32_t count, const sp<Connection>& connection); void requestNextVsync(const sp<Connection>& connection); @@ -122,7 +129,7 @@ public: void onScreenAcquired() override; // called when receiving a hotplug event - void onHotplugReceived(int type, bool connected) override; + void onHotplugReceived(DisplayType displayType, bool connected) override; void dump(String8& result) const override; @@ -131,9 +138,16 @@ public: private: friend EventThreadTest; + // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete. + EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + + status_t registerDisplayEventConnection(const sp<Connection>& connection); + void threadMain(); - Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, - DisplayEventReceiver::Event* event) + std::vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, + DisplayEventReceiver::Event* event) REQUIRES(mMutex); void removeDisplayEventConnectionLocked(const wp<Connection>& connection) REQUIRES(mMutex); @@ -143,8 +157,10 @@ private: // Implements VSyncSource::Callback void onVSyncEvent(nsecs_t timestamp) override; + // TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete. + VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr; + std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr; // constants - VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr; const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; const InterceptVSyncsCallback mInterceptVSyncsCallback; @@ -153,10 +169,9 @@ private: mutable std::condition_variable mCondition; // protected by mLock - SortedVector<wp<Connection>> mDisplayEventConnections GUARDED_BY(mMutex); - Vector<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); - DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES] GUARDED_BY( - mMutex); + std::vector<wp<Connection>> mDisplayEventConnections GUARDED_BY(mMutex); + std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); + std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex); bool mUseSoftwareVSync GUARDED_BY(mMutex) = false; bool mVsyncEnabled GUARDED_BY(mMutex) = false; bool mKeepRunning GUARDED_BY(mMutex) = true; diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h new file mode 100644 index 0000000000..a0e14478ab --- /dev/null +++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <mutex> + +#include "EventThread.h" + +namespace android { + +/** + * VSync signals used during SurfaceFlinger trace playback (traces we captured + * with SurfaceInterceptor). + */ +class InjectVSyncSource final : public VSyncSource { +public: + ~InjectVSyncSource() override = default; + + void setCallback(VSyncSource::Callback* callback) override { + std::lock_guard<std::mutex> lock(mCallbackMutex); + mCallback = callback; + } + + void onInjectSyncEvent(nsecs_t when) { + std::lock_guard<std::mutex> lock(mCallbackMutex); + if (mCallback) { + mCallback->onVSyncEvent(when); + } + } + + void setVSyncEnabled(bool) override {} + void setPhaseOffset(nsecs_t) override {} + +private: + std::mutex mCallbackMutex; + VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 056d381eb9..58355ae3a5 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -101,6 +101,17 @@ void MessageQueue::setEventThread(android::EventThread* eventThread) { this); } +void MessageQueue::setEventConnection(const sp<BnDisplayEventConnection>& connection) { + if (mEventTube.getFd() >= 0) { + mLooper->removeFd(mEventTube.getFd()); + } + + mEvents = connection; + mEvents->stealReceiveChannel(&mEventTube); + mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, + this); +} + void MessageQueue::waitMessage() { do { IPCThreadState::self()->flushCommands(); diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 90d1c72450..2ec697ea35 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -85,7 +85,9 @@ public: virtual ~MessageQueue(); virtual void init(const sp<SurfaceFlinger>& flinger) = 0; + // TODO(akrulec): Remove this function once everything is migrated to Scheduler. virtual void setEventThread(EventThread* events) = 0; + virtual void setEventConnection(const sp<BnDisplayEventConnection>& connection) = 0; virtual void waitMessage() = 0; virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0; virtual void invalidate() = 0; @@ -125,6 +127,7 @@ public: ~MessageQueue() override = default; void init(const sp<SurfaceFlinger>& flinger) override; void setEventThread(android::EventThread* events) override; + void setEventConnection(const sp<BnDisplayEventConnection>& connection) override; void waitMessage() override; status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp new file mode 100644 index 0000000000..18a8bb1578 --- /dev/null +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -0,0 +1,199 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Scheduler.h" + +#include <cinttypes> +#include <cstdint> +#include <memory> + +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> +#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> + +#include <gui/ISurfaceComposer.h> +#include <ui/DisplayStatInfo.h> + +#include "DispSync.h" +#include "DispSyncSource.h" +#include "EventControlThread.h" +#include "EventThread.h" +#include "InjectVSyncSource.h" + +namespace android { + +using namespace android::hardware::configstore; +using namespace android::hardware::configstore::V1_0; + +#define RETURN_VALUE_IF_INVALID(value) \ + if (handle == nullptr || mConnections.count(handle->id) == 0) return value +#define RETURN_IF_INVALID() \ + if (handle == nullptr || mConnections.count(handle->id) == 0) return + +std::atomic<int64_t> Scheduler::sNextId = 0; + +Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function) + : mHasSyncFramework( + getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(true)), + mDispSyncPresentTimeOffset( + getInt64<ISurfaceFlingerConfigs, + &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0)), + mPrimaryHWVsyncEnabled(false), + mHWVsyncAvailable(false) { + // Note: We create a local temporary with the real DispSync implementation + // type temporarily so we can initialize it with the configured values, + // before storing it for more generic use using the interface type. + auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync"); + primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset); + mPrimaryDispSync = std::move(primaryDispSync); + mEventControlThread = std::make_unique<impl::EventControlThread>(function); +} + +Scheduler::~Scheduler() = default; + +sp<Scheduler::ConnectionHandle> Scheduler::createConnection( + const std::string& connectionName, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback) { + const int64_t id = sNextId++; + ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id); + + std::unique_ptr<EventThread> eventThread = + makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs, resyncCallback, + interceptCallback); + auto connection = std::make_unique<Connection>(new ConnectionHandle(id), + eventThread->createEventConnection(), + std::move(eventThread)); + mConnections.insert(std::make_pair(id, std::move(connection))); + return mConnections[id]->handle; +} + +std::unique_ptr<EventThread> Scheduler::makeEventThread( + const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback) { + const std::string sourceName = connectionName + "Source"; + std::unique_ptr<VSyncSource> eventThreadSource = + std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, sourceName.c_str()); + const std::string threadName = connectionName + "Thread"; + return std::make_unique<impl::EventThread>(std::move(eventThreadSource), resyncCallback, + interceptCallback, threadName.c_str()); +} + +sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( + const sp<Scheduler::ConnectionHandle>& handle) { + RETURN_VALUE_IF_INVALID(nullptr); + return mConnections[handle->id]->thread->createEventConnection(); +} + +EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) { + RETURN_VALUE_IF_INVALID(nullptr); + return mConnections[handle->id]->thread.get(); +} + +sp<BnDisplayEventConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) { + RETURN_VALUE_IF_INVALID(nullptr); + return mConnections[handle->id]->eventConnection; +} + +void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle, + EventThread::DisplayType displayType, bool connected) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->onHotplugReceived(displayType, connected); +} + +void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->onScreenAcquired(); +} + +void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->onScreenReleased(); +} + +void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, String8& result) const { + RETURN_IF_INVALID(); + mConnections.at(handle->id)->thread->dump(result); +} + +void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->setPhaseOffset(phaseOffset); +} + +void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) { + stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0); + stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); +} + +void Scheduler::enableHardwareVsync() { + std::lock_guard<std::mutex> lock(mHWVsyncLock); + if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { + mPrimaryDispSync->beginResync(); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } +} + +void Scheduler::disableHardwareVsync(bool makeUnavailable) { + std::lock_guard<std::mutex> lock(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + mEventControlThread->setVsyncEnabled(false); + mPrimaryDispSync->endResync(); + mPrimaryHWVsyncEnabled = false; + } + if (makeUnavailable) { + mHWVsyncAvailable = false; + } +} + +void Scheduler::setVsyncPeriod(const nsecs_t period) { + mPrimaryDispSync->reset(); + mPrimaryDispSync->setPeriod(period); + enableHardwareVsync(); +} + +void Scheduler::addResyncSample(const nsecs_t timestamp) { + bool needsHwVsync = false; + { // Scope for the lock + std::lock_guard<std::mutex> lock(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } +} + +void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { + if (mPrimaryDispSync->addPresentFence(fenceTime)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } +} + +void Scheduler::setIgnorePresentFences(bool ignore) { + mPrimaryDispSync->setIgnorePresentFences(ignore); +} + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h new file mode 100644 index 0000000000..fdafe58784 --- /dev/null +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -0,0 +1,138 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <memory> + +#include <gui/ISurfaceComposer.h> +#include <ui/DisplayStatInfo.h> + +#include "DispSync.h" +#include "EventControlThread.h" +#include "EventThread.h" +#include "InjectVSyncSource.h" + +namespace android { + +class EventControlThread; + +class Scheduler { +public: + // Enum to indicate whether to start the transaction early, or at vsync time. + enum class TransactionStart { EARLY, NORMAL }; + + /* The scheduler handle is a BBinder object passed to the client from which we can extract + * an ID for subsequent operations. + */ + class ConnectionHandle : public BBinder { + public: + ConnectionHandle(int64_t id) : id(id) {} + + ~ConnectionHandle() = default; + + const int64_t id; + }; + + class Connection { + public: + Connection(sp<ConnectionHandle> handle, sp<BnDisplayEventConnection> eventConnection, + std::unique_ptr<EventThread> eventThread) + : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {} + + ~Connection() = default; + + sp<ConnectionHandle> handle; + sp<BnDisplayEventConnection> eventConnection; + const std::unique_ptr<EventThread> thread; + }; + + explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function); + + virtual ~Scheduler(); + + /** Creates an EventThread connection. */ + sp<ConnectionHandle> createConnection( + const std::string& connectionName, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback); + + sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle); + + // Getter methods. + EventThread* getEventThread(const sp<ConnectionHandle>& handle); + + sp<BnDisplayEventConnection> getEventConnection(const sp<ConnectionHandle>& handle); + + // Should be called when receiving a hotplug event. + void hotplugReceived(const sp<ConnectionHandle>& handle, EventThread::DisplayType displayType, + bool connected); + + // Should be called after the screen is turned on. + void onScreenAcquired(const sp<ConnectionHandle>& handle); + + // Should be called before the screen is turned off. + void onScreenReleased(const sp<ConnectionHandle>& handle); + + // Should be called when dumpsys command is received. + void dump(const sp<ConnectionHandle>& handle, String8& result) const; + + // Offers ability to modify phase offset in the event thread. + void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset); + + void getDisplayStatInfo(DisplayStatInfo* stats); + + void enableHardwareVsync(); + void disableHardwareVsync(bool makeUnavailable); + void setVsyncPeriod(const nsecs_t period); + void addResyncSample(const nsecs_t timestamp); + void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); + void setIgnorePresentFences(bool ignore); + +protected: + virtual std::unique_ptr<EventThread> makeEventThread( + const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback); + +private: + // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it + // should make request to Scheduler to compute next refresh. + friend class BufferQueueLayer; + + // If fences from sync Framework are supported. + const bool mHasSyncFramework; + + // The offset in nanoseconds to use, when DispSync timestamps present fence + // signaling time. + const nsecs_t mDispSyncPresentTimeOffset; + + // Each connection has it's own ID. This variable keeps track of the count. + static std::atomic<int64_t> sNextId; + + // Connections are stored in a map <connection ID, connection> for easy retrieval. + std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections; + + std::mutex mHWVsyncLock; + bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock); + bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock); + + std::unique_ptr<DispSync> mPrimaryDispSync; + std::unique_ptr<EventControlThread> mEventControlThread; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h index e071a599c9..dde0c57f72 100644 --- a/services/surfaceflinger/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -29,23 +29,16 @@ namespace android { */ class VSyncModulator { private: - // Number of frames we'll keep the early phase offsets once they are activated. This acts as a // low-pass filter in case the client isn't quick enough in sending new transactions. const int MIN_EARLY_FRAME_COUNT = 2; public: - struct Offsets { nsecs_t sf; nsecs_t app; }; - enum TransactionStart { - EARLY, - NORMAL - }; - // Sets the phase offsets // // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction @@ -63,27 +56,31 @@ public: mOffsets = late; } - Offsets getEarlyOffsets() const { - return mEarlyOffsets; - } + Offsets getEarlyOffsets() const { return mEarlyOffsets; } - Offsets getEarlyGlOffsets() const { - return mEarlyGlOffsets; - } + Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; } void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { mSfEventThread = sfEventThread; mAppEventThread = appEventThread; } - void setTransactionStart(TransactionStart transactionStart) { + void setSchedulerAndHandles(Scheduler* scheduler, + Scheduler::ConnectionHandle* appConnectionHandle, + Scheduler::ConnectionHandle* sfConnectionHandle) { + mScheduler = scheduler; + mAppConnectionHandle = appConnectionHandle; + mSfConnectionHandle = sfConnectionHandle; + } - if (transactionStart == TransactionStart::EARLY) { + void setTransactionStart(Scheduler::TransactionStart transactionStart) { + if (transactionStart == Scheduler::TransactionStart::EARLY) { mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; } // An early transaction stays an early transaction. - if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) { + if (transactionStart == mTransactionStart || + mTransactionStart == Scheduler::TransactionStart::EARLY) { return; } mTransactionStart = transactionStart; @@ -91,8 +88,8 @@ public: } void onTransactionHandled() { - if (mTransactionStart == TransactionStart::NORMAL) return; - mTransactionStart = TransactionStart::NORMAL; + if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return; + mTransactionStart = Scheduler::TransactionStart::NORMAL; updateOffsets(); } @@ -112,18 +109,25 @@ public: } private: - void updateOffsets() { const Offsets desired = getOffsets(); const Offsets current = mOffsets; bool changed = false; if (desired.sf != current.sf) { - mSfEventThread->setPhaseOffset(desired.sf); + if (mSfConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); + } else { + mSfEventThread->setPhaseOffset(desired.sf); + } changed = true; } if (desired.app != current.app) { - mAppEventThread->setPhaseOffset(desired.app); + if (mSfConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); + } else { + mAppEventThread->setPhaseOffset(desired.app); + } changed = true; } @@ -133,7 +137,8 @@ private: } Offsets getOffsets() { - if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) { + if (mTransactionStart == Scheduler::TransactionStart::EARLY || + mRemainingEarlyFrameCount > 0) { return mEarlyOffsets; } else if (mLastFrameUsedRenderEngine) { return mEarlyGlOffsets; @@ -149,9 +154,14 @@ private: EventThread* mSfEventThread = nullptr; EventThread* mAppEventThread = nullptr; + Scheduler* mScheduler = nullptr; + Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr; + Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr; + std::atomic<Offsets> mOffsets; - std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL; + std::atomic<Scheduler::TransactionStart> mTransactionStart = + Scheduler::TransactionStart::NORMAL; std::atomic<bool> mLastFrameUsedRenderEngine = false; std::atomic<int> mRemainingEarlyFrameCount = 0; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 11e7ff0f68..5c31ada5ef 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -47,7 +47,7 @@ #include <gui/IDisplayEventConnection.h> #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> - +#include <renderengine/RenderEngine.h> #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> #include <ui/UiConfig.h> @@ -63,33 +63,43 @@ #include <private/gui/SyncFeatures.h> #include "BufferLayer.h" +#include "BufferQueueLayer.h" +#include "BufferStateLayer.h" #include "Client.h" #include "ColorLayer.h" #include "Colorizer.h" #include "ContainerLayer.h" #include "DdmConnection.h" -#include "DispSync.h" #include "DisplayDevice.h" -#include "EventControlThread.h" -#include "EventThread.h" #include "Layer.h" #include "LayerVector.h" #include "MonitoredProducer.h" +#include "NativeWindowSurface.h" +#include "StartPropertySetThread.h" #include "SurfaceFlinger.h" -#include "clz.h" +#include "SurfaceInterceptor.h" #include "DisplayHardware/ComposerHal.h" +#include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" - #include "Effects/Daltonizer.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/DispSyncSource.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/InjectVSyncSource.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/Scheduler.h" -#include "RenderEngine/RenderEngine.h" #include <cutils/compiler.h> +#include "android-base/stringprintf.h" + #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> +#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> #include <configstore/Utils.h> @@ -97,12 +107,6 @@ #define DISPLAY_COUNT 1 -/* - * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all - * black pixels. - */ -#define DEBUG_SCREENSHOTS false - namespace android { using namespace android::hardware::configstore; @@ -113,6 +117,49 @@ using ui::Hdr; using ui::RenderIntent; namespace { + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wswitch-enum" + +bool isWideColorMode(const ColorMode colorMode) { + switch (colorMode) { + case ColorMode::DISPLAY_P3: + case ColorMode::ADOBE_RGB: + case ColorMode::DCI_P3: + case ColorMode::BT2020: + case ColorMode::DISPLAY_BT2020: + case ColorMode::BT2100_PQ: + case ColorMode::BT2100_HLG: + return true; + case ColorMode::NATIVE: + case ColorMode::STANDARD_BT601_625: + case ColorMode::STANDARD_BT601_625_UNADJUSTED: + case ColorMode::STANDARD_BT601_525: + case ColorMode::STANDARD_BT601_525_UNADJUSTED: + case ColorMode::STANDARD_BT709: + case ColorMode::SRGB: + return false; + } + return false; +} + +ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) { + switch (rotation) { + case ISurfaceComposer::eRotateNone: + return ui::Transform::ROT_0; + case ISurfaceComposer::eRotate90: + return ui::Transform::ROT_90; + case ISurfaceComposer::eRotate180: + return ui::Transform::ROT_180; + case ISurfaceComposer::eRotate270: + return ui::Transform::ROT_270; + } + ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation); + return ui::Transform::ROT_0; +} + +#pragma clang diagnostic pop + class ConditionalLock { public: ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) { @@ -125,6 +172,7 @@ private: Mutex& mMutex; bool mLocked; }; + } // namespace anonymous // --------------------------------------------------------------------------- @@ -145,7 +193,13 @@ bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; // TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning. bool SurfaceFlinger::hasWideColorDisplay; - +int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault; +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; std::string getHwcServiceName() { char value[PROPERTY_VALUE_MAX] = {}; @@ -175,32 +229,6 @@ std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { } } -NativeWindowSurface::~NativeWindowSurface() = default; - -namespace impl { - -class NativeWindowSurface final : public android::NativeWindowSurface { -public: - static std::unique_ptr<android::NativeWindowSurface> create( - const sp<IGraphicBufferProducer>& producer) { - return std::make_unique<NativeWindowSurface>(producer); - } - - explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer) - : surface(new Surface(producer, false)) {} - - ~NativeWindowSurface() override = default; - -private: - sp<ANativeWindow> getNativeWindow() const override { return surface; } - - void preallocateBuffers() override { surface->allocateBuffers(); } - - sp<Surface> surface; -}; - -} // namespace impl - SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()), mRenderEngine(nullptr), @@ -210,16 +238,16 @@ SurfaceFlingerBE::SurfaceFlingerBE() mComposerSequenceId(0) { } -SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag) +SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory, + SurfaceFlinger::SkipInitializationTag) : BnSurfaceComposer(), - mTransactionFlags(0), + mFactory(factory), mTransactionPending(false), mAnimTransactionPending(false), mLayersRemoved(false), mLayersAdded(false), - mRepaintEverything(0), mBootTime(systemTime()), - mBuiltinDisplays(), + mDisplayTokens(), mVisibleRegionsDirty(false), mGeometryInvalid(false), mAnimCompositionPending(false), @@ -228,22 +256,19 @@ SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag) mDebugDDMS(0), mDebugDisableHWC(0), mDebugDisableTransformHint(0), - mDebugInSwapBuffers(0), - mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), mForceFullDamage(false), - mPrimaryDispSync("PrimaryDispSync"), mPrimaryHWVsyncEnabled(false), mHWVsyncAvailable(false), + mRefreshStartTime(0), mHasPoweredOff(false), mNumLayers(0), mVrFlingerRequestsDisplay(false), - mMainThreadId(std::this_thread::get_id()), - mCreateBufferQueue(&BufferQueue::createBufferQueue), - mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {} + mMainThreadId(std::this_thread::get_id()) {} -SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { +SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory) + : SurfaceFlinger(factory, SkipInitialization) { ALOGI("SurfaceFlinger is starting"); vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs, @@ -273,28 +298,49 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { hasWideColorDisplay = getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); + useColorManagement = + getBool<V1_2::ISurfaceFlingerConfigs, + &V1_2::ISurfaceFlingerConfigs::useColorManagement>(false); + + auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); + if (surfaceFlingerConfigsServiceV1_2) { + surfaceFlingerConfigsServiceV1_2->getCompositionPreference( + [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat, + auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) { + defaultCompositionDataspace = tmpDefaultDataspace; + defaultCompositionPixelFormat = tmpDefaultPixelFormat; + wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace; + wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat; + }); + } + + useContextPriority = getBool<ISurfaceFlingerConfigs, + &ISurfaceFlingerConfigs::useContextPriority>(true); V1_1::DisplayOrientation primaryDisplayOrientation = - getDisplayOrientation< V1_1::ISurfaceFlingerConfigs, &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>( + getDisplayOrientation<V1_1::ISurfaceFlingerConfigs, + &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>( V1_1::DisplayOrientation::ORIENTATION_0); switch (primaryDisplayOrientation) { case V1_1::DisplayOrientation::ORIENTATION_90: - mPrimaryDisplayOrientation = DisplayState::eOrientation90; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90; break; case V1_1::DisplayOrientation::ORIENTATION_180: - mPrimaryDisplayOrientation = DisplayState::eOrientation180; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180; break; case V1_1::DisplayOrientation::ORIENTATION_270: - mPrimaryDisplayOrientation = DisplayState::eOrientation270; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270; break; default: - mPrimaryDisplayOrientation = DisplayState::eOrientationDefault; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault; break; } - ALOGV("Primary Display Orientation is set to %2d.", mPrimaryDisplayOrientation); + ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation); - mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset); + mPrimaryDispSync = + getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework, + SurfaceFlinger::dispSyncPresentTimeOffset); // debugging stuff... char value[PROPERTY_VALUE_MAX]; @@ -322,7 +368,7 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { property_get("debug.sf.enable_hwc_vds", value, "0"); mUseHwcVirtualDisplays = atoi(value); - ALOGI_IF(!mUseHwcVirtualDisplays, "Enabling HWC virtual displays"); + ALOGI_IF(mUseHwcVirtualDisplays, "Enabling HWC virtual displays"); property_get("ro.sf.disable_triple_buffer", value, "1"); mLayerTripleBufferingDisabled = atoi(value); @@ -344,6 +390,9 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1"); const int earlyGlAppOffsetNs = atoi(value); + property_get("debug.sf.use_scheduler", value, "0"); + mUseScheduler = atoi(value); + const VSyncModulator::Offsets earlyOffsets = {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs, earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs}; @@ -433,28 +482,30 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, sp<BBinder> token = new DisplayToken(this); Mutex::Autolock _l(mStateLock); - DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL, secure); + DisplayDeviceState info; + info.type = DisplayDevice::DISPLAY_VIRTUAL; info.displayName = displayName; + info.isSecure = secure; mCurrentState.displays.add(token, info); mInterceptor->saveDisplayCreation(info); return token; } -void SurfaceFlinger::destroyDisplay(const sp<IBinder>& display) { +void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) { Mutex::Autolock _l(mStateLock); - ssize_t idx = mCurrentState.displays.indexOfKey(display); + ssize_t idx = mCurrentState.displays.indexOfKey(displayToken); if (idx < 0) { - ALOGW("destroyDisplay: invalid display token"); + ALOGE("destroyDisplay: Invalid display token %p", displayToken.get()); return; } const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx)); - if (!info.isVirtualDisplay()) { + if (!info.isVirtual()) { ALOGE("destroyDisplay called for non-virtual display"); return; } - mInterceptor->saveDisplayDeletion(info.displayId); + mInterceptor->saveDisplayDeletion(info.sequenceId); mCurrentState.displays.removeItemsAt(idx); setTransactionFlags(eDisplayTransactionNeeded); } @@ -464,7 +515,11 @@ sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) { ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id); return nullptr; } - return mBuiltinDisplays[id]; + return mDisplayTokens[id]; +} + +bool SurfaceFlinger::isColorManagementUsed() const { + return useColorManagement; } void SurfaceFlinger::bootFinished() @@ -496,11 +551,10 @@ void SurfaceFlinger::bootFinished() LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - sp<LambdaMessage> readProperties = new LambdaMessage([&]() { + postMessageAsync(new LambdaMessage([this] { readPersistentProperties(); mBootStage = BootStage::FINISHED; - }); - postMessageAsync(readProperties); + })); } uint32_t SurfaceFlinger::getNewTexture() { @@ -525,151 +579,9 @@ uint32_t SurfaceFlinger::getNewTexture() { } void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { - class MessageDestroyGLTexture : public MessageBase { - RE::RenderEngine& engine; - uint32_t texture; - public: - MessageDestroyGLTexture(RE::RenderEngine& engine, uint32_t texture) - : engine(engine), texture(texture) {} - virtual bool handler() { - engine.deleteTextures(1, &texture); - return true; - } - }; - postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture)); + postMessageAsync(new LambdaMessage([=] { getRenderEngine().deleteTextures(1, &texture); })); } -class DispSyncSource final : public VSyncSource, private DispSync::Callback { -public: - DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, - const char* name) : - mName(name), - mValue(0), - mTraceVsync(traceVsync), - mVsyncOnLabel(String8::format("VsyncOn-%s", name)), - mVsyncEventLabel(String8::format("VSYNC-%s", name)), - mDispSync(dispSync), - mCallbackMutex(), - mVsyncMutex(), - mPhaseOffset(phaseOffset), - mEnabled(false) {} - - ~DispSyncSource() override = default; - - void setVSyncEnabled(bool enable) override { - Mutex::Autolock lock(mVsyncMutex); - if (enable) { - status_t err = mDispSync->addEventListener(mName, mPhaseOffset, - static_cast<DispSync::Callback*>(this)); - if (err != NO_ERROR) { - ALOGE("error registering vsync callback: %s (%d)", - strerror(-err), err); - } - //ATRACE_INT(mVsyncOnLabel.string(), 1); - } else { - status_t err = mDispSync->removeEventListener( - static_cast<DispSync::Callback*>(this)); - if (err != NO_ERROR) { - ALOGE("error unregistering vsync callback: %s (%d)", - strerror(-err), err); - } - //ATRACE_INT(mVsyncOnLabel.string(), 0); - } - mEnabled = enable; - } - - void setCallback(VSyncSource::Callback* callback) override{ - Mutex::Autolock lock(mCallbackMutex); - mCallback = callback; - } - - void setPhaseOffset(nsecs_t phaseOffset) override { - Mutex::Autolock lock(mVsyncMutex); - - // Normalize phaseOffset to [0, period) - auto period = mDispSync->getPeriod(); - phaseOffset %= period; - if (phaseOffset < 0) { - // If we're here, then phaseOffset is in (-period, 0). After this - // operation, it will be in (0, period) - phaseOffset += period; - } - mPhaseOffset = phaseOffset; - - // If we're not enabled, we don't need to mess with the listeners - if (!mEnabled) { - return; - } - - status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), - mPhaseOffset); - if (err != NO_ERROR) { - ALOGE("error changing vsync offset: %s (%d)", - strerror(-err), err); - } - } - -private: - virtual void onDispSyncEvent(nsecs_t when) { - VSyncSource::Callback* callback; - { - Mutex::Autolock lock(mCallbackMutex); - callback = mCallback; - - if (mTraceVsync) { - mValue = (mValue + 1) % 2; - ATRACE_INT(mVsyncEventLabel.string(), mValue); - } - } - - if (callback != nullptr) { - callback->onVSyncEvent(when); - } - } - - const char* const mName; - - int mValue; - - const bool mTraceVsync; - const String8 mVsyncOnLabel; - const String8 mVsyncEventLabel; - - DispSync* mDispSync; - - Mutex mCallbackMutex; // Protects the following - VSyncSource::Callback* mCallback = nullptr; - - Mutex mVsyncMutex; // Protects the following - nsecs_t mPhaseOffset; - bool mEnabled; -}; - -class InjectVSyncSource final : public VSyncSource { -public: - InjectVSyncSource() = default; - ~InjectVSyncSource() override = default; - - void setCallback(VSyncSource::Callback* callback) override { - std::lock_guard<std::mutex> lock(mCallbackMutex); - mCallback = callback; - } - - void onInjectSyncEvent(nsecs_t when) { - std::lock_guard<std::mutex> lock(mCallbackMutex); - if (mCallback) { - mCallback->onVSyncEvent(when); - } - } - - void setVSyncEnabled(bool) override {} - void setPhaseOffset(nsecs_t) override {} - -private: - std::mutex mCallbackMutex; // Protects the following - VSyncSource::Callback* mCallback = nullptr; -}; - // Do not call property_set on main thread which will be blocked by init // Use StartPropertySetThread instead. void SurfaceFlinger::init() { @@ -681,73 +593,100 @@ void SurfaceFlinger::init() { Mutex::Autolock _l(mStateLock); // start the EventThread - mEventThreadSource = - std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs, - true, "app"); - mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(), - [this]() { resyncWithRateLimit(); }, - impl::EventThread::InterceptVSyncsCallback(), - "appEventThread"); - mSfEventThreadSource = - std::make_unique<DispSyncSource>(&mPrimaryDispSync, - SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf"); - - mSFEventThread = - std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), - [this]() { resyncWithRateLimit(); }, - [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }, - "sfEventThread"); - mEventQueue->setEventThread(mSFEventThread.get()); - mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get()); + if (mUseScheduler) { + mScheduler = getFactory().createScheduler( + [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); }); + mAppConnectionHandle = + mScheduler->createConnection("appConnection", SurfaceFlinger::vsyncPhaseOffsetNs, + [this] { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = + mScheduler->createConnection("sfConnection", SurfaceFlinger::sfVsyncPhaseOffsetNs, + [this] { resyncWithRateLimit(); }, + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), + mSfConnectionHandle.get()); + } else { + mEventThreadSource = + std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), + SurfaceFlinger::vsyncPhaseOffsetNs, true, "app"); + mEventThread = + std::make_unique<impl::EventThread>(mEventThreadSource.get(), + [this] { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), + "appEventThread"); + mSfEventThreadSource = + std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), + SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf"); + + mSFEventThread = + std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), + [this] { resyncWithRateLimit(); }, + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }, + "sfEventThread"); + mEventQueue->setEventThread(mSFEventThread.get()); + mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get()); + } // Get a RenderEngine for the given display / config (can't fail) + int32_t renderEngineFeature = 0; + renderEngineFeature |= (useColorManagement ? + renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0); + renderEngineFeature |= (useContextPriority ? + renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0); + + // TODO(b/77156734): We need to stop casting and use HAL types when possible. getBE().mRenderEngine = - RE::impl::RenderEngine::create(HAL_PIXEL_FORMAT_RGBA_8888, - hasWideColorDisplay - ? RE::RenderEngine::WIDE_COLOR_SUPPORT - : 0); + renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat), + renderEngineFeature); LOG_ALWAYS_FATAL_IF(getBE().mRenderEngine == nullptr, "couldn't create RenderEngine"); LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, "Starting with vr flinger active is not currently supported."); - getBE().mHwc.reset( - new HWComposer(std::make_unique<Hwc2::impl::Composer>(getBE().mHwcServiceName))); + getBE().mHwc = getFactory().createHWComposer(getBE().mHwcServiceName); getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId); // Process any initial hotplug and resulting display changes. processDisplayHotplugEventsLocked(); - LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY), - "Registered composer callback but didn't create the default primary display"); + const auto display = getDefaultDisplayDeviceLocked(); + LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback."); + LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(display->getId()), + "Internal display is disconnected."); // make the default display GLContext current so that we can create textures // when creating Layers (which may happens before we render something) - getDefaultDisplayDeviceLocked()->makeCurrent(); + display->makeCurrent(); if (useVrFlinger) { - auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) { + auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) { // This callback is called from the vr flinger dispatch thread. We // need to call signalTransaction(), which requires holding // mStateLock when we're not on the main thread. Acquiring // mStateLock from the vr flinger dispatch thread might trigger a // deadlock in surface flinger (see b/66916578), so post a message // to be handled on the main thread instead. - sp<LambdaMessage> message = new LambdaMessage([=]() { + postMessageAsync(new LambdaMessage([=] { ALOGI("VR request display mode: requestDisplay=%d", requestDisplay); mVrFlingerRequestsDisplay = requestDisplay; signalTransaction(); - }); - postMessageAsync(message); + })); }; - mVrFlinger = dvr::VrFlinger::Create(getBE().mHwc->getComposer(), - getBE().mHwc->getHwcDisplayId(HWC_DISPLAY_PRIMARY).value_or(0), - vrFlingerRequestDisplayCallback); + mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(), + getHwComposer() + .getHwcDisplayId(display->getId()) + .value_or(0), + vrFlingerRequestDisplayCallback); if (!mVrFlinger) { ALOGE("Failed to start vrflinger"); } } - mEventControlThread = std::make_unique<impl::EventControlThread>( + mEventControlThread = getFactory().createEventControlThread( [this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); }); // initialize our drawing state @@ -759,12 +698,10 @@ void SurfaceFlinger::init() { getBE().mRenderEngine->primeCache(); // Inform native graphics APIs whether the present timestamp is supported: - if (getHwComposer().hasCapability( - HWC2::Capability::PresentFenceIsNotReliable)) { - mStartPropertySetThread = new StartPropertySetThread(false); - } else { - mStartPropertySetThread = new StartPropertySetThread(true); - } + + const bool presentFenceReliable = + !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable); + mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable); if (mStartPropertySetThread->Start() != NO_ERROR) { ALOGE("Run StartPropertySetThread failed!"); @@ -776,7 +713,7 @@ void SurfaceFlinger::init() { // and apply this saturation matrix on Display P3 content. Unless the risk of applying // such saturation matrix on Display P3 is understood fully, the API should always return // identify matrix. - mEnhancedSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY, + mEnhancedSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(display->getId(), Dataspace::SRGB_LINEAR); // we will apply this on Display P3. @@ -858,18 +795,15 @@ status_t SurfaceFlinger::getSupportedFrameTimestamps( return NO_ERROR; } -status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, - Vector<DisplayInfo>* configs) { - if (configs == nullptr || display.get() == nullptr) { +status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, + Vector<DisplayInfo>* configs) { + if (!displayToken || !configs) { return BAD_VALUE; } - if (!display.get()) - return NAME_NOT_FOUND; - int32_t type = NAME_NOT_FOUND; - for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { - if (display == mBuiltinDisplays[i]) { + for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) { + if (displayToken == mDisplayTokens[i]) { type = i; break; } @@ -881,18 +815,18 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, // TODO: Not sure if display density should handled by SF any longer class Density { - static int getDensityFromProperty(char const* propName) { + static float getDensityFromProperty(char const* propName) { char property[PROPERTY_VALUE_MAX]; - int density = 0; + float density = 0.0f; if (property_get(propName, property, nullptr) > 0) { - density = atoi(property); + density = strtof(property, nullptr); } return density; } public: - static int getEmuDensity() { + static float getEmuDensity() { return getDensityFromProperty("qemu.sf.lcd_density"); } - static int getBuildDensity() { + static float getBuildDensity() { return getDensityFromProperty("ro.sf.lcd_density"); } }; @@ -906,6 +840,12 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, float xdpi = hwConfig->getDpiX(); float ydpi = hwConfig->getDpiY(); + info.w = hwConfig->getWidth(); + info.h = hwConfig->getHeight(); + // Default display viewport to display width and height + info.viewportW = info.w; + info.viewportH = info.h; + if (type == DisplayDevice::DISPLAY_PRIMARY) { // The density of the device is provided by a build property float density = Density::getBuildDensity() / 160.0f; @@ -923,8 +863,15 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.density = density; // TODO: this needs to go away (currently needed only by webkit) - sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); - info.orientation = hw ? hw->getOrientation() : 0; + const auto display = getDefaultDisplayDeviceLocked(); + info.orientation = display ? display->getOrientation() : 0; + + // This is for screenrecord + const Rect viewport = display->getViewport(); + if (viewport.isValid()) { + info.viewportW = uint32_t(viewport.getWidth()); + info.viewportH = uint32_t(viewport.getHeight()); + } } else { // TODO: where should this value come from? static const int TV_DENSITY = 213; @@ -932,8 +879,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.orientation = 0; } - info.w = hwConfig->getWidth(); - info.h = hwConfig->getHeight(); info.xdpi = xdpi; info.ydpi = ydpi; info.fps = 1e9 / hwConfig->getVsyncPeriod(); @@ -958,7 +903,7 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.secure = true; if (type == DisplayDevice::DISPLAY_PRIMARY && - mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) { + primaryDisplayOrientation & DisplayState::eOrientationSwapMask) { std::swap(info.w, info.h); } @@ -968,115 +913,77 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, return NO_ERROR; } -status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */, - DisplayStatInfo* stats) { - if (stats == nullptr) { - return BAD_VALUE; - } - - // FIXME for now we always return stats for the primary display - memset(stats, 0, sizeof(*stats)); - stats->vsyncTime = mPrimaryDispSync.computeNextRefresh(0); - stats->vsyncPeriod = mPrimaryDispSync.getPeriod(); - return NO_ERROR; -} - -status_t SurfaceFlinger::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - if (outViewport == nullptr || display.get() == nullptr) { +status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) { + if (!stats) { return BAD_VALUE; } - sp<const DisplayDevice> device(getDisplayDevice(display)); - if (device == nullptr) { - return BAD_VALUE; + // FIXME for now we always return stats for the primary display. + if (mUseScheduler) { + mScheduler->getDisplayStatInfo(stats); + } else { + stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0); + stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); } - - *outViewport = device->getViewport(); - return NO_ERROR; } -int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) { - if (display == nullptr) { - ALOGE("%s : display is nullptr", __func__); +int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) { + const auto display = getDisplayDevice(displayToken); + if (!display) { + ALOGE("getActiveConfig: Invalid display token %p", displayToken.get()); return BAD_VALUE; } - sp<const DisplayDevice> device(getDisplayDevice(display)); - if (device != nullptr) { - return device->getActiveConfig(); - } - - return BAD_VALUE; + return display->getActiveConfig(); } -void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) { - ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), - this); - int32_t type = hw->getDisplayType(); - int currentMode = hw->getActiveConfig(); - +void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& display, int mode) { + int currentMode = display->getActiveConfig(); if (mode == currentMode) { - ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode); return; } - if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + if (display->isVirtual()) { ALOGW("Trying to set config for virtual display"); return; } - hw->setActiveConfig(mode); - getHwComposer().setActiveConfig(type, mode); + display->setActiveConfig(mode); + getHwComposer().setActiveConfig(display->getDisplayType(), mode); } -status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) { - class MessageSetActiveConfig: public MessageBase { - SurfaceFlinger& mFlinger; - sp<IBinder> mDisplay; - int mMode; - public: - MessageSetActiveConfig(SurfaceFlinger& flinger, const sp<IBinder>& disp, - int mode) : - mFlinger(flinger), mDisplay(disp) { mMode = mode; } - virtual bool handler() { - Vector<DisplayInfo> configs; - mFlinger.getDisplayConfigs(mDisplay, &configs); - if (mMode < 0 || mMode >= static_cast<int>(configs.size())) { - ALOGE("Attempt to set active config = %d for display with %zu configs", - mMode, configs.size()); - return true; - } - sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); - if (hw == nullptr) { - ALOGE("Attempt to set active config = %d for null display %p", - mMode, mDisplay.get()); - } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { - ALOGW("Attempt to set active config = %d for virtual display", - mMode); - } else { - mFlinger.setActiveConfigInternal(hw, mMode); - } - return true; +status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) { + postMessageSync(new LambdaMessage([&] { + Vector<DisplayInfo> configs; + getDisplayConfigs(displayToken, &configs); + if (mode < 0 || mode >= static_cast<int>(configs.size())) { + ALOGE("Attempt to set active config %d for display with %zu configs", mode, + configs.size()); + return; } - }; - sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode); - postMessageSync(msg); + const auto display = getDisplayDevice(displayToken); + if (!display) { + ALOGE("Attempt to set active config %d for invalid display token %p", mode, + displayToken.get()); + } else if (display->isVirtual()) { + ALOGW("Attempt to set active config %d for virtual display", mode); + } else { + setActiveConfigInternal(display, mode); + } + })); + return NO_ERROR; } -status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, - Vector<ColorMode>* outColorModes) { - if ((outColorModes == nullptr) || (display.get() == nullptr)) { +status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken, + Vector<ColorMode>* outColorModes) { + if (!displayToken || !outColorModes) { return BAD_VALUE; } - if (!display.get()) { - return NAME_NOT_FOUND; - } - int32_t type = NAME_NOT_FOUND; - for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) { - if (display == mBuiltinDisplays[i]) { + for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) { + if (displayToken == mDisplayTokens[i]) { type = i; break; } @@ -1098,79 +1005,62 @@ status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, return NO_ERROR; } -ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) { - sp<const DisplayDevice> device(getDisplayDevice(display)); - if (device != nullptr) { - return device->getActiveColorMode(); +ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) { + if (const auto display = getDisplayDevice(displayToken)) { + return display->getActiveColorMode(); } return static_cast<ColorMode>(BAD_VALUE); } -void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& hw, - ColorMode mode, Dataspace dataSpace, - RenderIntent renderIntent) { - int32_t type = hw->getDisplayType(); - ColorMode currentMode = hw->getActiveColorMode(); - Dataspace currentDataSpace = hw->getCompositionDataSpace(); - RenderIntent currentRenderIntent = hw->getActiveRenderIntent(); +void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& display, ColorMode mode, + Dataspace dataSpace, RenderIntent renderIntent) { + ColorMode currentMode = display->getActiveColorMode(); + Dataspace currentDataSpace = display->getCompositionDataSpace(); + RenderIntent currentRenderIntent = display->getActiveRenderIntent(); if (mode == currentMode && dataSpace == currentDataSpace && renderIntent == currentRenderIntent) { return; } - if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + if (display->isVirtual()) { ALOGW("Trying to set config for virtual display"); return; } - hw->setActiveColorMode(mode); - hw->setCompositionDataSpace(dataSpace); - hw->setActiveRenderIntent(renderIntent); - getHwComposer().setActiveColorMode(type, mode, renderIntent); + display->setActiveColorMode(mode); + display->setCompositionDataSpace(dataSpace); + display->setActiveRenderIntent(renderIntent); + getHwComposer().setActiveColorMode(display->getDisplayType(), mode, renderIntent); ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), type=%d", - decodeColorMode(mode).c_str(), mode, - decodeRenderIntent(renderIntent).c_str(), renderIntent, - hw->getDisplayType()); -} - - -status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& display, - ColorMode colorMode) { - class MessageSetActiveColorMode: public MessageBase { - SurfaceFlinger& mFlinger; - sp<IBinder> mDisplay; - ColorMode mMode; - public: - MessageSetActiveColorMode(SurfaceFlinger& flinger, const sp<IBinder>& disp, - ColorMode mode) : - mFlinger(flinger), mDisplay(disp) { mMode = mode; } - virtual bool handler() { - Vector<ColorMode> modes; - mFlinger.getDisplayColorModes(mDisplay, &modes); - bool exists = std::find(std::begin(modes), std::end(modes), mMode) != std::end(modes); - if (mMode < ColorMode::NATIVE || !exists) { - ALOGE("Attempt to set invalid active color mode %s (%d) for display %p", - decodeColorMode(mMode).c_str(), mMode, mDisplay.get()); - return true; - } - sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); - if (hw == nullptr) { - ALOGE("Attempt to set active color mode %s (%d) for null display %p", - decodeColorMode(mMode).c_str(), mMode, mDisplay.get()); - } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { - ALOGW("Attempt to set active color mode %s %d for virtual display", - decodeColorMode(mMode).c_str(), mMode); - } else { - mFlinger.setActiveColorModeInternal(hw, mMode, Dataspace::UNKNOWN, - RenderIntent::COLORIMETRIC); - } - return true; + decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(), + renderIntent, display->getDisplayType()); +} + +status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) { + postMessageSync(new LambdaMessage([&] { + Vector<ColorMode> modes; + getDisplayColorModes(displayToken, &modes); + 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; } - }; - sp<MessageBase> msg = new MessageSetActiveColorMode(*this, display, colorMode); - postMessageSync(msg); + const auto display = getDisplayDevice(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()) { + ALOGW("Attempt to set active color mode %s (%d) for virtual display", + decodeColorMode(mode).c_str(), mode); + } else { + setActiveColorModeInternal(display, mode, Dataspace::UNKNOWN, + RenderIntent::COLORIMETRIC); + } + })); + return NO_ERROR; } @@ -1186,20 +1076,20 @@ status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { return NO_ERROR; } -status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) const { +status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken, + HdrCapabilities* outCapabilities) const { Mutex::Autolock _l(mStateLock); - sp<const DisplayDevice> displayDevice(getDisplayDeviceLocked(display)); - if (displayDevice == nullptr) { - ALOGE("getHdrCapabilities: Invalid display %p", displayDevice.get()); + const auto display = getDisplayDeviceLocked(displayToken); + if (!display) { + ALOGE("getHdrCapabilities: Invalid display token %p", displayToken.get()); return BAD_VALUE; } // At this point the DisplayDeivce should already be set up, // meaning the luminance information is already queried from // hardware composer and stored properly. - const HdrCapabilities& capabilities = displayDevice->getHdrCapabilities(); + const HdrCapabilities& capabilities = display->getHdrCapabilities(); *outCapabilities = HdrCapabilities(capabilities.getSupportedHdrTypes(), capabilities.getDesiredMaxLuminance(), capabilities.getDesiredMaxAverageLuminance(), @@ -1209,20 +1099,21 @@ status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display, } status_t SurfaceFlinger::enableVSyncInjections(bool enable) { - sp<LambdaMessage> enableVSyncInjections = new LambdaMessage([&]() { + postMessageSync(new LambdaMessage([&] { Mutex::Autolock _l(mStateLock); if (mInjectVSyncs == enable) { return; } + // TODO(akrulec): Part of the Injector should be refactored, so that it + // can be passed to Scheduler. if (enable) { ALOGV("VSync Injections enabled"); if (mVSyncInjector.get() == nullptr) { mVSyncInjector = std::make_unique<InjectVSyncSource>(); mInjectorEventThread = std::make_unique< - impl::EventThread>(mVSyncInjector.get(), - [this]() { resyncWithRateLimit(); }, + impl::EventThread>(mVSyncInjector.get(), [this] { resyncWithRateLimit(); }, impl::EventThread::InterceptVSyncsCallback(), "injEventThread"); } @@ -1233,8 +1124,8 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { } mInjectVSyncs = enable; - }); - postMessageSync(enableVSyncInjections); + })); + return NO_ERROR; } @@ -1254,15 +1145,6 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const NO_THREAD_SAFETY_ANALYSIS { - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - if ((uid != AID_SHELL) && - !PermissionCache::checkPermission(sDump, pid, uid)) { - ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - // Try to acquire a lock for 1s, fail gracefully const status_t err = mStateLock.timedLock(s2ns(1)); const bool locked = (err == NO_ERROR); @@ -1280,14 +1162,33 @@ status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayer return NO_ERROR; } +status_t SurfaceFlinger::getCompositionPreference( + Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, + Dataspace* outWideColorGamutDataspace, + ui::PixelFormat* outWideColorGamutPixelFormat) const { + *outDataspace = defaultCompositionDataspace; + *outPixelFormat = defaultCompositionPixelFormat; + *outWideColorGamutDataspace = wideColorGamutCompositionDataspace; + *outWideColorGamutPixelFormat = wideColorGamutCompositionPixelFormat; + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource) { - if (vsyncSource == eVsyncSourceSurfaceFlinger) { - return mSFEventThread->createEventConnection(); + if (mUseScheduler) { + if (vsyncSource == eVsyncSourceSurfaceFlinger) { + return mScheduler->createDisplayEventConnection(mSfConnectionHandle); + } else { + return mScheduler->createDisplayEventConnection(mAppConnectionHandle); + } } else { - return mEventThread->createEventConnection(); + if (vsyncSource == eVsyncSourceSurfaceFlinger) { + return mSFEventThread->createEventConnection(); + } else { + return mEventThread->createEventConnection(); + } } } @@ -1333,8 +1234,7 @@ void SurfaceFlinger::run() { void SurfaceFlinger::enableHardwareVsync() { Mutex::Autolock _l(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { - mPrimaryDispSync.beginResync(); - //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mPrimaryDispSync->beginResync(); mEventControlThread->setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } @@ -1351,26 +1251,33 @@ void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) { return; } - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const auto displayId = DisplayDevice::DISPLAY_PRIMARY; + if (!getHwComposer().isConnected(displayId)) { + return; + } + + const auto activeConfig = getHwComposer().getActiveConfig(displayId); const nsecs_t period = activeConfig->getVsyncPeriod(); - mPrimaryDispSync.reset(); - mPrimaryDispSync.setPeriod(period); + if (mUseScheduler) { + mScheduler->setVsyncPeriod(period); + } else { + mPrimaryDispSync->reset(); + mPrimaryDispSync->setPeriod(period); - if (!mPrimaryHWVsyncEnabled) { - mPrimaryDispSync.beginResync(); - //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); - mEventControlThread->setVsyncEnabled(true); - mPrimaryHWVsyncEnabled = true; + if (!mPrimaryHWVsyncEnabled) { + mPrimaryDispSync->beginResync(); + mEventControlThread->setVsyncEnabled(true); + mPrimaryHWVsyncEnabled = true; + } } } void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) { Mutex::Autolock _l(mHWVsyncLock); if (mPrimaryHWVsyncEnabled) { - //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false); mEventControlThread->setVsyncEnabled(false); - mPrimaryDispSync.endResync(); + mPrimaryDispSync->endResync(); mPrimaryHWVsyncEnabled = false; } if (makeUnavailable) { @@ -1390,8 +1297,10 @@ void SurfaceFlinger::resyncWithRateLimit() { sLastResyncAttempted = now; } -void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, - hwc2_display_t displayId, int64_t timestamp) { +void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, + int64_t timestamp) { + ATRACE_NAME("SF onVsync"); + Mutex::Autolock lock(mStateLock); // Ignore any vsyncs from a previous hardware composer. if (sequenceId != getBE().mComposerSequenceId) { @@ -1399,23 +1308,31 @@ void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, } int32_t type; - if (!getBE().mHwc->onVsync(displayId, timestamp, &type)) { + if (!getBE().mHwc->onVsync(hwcDisplayId, timestamp, &type)) { return; } - bool needsHwVsync = false; - - { // Scope for the lock - Mutex::Autolock _l(mHWVsyncLock); - if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) { - needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp); - } + if (type != DisplayDevice::DISPLAY_PRIMARY) { + // For now, we don't do anything with external display vsyncs. + return; } - if (needsHwVsync) { - enableHardwareVsync(); + if (mUseScheduler) { + mScheduler->addResyncSample(timestamp); } else { - disableHardwareVsync(false); + bool needsHwVsync = false; + { // Scope for the lock + Mutex::Autolock _l(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } } } @@ -1424,9 +1341,9 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display, +void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, HWC2::Connection connection) { - ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display, + ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId, connection == HWC2::Connection::Connected ? "connected" : "disconnected"); // Ignore events that do not have the right sequenceId. @@ -1440,7 +1357,7 @@ void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t displa // acquire it here. ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection}); + mPendingHotplugEvents.emplace_back(HotplugEvent{hwcDisplayId, connection}); if (std::this_thread::get_id() == mMainThreadId) { // Process all pending hot plug events immediately if we are on the main thread. @@ -1450,8 +1367,7 @@ void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t displa setTransactionFlags(eDisplayTransactionNeeded); } -void SurfaceFlinger::onRefreshReceived(int sequenceId, - hwc2_display_t /*display*/) { +void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) { Mutex::Autolock lock(mStateLock); if (sequenceId != getBE().mComposerSequenceId) { return; @@ -1468,7 +1384,11 @@ void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) { // Note: it is assumed the caller holds |mStateLock| when this is called void SurfaceFlinger::resetDisplayState() { - disableHardwareVsync(true); + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + disableHardwareVsync(true); + } // Clear the drawing state so that the logic inside of // handleTransactionLocked will fire. It will determine the delta between // mCurrentState and mDrawingState and re-apply all changes when we make the @@ -1496,8 +1416,13 @@ void SurfaceFlinger::updateVrFlinger() { Mutex::Autolock _l(mStateLock); - int currentDisplayPowerMode = getDisplayDeviceLocked( - mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])->getPowerMode(); + sp<DisplayDevice> display = getDefaultDisplayDeviceLocked(); + LOG_ALWAYS_FATAL_IF(!display); + const int currentDisplayPowerMode = display->getPowerMode(); + // This DisplayDevice will no longer be relevant once resetDisplayState() is + // called below. Clear the reference now so we don't accidentally use it + // later. + display.clear(); if (!vrFlingerRequestsDisplay) { mVrFlinger->SeizeDisplayOwnership(); @@ -1505,8 +1430,8 @@ void SurfaceFlinger::updateVrFlinger() { resetDisplayState(); getBE().mHwc.reset(); // Delete the current instance before creating the new one - getBE().mHwc.reset(new HWComposer(std::make_unique<Hwc2::impl::Composer>( - vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName))); + getBE().mHwc = getFactory().createHWComposer( + vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName); getBE().mHwc->registerCallback(this, ++getBE().mComposerSequenceId); LOG_ALWAYS_FATAL_IF(!getBE().mHwc->getComposer()->isRemote(), @@ -1514,28 +1439,38 @@ void SurfaceFlinger::updateVrFlinger() { if (vrFlingerRequestsDisplay) { mVrFlinger->GrantDisplayOwnership(); - } else { - enableHardwareVsync(); } mVisibleRegionsDirty = true; invalidateHwcGeometry(); // Re-enable default display. - sp<DisplayDevice> hw(getDisplayDeviceLocked( - mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])); - setPowerModeInternal(hw, currentDisplayPowerMode, /*stateLockHeld*/ true); + display = getDefaultDisplayDeviceLocked(); + LOG_ALWAYS_FATAL_IF(!display); + setPowerModeInternal(display, currentDisplayPowerMode, /*stateLockHeld*/ true); // Reset the timing values to account for the period of the swapped in HWC - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const auto activeConfig = getHwComposer().getActiveConfig(display->getId()); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); + // The present fences returned from vr_hwc are not an accurate + // representation of vsync times. + if (mUseScheduler) { + mScheduler->setIgnorePresentFences(getBE().mHwc->isUsingVrComposer() || !hasSyncFramework); + } else { + mPrimaryDispSync->setIgnorePresentFences(getBE().mHwc->isUsingVrComposer() || + !hasSyncFramework); + } + // Use phase of 0 since phase is not known. // Use latency of 0, which will snap to the ideal latency. - setCompositorTimingSnapped(0, period, 0); + DisplayStatInfo stats{0 /* vsyncTime */, period /* vsyncPeriod */}; + setCompositorTimingSnapped(stats, 0); + + resyncToHardwareVsync(false); - android_atomic_or(1, &mRepaintEverything); + mRepaintEverything = true; setTransactionFlags(eDisplayTransactionNeeded); } @@ -1547,6 +1482,7 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { mPreviousPresentFence != Fence::NO_FENCE && (mPreviousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING); + mFrameMissedCount += frameMissed; ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); if (frameMissed) { mTimeStats.incrementMissedFrames(); @@ -1588,83 +1524,198 @@ bool SurfaceFlinger::handleMessageTransaction() { return false; } -bool SurfaceFlinger::handleMessageInvalidate() { - ATRACE_CALL(); - return handlePageFlip(); -} - void SurfaceFlinger::handleMessageRefresh() { ATRACE_CALL(); mRefreshPending = false; - nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); - - preComposition(refreshStartTime); + const bool repaintEverything = mRepaintEverything.exchange(false); + preComposition(); rebuildLayerStacks(); - setUpHWComposer(); - doDebugFlashRegions(); + calculateWorkingSet(); + for (const auto& [token, display] : mDisplays) { + beginFrame(display); + prepareFrame(display); + doDebugFlashRegions(display, repaintEverything); + doComposition(display, repaintEverything); + } + doTracing("handleRefresh"); logLayerStats(); - doComposition(); - postComposition(refreshStartTime); - mPreviousPresentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); + postFrame(); + postComposition(); mHadClientComposition = false; - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - const sp<DisplayDevice>& displayDevice = mDisplays[displayId]; + for (const auto& [token, display] : mDisplays) { mHadClientComposition = mHadClientComposition || - getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId()); + getBE().mHwc->hasClientComposition(display->getId()); + } + + // Setup RenderEngine sync fences if native sync is supported. + if (getBE().mRenderEngine->useNativeFenceSync()) { + if (mHadClientComposition) { + base::unique_fd flushFence(getRenderEngine().flush()); + ALOGE_IF(flushFence < 0, "Failed to flush RenderEngine!"); + getBE().flushFence = new Fence(std::move(flushFence)); + } else { + // Cleanup for hygiene. + getBE().flushFence = Fence::NO_FENCE; + } } + mVsyncModulator.onRefreshed(mHadClientComposition); + getBE().mEndOfFrameCompositionInfo = std::move(getBE().mCompositionInfo); + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + for (auto& compositionInfo : getBE().mEndOfFrameCompositionInfo[displayId]) { + compositionInfo.hwc.hwcLayer = nullptr; + } + } + mLayersWithQueuedFrames.clear(); } -void SurfaceFlinger::doDebugFlashRegions() + +bool SurfaceFlinger::handleMessageInvalidate() { + ATRACE_CALL(); + return handlePageFlip(); +} + +void SurfaceFlinger::calculateWorkingSet() { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + // build the h/w work list + if (CC_UNLIKELY(mGeometryInvalid)) { + mGeometryInvalid = false; + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + if (displayId >= 0) { + const Vector<sp<Layer>>& currentLayers( + display->getVisibleLayersSortedByZ()); + for (size_t i = 0; i < currentLayers.size(); i++) { + const auto& layer = currentLayers[i]; + + if (!layer->hasHwcLayer(displayId)) { + if (!layer->createHwcLayer(getBE().mHwc.get(), displayId)) { + layer->forceClientComposition(displayId); + continue; + } + } + + layer->setGeometry(display, i); + if (mDebugDisableHWC || mDebugRegion) { + layer->forceClientComposition(displayId); + } + } + } + } + } + + // Set the per-frame data + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + if (displayId < 0) { + continue; + } + + if (mDrawingState.colorMatrixChanged) { + display->setColorTransform(mDrawingState.colorMatrix); + status_t result = getBE().mHwc->setColorTransform(displayId, mDrawingState.colorMatrix); + ALOGE_IF(result != NO_ERROR, "Failed to set color transform on " + "display %d: %d", displayId, result); + } + for (auto& layer : display->getVisibleLayersSortedByZ()) { + if (layer->isHdrY410()) { + layer->forceClientComposition(displayId); + } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ || + layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) && + !display->hasHDR10Support()) { + layer->forceClientComposition(displayId); + } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG || + layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) && + !display->hasHLGSupport()) { + layer->forceClientComposition(displayId); + } + + // TODO(b/111562338) remove when composer 2.3 is shipped. + if (layer->hasColorTransform()) { + layer->forceClientComposition(displayId); + } + + if (layer->getForceClientComposition(displayId)) { + ALOGV("[%s] Requesting Client composition", layer->getName().string()); + layer->setCompositionType(displayId, HWC2::Composition::Client); + continue; + } + + layer->setPerFrameData(display); + } + + if (useColorManagement) { + ColorMode colorMode; + Dataspace dataSpace; + RenderIntent renderIntent; + pickColorMode(display, &colorMode, &dataSpace, &renderIntent); + setActiveColorModeInternal(display, colorMode, dataSpace, renderIntent); + } + } + + mDrawingState.colorMatrixChanged = false; + + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + getBE().mCompositionInfo[displayId].clear(); + for (auto& layer : display->getVisibleLayersSortedByZ()) { + auto displayId = display->getId(); + layer->getBE().compositionInfo.compositionType = layer->getCompositionType(displayId); + if (!layer->setHwcLayer(displayId)) { + ALOGV("Need to create HWCLayer for %s", layer->getName().string()); + } + layer->getBE().compositionInfo.hwc.displayId = displayId; + getBE().mCompositionInfo[displayId].push_back(layer->getBE().compositionInfo); + layer->getBE().compositionInfo.hwc.hwcLayer = nullptr; + } + } +} + +void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything) { + const auto displayId = display->getId(); // is debugging enabled if (CC_LIKELY(!mDebugRegion)) return; - const bool repaintEverything = mRepaintEverything; - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - const sp<DisplayDevice>& hw(mDisplays[dpy]); - if (hw->isDisplayOn()) { - // transform the dirty region into this screen's coordinate space - const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); - if (!dirtyRegion.isEmpty()) { - // redraw the whole screen - doComposeSurfaces(hw); - - // and draw the dirty region - const int32_t height = hw->getHeight(); - auto& engine(getRenderEngine()); - engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1); - - hw->swapBuffers(getHwComposer()); - } + if (display->isPoweredOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(display->getDirtyRegion(repaintEverything)); + if (!dirtyRegion.isEmpty()) { + // redraw the whole screen + doComposeSurfaces(display); + + // and draw the dirty region + auto& engine(getRenderEngine()); + engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1); + + display->swapBuffers(getHwComposer()); } } - postFramebuffer(); + postFramebuffer(display); if (mDebugRegion > 1) { usleep(mDebugRegion * 1000); } - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - if (!displayDevice->isDisplayOn()) { - continue; - } - - status_t result = displayDevice->prepareFrame(*getBE().mHwc); + if (display->isPoweredOn()) { + status_t result = display->prepareFrame( + *getBE().mHwc, getBE().mCompositionInfo[displayId]); ALOGE_IF(result != NO_ERROR, - "prepareFrame for display %zd failed:" + "prepareFrame for display %d failed:" " %d (%s)", - displayId, result, strerror(-result)); + display->getId(), result, strerror(-result)); } } @@ -1679,30 +1730,27 @@ void SurfaceFlinger::doTracing(const char* where) { void SurfaceFlinger::logLayerStats() { ATRACE_CALL(); if (CC_UNLIKELY(mLayerStats.isEnabled())) { - int32_t hwcId = -1; - for (size_t dpy = 0; dpy < mDisplays.size(); ++dpy) { - const sp<const DisplayDevice>& displayDevice(mDisplays[dpy]); - if (displayDevice->isPrimary()) { - hwcId = displayDevice->getHwcDisplayId(); - break; + for (const auto& [token, display] : mDisplays) { + if (display->isPrimary()) { + mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(*display)); + return; } } - if (hwcId < 0) { - ALOGE("LayerStats: Hmmm, no primary display?"); - return; - } - mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(hwcId)); + + ALOGE("logLayerStats: no primary display"); } } -void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) +void SurfaceFlinger::preComposition() { ATRACE_CALL(); ALOGV("preComposition"); + mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + bool needExtraInvalidate = false; mDrawingState.traverseInZOrder([&](Layer* layer) { - if (layer->onPreComposition(refreshStartTime)) { + if (layer->onPreComposition(mRefreshStartTime)) { needExtraInvalidate = true; } }); @@ -1712,9 +1760,8 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -void SurfaceFlinger::updateCompositorTiming( - nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, - std::shared_ptr<FenceTime>& presentFenceTime) { +void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime) { // Update queue of past composite+present times and determine the // most recently known composite to present latency. getBE().mCompositePresentTimes.push({compositeTime, presentFenceTime}); @@ -1736,21 +1783,20 @@ void SurfaceFlinger::updateCompositorTiming( getBE().mCompositePresentTimes.pop(); } - setCompositorTimingSnapped( - vsyncPhase, vsyncInterval, compositeToPresentLatency); + setCompositorTimingSnapped(stats, compositeToPresentLatency); } -void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase, - nsecs_t vsyncInterval, nsecs_t compositeToPresentLatency) { +void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, + nsecs_t compositeToPresentLatency) { // Integer division and modulo round toward 0 not -inf, so we need to // treat negative and positive offsets differently. - nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) ? - (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) : - ((-sfVsyncPhaseOffsetNs) % vsyncInterval); + nsecs_t idealLatency = (sfVsyncPhaseOffsetNs > 0) + ? (stats.vsyncPeriod - (sfVsyncPhaseOffsetNs % stats.vsyncPeriod)) + : ((-sfVsyncPhaseOffsetNs) % stats.vsyncPeriod); // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval. if (idealLatency <= 0) { - idealLatency = vsyncInterval; + idealLatency = stats.vsyncPeriod; } // Snap the latency to a value that removes scheduling jitter from the @@ -1759,19 +1805,18 @@ void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase, // something (such as user input) to an accurate diasplay time. // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs // with (presentLatency % interval). - nsecs_t bias = vsyncInterval / 2; - int64_t extraVsyncs = - (compositeToPresentLatency - idealLatency + bias) / vsyncInterval; - nsecs_t snappedCompositeToPresentLatency = (extraVsyncs > 0) ? - idealLatency + (extraVsyncs * vsyncInterval) : idealLatency; + nsecs_t bias = stats.vsyncPeriod / 2; + int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod; + nsecs_t snappedCompositeToPresentLatency = + (extraVsyncs > 0) ? idealLatency + (extraVsyncs * stats.vsyncPeriod) : idealLatency; std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); - getBE().mCompositorTiming.deadline = vsyncPhase - idealLatency; - getBE().mCompositorTiming.interval = vsyncInterval; + getBE().mCompositorTiming.deadline = stats.vsyncTime - idealLatency; + getBE().mCompositorTiming.interval = stats.vsyncPeriod; getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; } -void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) +void SurfaceFlinger::postComposition() { ATRACE_CALL(); ALOGV("postComposition"); @@ -1783,31 +1828,36 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } // |mStateLock| not needed as we are on the main thread - const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); + const auto display = getDefaultDisplayDeviceLocked(); getBE().mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; - if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) { + if (display && getHwComposer().hasClientComposition(display->getId())) { glCompositionDoneFenceTime = - std::make_shared<FenceTime>(hw->getClientTargetAcquireFence()); + std::make_shared<FenceTime>(display->getClientTargetAcquireFence()); getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime); } else { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } getBE().mDisplayTimeline.updateSignalTimes(); - sp<Fence> presentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); - auto presentFenceTime = std::make_shared<FenceTime>(presentFence); + mPreviousPresentFence = + display ? getHwComposer().getPresentFence(display->getId()) : Fence::NO_FENCE; + auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence); getBE().mDisplayTimeline.push(presentFenceTime); - nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); - nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + DisplayStatInfo stats; + if (mUseScheduler) { + mScheduler->getDisplayStatInfo(&stats); + } else { + stats.vsyncTime = mPrimaryDispSync->computeNextRefresh(0); + stats.vsyncPeriod = mPrimaryDispSync->getPeriod(); + } - // We use the refreshStartTime which might be sampled a little later than + // We use the mRefreshStartTime which might be sampled a little later than // when we started doing work for this frame, but that should be okay // since updateCompositorTiming has snapping logic. - updateCompositorTiming( - vsyncPhase, vsyncInterval, refreshStartTime, presentFenceTime); + updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime); CompositorTiming compositorTiming; { std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); @@ -1824,16 +1874,24 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) }); if (presentFenceTime->isValid()) { - if (mPrimaryDispSync.addPresentFence(presentFenceTime)) { - enableHardwareVsync(); + if (mUseScheduler) { + mScheduler->addPresentFence(presentFenceTime); } else { - disableHardwareVsync(false); + if (mPrimaryDispSync->addPresentFence(presentFenceTime)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } } } if (!hasSyncFramework) { - if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) { - enableHardwareVsync(); + if (display && getHwComposer().isConnected(display->getId()) && display->isPoweredOn()) { + if (mUseScheduler) { + mScheduler->enableHardwareVsync(); + } else { + enableHardwareVsync(); + } } } @@ -1843,11 +1901,10 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) if (presentFenceTime->isValid()) { mAnimFrameTracker.setActualPresentFence( std::move(presentFenceTime)); - } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) { + } else if (display && getHwComposer().isConnected(display->getId())) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. - nsecs_t presentTime = - getBE().mHwc->getRefreshTimestamp(HWC_DISPLAY_PRIMARY); + const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(display->getId()); mAnimFrameTracker.setActualPresentTime(presentTime); } mAnimFrameTracker.advanceFrame(); @@ -1858,8 +1915,10 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) mTimeStats.incrementClientCompositionFrames(); } - if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && - hw->getPowerMode() == HWC_POWER_MODE_OFF) { + mTimeStats.setPresentFenceGlobal(presentFenceTime); + + if (display && getHwComposer().isConnected(display->getId()) && + display->getPowerMode() == HWC_POWER_MODE_OFF) { return; } @@ -1868,7 +1927,7 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) mHasPoweredOff = false; } else { nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime; - size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval); + size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod); if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) { getBE().mFrameBuckets[numPeriods] += elapsedTime; } else { @@ -1900,21 +1959,20 @@ void SurfaceFlinger::rebuildLayerStacks() { mVisibleRegionsDirty = false; invalidateHwcGeometry(); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + for (const auto& pair : mDisplays) { + const auto& display = pair.second; Region opaqueRegion; Region dirtyRegion; Vector<sp<Layer>> layersSortedByZ; Vector<sp<Layer>> layersNeedingFences; - const sp<DisplayDevice>& displayDevice(mDisplays[dpy]); - const Transform& tr(displayDevice->getTransform()); - const Rect bounds(displayDevice->getBounds()); - if (displayDevice->isDisplayOn()) { - computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion); + const ui::Transform& tr = display->getTransform(); + const Rect bounds = display->getBounds(); + if (display->isPoweredOn()) { + computeVisibleRegions(display, dirtyRegion, opaqueRegion); mDrawingState.traverseInZOrder([&](Layer* layer) { bool hwcLayerDestroyed = false; - if (layer->belongsToDisplay(displayDevice->getLayerStack(), - displayDevice->isPrimary())) { + if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { Region drawRegion(tr.transform( layer->visibleNonTransparentRegion)); drawRegion.andSelf(bounds); @@ -1923,15 +1981,13 @@ void SurfaceFlinger::rebuildLayerStacks() { } else { // Clear out the HWC layer if this layer was // previously visible, but no longer is - hwcLayerDestroyed = layer->destroyHwcLayer( - displayDevice->getHwcDisplayId()); + hwcLayerDestroyed = layer->destroyHwcLayer(display->getId()); } } else { - // WM changes displayDevice->layerStack upon sleep/awake. + // WM changes display->layerStack upon sleep/awake. // Here we make sure we delete the HWC layers even if // WM changed their layer stack. - hwcLayerDestroyed = layer->destroyHwcLayer( - displayDevice->getHwcDisplayId()); + hwcLayerDestroyed = layer->destroyHwcLayer(display->getId()); } // If a layer is not going to get a release fence because @@ -1947,12 +2003,11 @@ void SurfaceFlinger::rebuildLayerStacks() { } }); } - displayDevice->setVisibleLayersSortedByZ(layersSortedByZ); - displayDevice->setLayersNeedingFences(layersNeedingFences); - displayDevice->undefinedRegion.set(bounds); - displayDevice->undefinedRegion.subtractSelf( - tr.transform(opaqueRegion)); - displayDevice->dirtyRegion.orSelf(dirtyRegion); + display->setVisibleLayersSortedByZ(layersSortedByZ); + display->setLayersNeedingFences(layersNeedingFences); + display->undefinedRegion.set(bounds); + display->undefinedRegion.subtractSelf(tr.transform(opaqueRegion)); + display->dirtyRegion.orSelf(dirtyRegion); } } } @@ -1961,19 +2016,26 @@ void SurfaceFlinger::rebuildLayerStacks() { // can only be one of // - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced) // - Dataspace::DISPLAY_P3 +// - Dataspace::DISPLAY_BT2020 // The returned HDR data space is one of // - Dataspace::UNKNOWN // - Dataspace::BT2020_HLG // - Dataspace::BT2020_PQ -Dataspace SurfaceFlinger::getBestDataspace( - const sp<const DisplayDevice>& displayDevice, Dataspace* outHdrDataSpace) const { +Dataspace SurfaceFlinger::getBestDataspace(const sp<const DisplayDevice>& display, + Dataspace* outHdrDataSpace) const { Dataspace bestDataSpace = Dataspace::SRGB; *outHdrDataSpace = Dataspace::UNKNOWN; - for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + for (const auto& layer : display->getVisibleLayersSortedByZ()) { switch (layer->getDataSpace()) { case Dataspace::V0_SCRGB: case Dataspace::V0_SCRGB_LINEAR: + case Dataspace::BT2020: + case Dataspace::BT2020_ITU: + case Dataspace::BT2020_LINEAR: + case Dataspace::DISPLAY_BT2020: + bestDataSpace = Dataspace::DISPLAY_BT2020; + break; case Dataspace::DISPLAY_P3: bestDataSpace = Dataspace::DISPLAY_P3; break; @@ -1998,9 +2060,8 @@ Dataspace SurfaceFlinger::getBestDataspace( } // Pick the ColorMode / Dataspace for the display device. -void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, - ColorMode* outMode, Dataspace* outDataSpace, - RenderIntent* outRenderIntent) const { +void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode, + Dataspace* outDataSpace, RenderIntent* outRenderIntent) const { if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) { *outMode = ColorMode::NATIVE; *outDataSpace = Dataspace::UNKNOWN; @@ -2009,11 +2070,11 @@ void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, } Dataspace hdrDataSpace; - Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace); + Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace); // respect hdrDataSpace only when there is no legacy HDR support const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN && - !displayDevice->hasLegacyHdrSupport(hdrDataSpace); + !display->hasLegacyHdrSupport(hdrDataSpace); if (isHdr) { bestDataSpace = hdrDataSpace; } @@ -2032,177 +2093,105 @@ void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, break; } - displayDevice->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent); + display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent); } -void SurfaceFlinger::setUpHWComposer() { - ATRACE_CALL(); - ALOGV("setUpHWComposer"); - - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - bool dirty = !mDisplays[dpy]->getDirtyRegion(mRepaintEverything).isEmpty(); - bool empty = mDisplays[dpy]->getVisibleLayersSortedByZ().size() == 0; - bool wasEmpty = !mDisplays[dpy]->lastCompositionHadVisibleLayers; +void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display) +{ + bool dirty = !display->getDirtyRegion(false).isEmpty(); + bool empty = display->getVisibleLayersSortedByZ().size() == 0; + bool wasEmpty = !display->lastCompositionHadVisibleLayers; - // If nothing has changed (!dirty), don't recompose. - // If something changed, but we don't currently have any visible layers, - // and didn't when we last did a composition, then skip it this time. - // The second rule does two things: - // - When all layers are removed from a display, we'll emit one black - // frame, then nothing more until we get new layers. - // - When a display is created with a private layer stack, we won't - // emit any black frames until a layer is added to the layer stack. - bool mustRecompose = dirty && !(empty && wasEmpty); + // If nothing has changed (!dirty), don't recompose. + // If something changed, but we don't currently have any visible layers, + // and didn't when we last did a composition, then skip it this time. + // The second rule does two things: + // - When all layers are removed from a display, we'll emit one black + // frame, then nothing more until we get new layers. + // - When a display is created with a private layer stack, we won't + // emit any black frames until a layer is added to the layer stack. + bool mustRecompose = dirty && !(empty && wasEmpty); - ALOGV_IF(mDisplays[dpy]->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL, - "dpy[%zu]: %s composition (%sdirty %sempty %swasEmpty)", dpy, - mustRecompose ? "doing" : "skipping", - dirty ? "+" : "-", - empty ? "+" : "-", - wasEmpty ? "+" : "-"); + ALOGV_IF(display->getDisplayType() == DisplayDevice::DISPLAY_VIRTUAL, + "id[%d]: %s composition (%sdirty %sempty %swasEmpty)", display->getId(), + mustRecompose ? "doing" : "skipping", + dirty ? "+" : "-", + empty ? "+" : "-", + wasEmpty ? "+" : "-"); - mDisplays[dpy]->beginFrame(mustRecompose); + display->beginFrame(mustRecompose); - if (mustRecompose) { - mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty; - } + if (mustRecompose) { + display->lastCompositionHadVisibleLayers = !empty; } +} - // build the h/w work list - if (CC_UNLIKELY(mGeometryInvalid)) { - mGeometryInvalid = false; - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> displayDevice(mDisplays[dpy]); - const auto hwcId = displayDevice->getHwcDisplayId(); - if (hwcId >= 0) { - const Vector<sp<Layer>>& currentLayers( - displayDevice->getVisibleLayersSortedByZ()); - for (size_t i = 0; i < currentLayers.size(); i++) { - const auto& layer = currentLayers[i]; - if (!layer->hasHwcLayer(hwcId)) { - if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) { - layer->forceClientComposition(hwcId); - continue; - } - } - - layer->setGeometry(displayDevice, i); - if (mDebugDisableHWC || mDebugRegion) { - layer->forceClientComposition(hwcId); - } - } - } - } - } - - // Set the per-frame data - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - const auto hwcId = displayDevice->getHwcDisplayId(); - - if (hwcId < 0) { - continue; - } - if (mDrawingState.colorMatrixChanged) { - displayDevice->setColorTransform(mDrawingState.colorMatrix); - status_t result = getBE().mHwc->setColorTransform(hwcId, mDrawingState.colorMatrix); - ALOGE_IF(result != NO_ERROR, "Failed to set color transform on " - "display %zd: %d", displayId, result); - } - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - if (layer->isHdrY410()) { - layer->forceClientComposition(hwcId); - } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ || - layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) && - !displayDevice->hasHDR10Support()) { - layer->forceClientComposition(hwcId); - } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG || - layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) && - !displayDevice->hasHLGSupport()) { - layer->forceClientComposition(hwcId); - } - - if (layer->getForceClientComposition(hwcId)) { - ALOGV("[%s] Requesting Client composition", layer->getName().string()); - layer->setCompositionType(hwcId, HWC2::Composition::Client); - continue; - } - - layer->setPerFrameData(displayDevice); - } - - if (hasWideColorDisplay) { - ColorMode colorMode; - Dataspace dataSpace; - RenderIntent renderIntent; - pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent); - setActiveColorModeInternal(displayDevice, colorMode, dataSpace, renderIntent); - } +void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& display) +{ + const auto displayId = display->getId(); + if (!display->isPoweredOn()) { + return; } - mDrawingState.colorMatrixChanged = false; - - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - if (!displayDevice->isDisplayOn()) { - continue; - } - - status_t result = displayDevice->prepareFrame(*getBE().mHwc); - ALOGE_IF(result != NO_ERROR, "prepareFrame for display %zd failed:" - " %d (%s)", displayId, result, strerror(-result)); - } + status_t result = display->prepareFrame( + *getBE().mHwc, getBE().mCompositionInfo[displayId]); + ALOGE_IF(result != NO_ERROR, + "prepareFrame for display %d failed:" + " %d (%s)", + display->getId(), result, strerror(-result)); } -void SurfaceFlinger::doComposition() { +void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, bool repaintEverything) { ATRACE_CALL(); ALOGV("doComposition"); - const bool repaintEverything = android_atomic_and(0, &mRepaintEverything); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - const sp<DisplayDevice>& hw(mDisplays[dpy]); - if (hw->isDisplayOn()) { - // transform the dirty region into this screen's coordinate space - const Region dirtyRegion(hw->getDirtyRegion(repaintEverything)); + if (display->isPoweredOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(display->getDirtyRegion(repaintEverything)); - // repaint the framebuffer (if needed) - doDisplayComposition(hw, dirtyRegion); + // repaint the framebuffer (if needed) + doDisplayComposition(display, dirtyRegion); + + display->dirtyRegion.clear(); + display->flip(); + } + postFramebuffer(display); +} - hw->dirtyRegion.clear(); - hw->flip(); +void SurfaceFlinger::postFrame() +{ + // |mStateLock| not needed as we are on the main thread + if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) { + uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount(); + if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { + logFrameStats(); } } - postFramebuffer(); } -void SurfaceFlinger::postFramebuffer() +void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& display) { ATRACE_CALL(); ALOGV("postFramebuffer"); - const nsecs_t now = systemTime(); - mDebugInSwapBuffers = now; + mPostFramebufferTime = systemTime(); - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - if (!displayDevice->isDisplayOn()) { - continue; - } - const auto hwcId = displayDevice->getHwcDisplayId(); - if (hwcId >= 0) { - getBE().mHwc->presentAndGetReleaseFences(hwcId); + if (display->isPoweredOn()) { + const auto displayId = display->getId(); + if (displayId >= 0) { + getBE().mHwc->presentAndGetReleaseFences(displayId); } - displayDevice->onSwapBuffersCompleted(); - displayDevice->makeCurrent(); - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + display->onSwapBuffersCompleted(); + display->makeCurrent(); + for (auto& layer : display->getVisibleLayersSortedByZ()) { sp<Fence> releaseFence = Fence::NO_FENCE; // The layer buffer from the previous frame (if any) is released // by HWC only when the release fence from this frame (if any) is // signaled. Always get the release fence from HWC first. - auto hwcLayer = layer->getHwcLayer(hwcId); - if (hwcId >= 0) { - releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer); + auto hwcLayer = layer->getHwcLayer(displayId); + if (displayId >= 0) { + releaseFence = getBE().mHwc->getLayerReleaseFence(displayId, hwcLayer); } // If the layer was client composited in the previous frame, we @@ -2210,37 +2199,26 @@ void SurfaceFlinger::postFramebuffer() // Since we do not track that, always merge with the current // client target acquire fence when it is available, even though // this is suboptimal. - if (layer->getCompositionType(hwcId) == HWC2::Composition::Client) { + if (layer->getCompositionType(displayId) == HWC2::Composition::Client) { releaseFence = Fence::merge("LayerRelease", releaseFence, - displayDevice->getClientTargetAcquireFence()); + display->getClientTargetAcquireFence()); } - layer->onLayerDisplayed(releaseFence); + layer->getBE().onLayerDisplayed(releaseFence); } // We've got a list of layers needing fences, that are disjoint with - // displayDevice->getVisibleLayersSortedByZ. The best we can do is to + // display->getVisibleLayersSortedByZ. The best we can do is to // supply them with the present fence. - if (!displayDevice->getLayersNeedingFences().isEmpty()) { - sp<Fence> presentFence = getBE().mHwc->getPresentFence(hwcId); - for (auto& layer : displayDevice->getLayersNeedingFences()) { - layer->onLayerDisplayed(presentFence); + if (!display->getLayersNeedingFences().isEmpty()) { + sp<Fence> presentFence = getBE().mHwc->getPresentFence(displayId); + for (auto& layer : display->getLayersNeedingFences()) { + layer->getBE().onLayerDisplayed(presentFence); } } - if (hwcId >= 0) { - getBE().mHwc->clearReleaseFences(hwcId); - } - } - - mLastSwapBufferTime = systemTime() - now; - mDebugInSwapBuffers = 0; - - // |mStateLock| not needed as we are on the main thread - if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) { - uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount(); - if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { - logFrameStats(); + if (displayId >= 0) { + getBE().mHwc->clearReleaseFences(displayId); } } } @@ -2275,7 +2253,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) // here the transaction has been committed } -DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display, +DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t hwcDisplayId, HWC2::Connection connection) const { // Figure out whether the event is for the primary display or an // external display by matching the Hwc display id against one for a @@ -2284,17 +2262,16 @@ DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t d // have a connected primary display, we assume the new display is meant to // be the primary display, and then if we don't have an external display, // we assume it is that. - const auto primaryDisplayId = - getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY); - const auto externalDisplayId = + const auto primaryHwcDisplayId = getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY); + const auto externalHwcDisplayId = getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL); - if (primaryDisplayId && primaryDisplayId == display) { + if (primaryHwcDisplayId && primaryHwcDisplayId == hwcDisplayId) { return DisplayDevice::DISPLAY_PRIMARY; - } else if (externalDisplayId && externalDisplayId == display) { - return DisplayDevice::DISPLAY_EXTERNAL; - } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) { + } else if (externalHwcDisplayId && externalHwcDisplayId == hwcDisplayId) { + return DisplayDevice::DISPLAY_EXTERNAL; + } else if (connection == HWC2::Connection::Connected && !primaryHwcDisplayId) { return DisplayDevice::DISPLAY_PRIMARY; - } else if (connection == HWC2::Connection::Connected && !externalDisplayId) { + } else if (connection == HWC2::Connection::Connected && !externalHwcDisplayId) { return DisplayDevice::DISPLAY_EXTERNAL; } @@ -2303,9 +2280,9 @@ DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t d void SurfaceFlinger::processDisplayHotplugEventsLocked() { for (const auto& event : mPendingHotplugEvents) { - auto displayType = determineDisplayType(event.display, event.connection); + auto displayType = determineDisplayType(event.hwcDisplayId, event.connection); if (displayType == DisplayDevice::DISPLAY_ID_INVALID) { - ALOGW("Unable to determine the display type for display %" PRIu64, event.display); + ALOGW("Unable to determine the display type for display %" PRIu64, event.hwcDisplayId); continue; } @@ -2314,29 +2291,34 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { continue; } - getBE().mHwc->onHotplug(event.display, displayType, event.connection); + const auto displayId = + getBE().mHwc->onHotplug(event.hwcDisplayId, displayType, event.connection); + if (displayId) { + ALOGV("Display %" PRIu64 " has stable ID %" PRIu64, event.hwcDisplayId, *displayId); + } if (event.connection == HWC2::Connection::Connected) { - if (!mBuiltinDisplays[displayType].get()) { + if (!mDisplayTokens[displayType].get()) { ALOGV("Creating built in display %d", displayType); - mBuiltinDisplays[displayType] = new BBinder(); - // All non-virtual displays are currently considered secure. - DisplayDeviceState info(displayType, true); + mDisplayTokens[displayType] = new BBinder(); + DisplayDeviceState info; + info.type = displayType; info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ? "Built-in Screen" : "External Screen"; - mCurrentState.displays.add(mBuiltinDisplays[displayType], info); + info.isSecure = true; // All physical displays are currently considered secure. + mCurrentState.displays.add(mDisplayTokens[displayType], info); mInterceptor->saveDisplayCreation(info); } } else { ALOGV("Removing built in display %d", displayType); - ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]); + ssize_t idx = mCurrentState.displays.indexOfKey(mDisplayTokens[displayType]); if (idx >= 0) { const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx)); - mInterceptor->saveDisplayDeletion(info.displayId); + mInterceptor->saveDisplayDeletion(info.sequenceId); mCurrentState.displays.removeItemsAt(idx); } - mBuiltinDisplays[displayType].clear(); + mDisplayTokens[displayType].clear(); } processDisplayChangesLocked(); @@ -2346,49 +2328,47 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( - const wp<IBinder>& display, int hwcId, const DisplayDeviceState& state, + const wp<IBinder>& displayToken, int32_t displayId, const DisplayDeviceState& state, const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) { - bool hasWideColorGamut = false; - std::unordered_map<ColorMode, std::vector<RenderIntent>> hwcColorModes; - HdrCapabilities hdrCapabilities; - int32_t supportedPerFrameMetadata = 0; - - if (hasWideColorDisplay && hwcId >= 0) { - std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId); + DisplayDeviceCreationArgs creationArgs(this, displayToken, state.type, displayId); + creationArgs.isSecure = state.isSecure; + creationArgs.displaySurface = dispSurface; + creationArgs.hasWideColorGamut = false; + creationArgs.supportedPerFrameMetadata = 0; + + if (useColorManagement && displayId >= 0) { + std::vector<ColorMode> modes = getHwComposer().getColorModes(displayId); for (ColorMode colorMode : modes) { - switch (colorMode) { - case ColorMode::DISPLAY_P3: - case ColorMode::ADOBE_RGB: - case ColorMode::DCI_P3: - hasWideColorGamut = true; - break; - default: - break; + if (isWideColorMode(colorMode)) { + creationArgs.hasWideColorGamut = true; } - std::vector<RenderIntent> renderIntents = getHwComposer().getRenderIntents(hwcId, - colorMode); - hwcColorModes.emplace(colorMode, renderIntents); + std::vector<RenderIntent> renderIntents = + getHwComposer().getRenderIntents(displayId, colorMode); + creationArgs.hwcColorModes.emplace(colorMode, renderIntents); } } - if (hwcId >= 0) { - getHwComposer().getHdrCapabilities(hwcId, &hdrCapabilities); - supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(hwcId); + if (displayId >= 0) { + getHwComposer().getHdrCapabilities(displayId, &creationArgs.hdrCapabilities); + creationArgs.supportedPerFrameMetadata = + getHwComposer().getSupportedPerFrameMetadata(displayId); } - auto nativeWindowSurface = mCreateNativeWindowSurface(producer); + auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer); auto nativeWindow = nativeWindowSurface->getNativeWindow(); + creationArgs.nativeWindow = nativeWindow; /* * Create our display's surface */ - std::unique_ptr<RE::Surface> renderSurface = getRenderEngine().createSurface(); + std::unique_ptr<renderengine::Surface> renderSurface = getRenderEngine().createSurface(); renderSurface->setCritical(state.type == DisplayDevice::DISPLAY_PRIMARY); - renderSurface->setAsync(state.type >= DisplayDevice::DISPLAY_VIRTUAL); + renderSurface->setAsync(state.isVirtual()); renderSurface->setNativeWindow(nativeWindow.get()); - const int displayWidth = renderSurface->queryWidth(); - const int displayHeight = renderSurface->queryHeight(); + creationArgs.displayWidth = renderSurface->getWidth(); + creationArgs.displayHeight = renderSurface->getHeight(); + creationArgs.renderSurface = std::move(renderSurface); // Make sure that composition can never be stalled by a virtual display // consumer that isn't processing buffers fast enough. We have to do this @@ -2397,19 +2377,18 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( // * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the // window's swap interval in eglMakeCurrent, so they'll override the // interval we set here. - if (state.type >= DisplayDevice::DISPLAY_VIRTUAL) { + if (state.isVirtual()) { nativeWindow->setSwapInterval(nativeWindow.get(), 0); } + creationArgs.displayInstallOrientation = state.type == DisplayDevice::DISPLAY_PRIMARY + ? primaryDisplayOrientation + : DisplayState::eOrientationDefault; + // virtual displays are always considered enabled - auto initialPowerMode = (state.type >= DisplayDevice::DISPLAY_VIRTUAL) ? HWC_POWER_MODE_NORMAL - : HWC_POWER_MODE_OFF; + creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF; - sp<DisplayDevice> hw = - new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow, - dispSurface, std::move(renderSurface), displayWidth, displayHeight, - hasWideColorGamut, hdrCapabilities, - supportedPerFrameMetadata, hwcColorModes, initialPowerMode); + sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs)); if (maxFrameBufferAcquiredBuffers >= 3) { nativeWindowSurface->preallocateBuffers(); @@ -2417,20 +2396,20 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( ColorMode defaultColorMode = ColorMode::NATIVE; Dataspace defaultDataSpace = Dataspace::UNKNOWN; - if (hasWideColorGamut) { + if (display->hasWideColorGamut()) { defaultColorMode = ColorMode::SRGB; defaultDataSpace = Dataspace::SRGB; } - setActiveColorModeInternal(hw, defaultColorMode, defaultDataSpace, + setActiveColorModeInternal(display, defaultColorMode, defaultDataSpace, RenderIntent::COLORIMETRIC); if (state.type < DisplayDevice::DISPLAY_VIRTUAL) { - hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type)); + display->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type)); } - hw->setLayerStack(state.layerStack); - hw->setProjection(state.orientation, state.viewport, state.frame); - hw->setDisplayName(state.displayName); + display->setLayerStack(state.layerStack); + display->setProjection(state.orientation, state.viewport, state.frame); + display->setDisplayName(state.displayName); - return hw; + return display; } void SurfaceFlinger::processDisplayChangesLocked() { @@ -2455,17 +2434,32 @@ void SurfaceFlinger::processDisplayChangesLocked() { // Call makeCurrent() on the primary display so we can // be sure that nothing associated with this display // is current. - const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked()); - if (defaultDisplay != nullptr) defaultDisplay->makeCurrent(); - sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i))); - if (hw != nullptr) hw->disconnect(getHwComposer()); - if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) - mEventThread->onHotplugReceived(draw[i].type, false); - mDisplays.removeItem(draw.keyAt(i)); + if (const auto defaultDisplay = getDefaultDisplayDeviceLocked()) { + defaultDisplay->makeCurrent(); + } + if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) { + display->disconnect(getHwComposer()); + } + if (draw[i].type == DisplayDevice::DISPLAY_PRIMARY) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::Primary, false); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + } + } else if (draw[i].type == DisplayDevice::DISPLAY_EXTERNAL) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::External, false); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, false); + } + } + mDisplays.erase(draw.keyAt(i)); } else { // this display is in both lists. see if something changed. const DisplayDeviceState& state(curr[j]); - const wp<IBinder>& display(curr.keyAt(j)); + const wp<IBinder>& displayToken = curr.keyAt(j); const sp<IBinder> state_binder = IInterface::asBinder(state.surface); const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface); if (state_binder != draw_binder) { @@ -2473,26 +2467,26 @@ void SurfaceFlinger::processDisplayChangesLocked() { // recreating the DisplayDevice, so we just remove it // from the drawing state, so that it get re-added // below. - sp<DisplayDevice> hw(getDisplayDeviceLocked(display)); - if (hw != nullptr) hw->disconnect(getHwComposer()); - mDisplays.removeItem(display); + if (const auto display = getDisplayDeviceLocked(displayToken)) { + display->disconnect(getHwComposer()); + } + mDisplays.erase(displayToken); mDrawingState.displays.removeItemsAt(i); dc--; // at this point we must loop to the next item continue; } - const sp<DisplayDevice> disp(getDisplayDeviceLocked(display)); - if (disp != nullptr) { + if (const auto display = getDisplayDeviceLocked(displayToken)) { if (state.layerStack != draw[i].layerStack) { - disp->setLayerStack(state.layerStack); + display->setLayerStack(state.layerStack); } if ((state.orientation != draw[i].orientation) || (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) { - disp->setProjection(state.orientation, state.viewport, state.frame); + display->setProjection(state.orientation, state.viewport, state.frame); } if (state.width != draw[i].width || state.height != draw[i].height) { - disp->setDisplaySize(state.width, state.height); + display->setDisplaySize(state.width, state.height); } } } @@ -2509,10 +2503,10 @@ void SurfaceFlinger::processDisplayChangesLocked() { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferProducer> bqProducer; sp<IGraphicBufferConsumer> bqConsumer; - mCreateBufferQueue(&bqProducer, &bqConsumer, false); + getFactory().createBufferQueue(&bqProducer, &bqConsumer, false); - int32_t hwcId = -1; - if (state.isVirtualDisplay()) { + int32_t displayId = -1; + if (state.isVirtual()) { // Virtual displays without a surface are dormant: // they have external state (layer stack, projection, // etc.) but no internal state (i.e. a DisplayDevice). @@ -2530,13 +2524,14 @@ void SurfaceFlinger::processDisplayChangesLocked() { ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status); auto format = static_cast<ui::PixelFormat>(intFormat); - getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId); + getBE().mHwc->allocateVirtualDisplay(width, height, &format, + &displayId); } // TODO: Plumb requested format back up to consumer sp<VirtualDisplaySurface> vds = - new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface, + new VirtualDisplaySurface(*getBE().mHwc, displayId, state.surface, bqProducer, bqConsumer, state.displayName); @@ -2549,18 +2544,36 @@ void SurfaceFlinger::processDisplayChangesLocked() { "surface is provided (%p), ignoring it", state.surface.get()); - hwcId = state.type; - dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer); + displayId = state.type; + dispSurface = new FramebufferSurface(*getBE().mHwc, displayId, bqConsumer); producer = bqProducer; } - const wp<IBinder>& display(curr.keyAt(i)); + const wp<IBinder>& displayToken = curr.keyAt(i); if (dispSurface != nullptr) { - mDisplays.add(display, - setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface, - producer)); - if (!state.isVirtualDisplay()) { - mEventThread->onHotplugReceived(state.type, true); + mDisplays.emplace(displayToken, + setupNewDisplayDeviceInternal(displayToken, displayId, state, + dispSurface, producer)); + if (!state.isVirtual()) { + if (state.type == DisplayDevice::DISPLAY_PRIMARY) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::Primary, + true); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, + true); + } + } else if (state.type == DisplayDevice::DISPLAY_EXTERNAL) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::External, + true); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, + true); + } + } } } } @@ -2622,7 +2635,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // happened yet, so we must use the current state layer list // (soon to become the drawing state list). // - sp<const DisplayDevice> disp; + sp<const DisplayDevice> hintDisplay; uint32_t currentlayerStack = 0; bool first = true; mCurrentState.traverseInZOrder([&](Layer* layer) { @@ -2635,34 +2648,33 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // 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. - disp.clear(); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - sp<const DisplayDevice> hw(mDisplays[dpy]); - if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) { - if (disp == nullptr) { - disp = std::move(hw); - } else { - disp = nullptr; + hintDisplay = nullptr; + for (const auto& [token, display] : mDisplays) { + if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { + if (hintDisplay) { + hintDisplay = nullptr; break; + } else { + hintDisplay = display; } } } } - if (disp == nullptr) { + if (!hintDisplay) { // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to // redraw after transform hint changes. See bug 8508397. // could be null when this layer is using a layerStack // that is not visible on any display. Also can occur at // screen off/on times. - disp = getDefaultDisplayDeviceLocked(); + hintDisplay = getDefaultDisplayDeviceLocked(); } - // disp can be null if there is no display available at all to get + // could be null if there is no display available at all to get // the transform hint from. - if (disp != nullptr) { - layer->updateTransformHint(disp); + if (hintDisplay) { + layer->updateTransformHint(hintDisplay); } first = false; @@ -2705,14 +2717,13 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) void SurfaceFlinger::updateCursorAsync() { - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - if (displayDevice->getHwcDisplayId() < 0) { + for (const auto& [token, display] : mDisplays) { + if (display->getId() < 0) { continue; } - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - layer->updateCursorPosition(displayDevice); + for (auto& layer : display->getVisibleLayersSortedByZ()) { + layer->updateCursorPosition(display); } } } @@ -2745,9 +2756,8 @@ void SurfaceFlinger::commitTransaction() mTransactionCV.broadcast(); } -void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice, - Region& outDirtyRegion, Region& outOpaqueRegion) -{ +void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& display, + Region& outDirtyRegion, Region& outOpaqueRegion) { ATRACE_CALL(); ALOGV("computeVisibleRegions"); @@ -2762,8 +2772,9 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa const Layer::State& s(layer->getDrawingState()); // only consider the layers on the given layer stack - if (!layer->belongsToDisplay(displayDevice->getLayerStack(), displayDevice->isPrimary())) + if (!layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { return; + } /* * opaqueRegion: area of a surface that is fully opaque. @@ -2800,13 +2811,13 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa const bool translucent = !layer->isOpaque(s); Rect bounds(layer->computeScreenBounds()); visibleRegion.set(bounds); - Transform tr = layer->getTransform(); + ui::Transform tr = layer->getTransform(); if (!visibleRegion.isEmpty()) { // Remove the transparent area from the visible region if (translucent) { if (tr.preserveRects()) { // transform the transparent region - transparentRegion = tr.transform(s.activeTransparentRegion); + transparentRegion = tr.transform(layer->getActiveTransparentRegion(s)); } else { // transformation too complex, can't do the // transparent region optimization. @@ -2817,7 +2828,7 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa // compute the opaque region const int32_t layerOrientation = tr.getOrientation(); if (layer->getAlpha() == 1.0f && !translucent && - ((layerOrientation & Transform::ROT_INVALID) == false)) { + ((layerOrientation & ui::Transform::ROT_INVALID) == false)) { // the opaque region is the layer's footprint opaqueRegion = visibleRegion; } @@ -2883,10 +2894,9 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa } void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) { - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - const sp<DisplayDevice>& hw(mDisplays[dpy]); - if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) { - hw->dirtyRegion.orSelf(dirty); + for (const auto& [token, display] : mDisplays) { + if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { + display->dirtyRegion.orSelf(dirty); } } } @@ -2911,9 +2921,10 @@ bool SurfaceFlinger::handlePageFlip() // 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.traverseInZOrder([&](Layer* layer) { - if (layer->hasQueuedFrame()) { + if (layer->hasReadyFrame()) { frameQueued = true; - if (layer->shouldPresentNow(mPrimaryDispSync)) { + const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime(); + if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { layer->useEmptyDamage(); @@ -2924,7 +2935,7 @@ bool SurfaceFlinger::handlePageFlip() }); for (auto& layer : mLayersWithQueuedFrames) { - const Region dirty(layer->latchBuffer(visibleRegions, latchTime)); + const Region dirty(layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence)); layer->useSurfaceDamage(); invalidateLayerStack(layer, dirty); if (layer->isBufferLatched()) { @@ -2932,6 +2943,11 @@ bool SurfaceFlinger::handlePageFlip() } } + // Clear the renderengine fence here... + // downstream code assumes that a cleared fence == NO_FENCE, so reassign to + // clear instead of sp::clear. + getBE().flushFence = Fence::NO_FENCE; + mVisibleRegionsDirty |= visibleRegions; // If we will need to wake up at some time in the future to deal with a @@ -2956,38 +2972,35 @@ void SurfaceFlinger::invalidateHwcGeometry() mGeometryInvalid = true; } - -void SurfaceFlinger::doDisplayComposition( - const sp<const DisplayDevice>& displayDevice, - const Region& inDirtyRegion) -{ +void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& display, + const Region& inDirtyRegion) { // We only need to actually compose the display if: // 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) - bool isHwcDisplay = displayDevice->getHwcDisplayId() >= 0; + bool isHwcDisplay = display->getId() >= 0; if (!isHwcDisplay && inDirtyRegion.isEmpty()) { ALOGV("Skipping display composition"); return; } ALOGV("doDisplayComposition"); - if (!doComposeSurfaces(displayDevice)) return; + if (!doComposeSurfaces(display)) return; // swap buffers (presentation) - displayDevice->swapBuffers(getHwComposer()); + display->swapBuffers(getHwComposer()); } -bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDevice) -{ +bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& display) { ALOGV("doComposeSurfaces"); - const Region bounds(displayDevice->bounds()); - const DisplayRenderArea renderArea(displayDevice); - const auto hwcId = displayDevice->getHwcDisplayId(); - const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId); + const Region bounds(display->bounds()); + const DisplayRenderArea renderArea(display); + const auto displayId = display->getId(); + const bool hasClientComposition = getBE().mHwc->hasClientComposition(displayId); ATRACE_INT("hasClientComposition", hasClientComposition); + mat4 colorMatrix; bool applyColorMatrix = false; bool needsEnhancedColorMatrix = false; @@ -2995,18 +3008,18 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev ALOGV("hasClientComposition"); Dataspace outputDataspace = Dataspace::UNKNOWN; - if (displayDevice->hasWideColorGamut()) { - outputDataspace = displayDevice->getCompositionDataSpace(); + if (display->hasWideColorGamut()) { + outputDataspace = display->getCompositionDataSpace(); } getBE().mRenderEngine->setOutputDataSpace(outputDataspace); getBE().mRenderEngine->setDisplayMaxLuminance( - displayDevice->getHdrCapabilities().getDesiredMaxLuminance()); + display->getHdrCapabilities().getDesiredMaxLuminance()); - const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId); + const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(displayId); const bool skipClientColorTransform = getBE().mHwc->hasCapability( HWC2::Capability::SkipClientColorTransform); - mat4 colorMatrix; + // Compute the global color transform matrix. applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform; if (applyColorMatrix) { colorMatrix = mDrawingState.colorMatrix; @@ -3016,22 +3029,21 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev // thus we only apply this matrix when the render intent is not colorimetric // and the output color space is Display P3. needsEnhancedColorMatrix = - (displayDevice->getActiveRenderIntent() >= RenderIntent::ENHANCE && + (display->getActiveRenderIntent() >= RenderIntent::ENHANCE && outputDataspace == Dataspace::DISPLAY_P3); if (needsEnhancedColorMatrix) { colorMatrix *= mEnhancedSaturationMatrix; } - getRenderEngine().setupColorTransform(colorMatrix); - - if (!displayDevice->makeCurrent()) { + if (!display->makeCurrent()) { ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s", - displayDevice->getDisplayName().string()); + display->getDisplayName().c_str()); getRenderEngine().resetCurrentSurface(); // |mStateLock| not needed as we are on the main thread - if(!getDefaultDisplayDeviceLocked()->makeCurrent()) { - ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting."); + const auto defaultDisplay = getDefaultDisplayDeviceLocked(); + if (!defaultDisplay || !defaultDisplay->makeCurrent()) { + ALOGE("DisplayDevice::makeCurrent on default display failed. Aborting."); } return false; } @@ -3048,29 +3060,27 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev // we start with the whole screen area and remove the scissor part // we're left with the letterbox region // (common case is that letterbox ends-up being empty) - const Region letterbox(bounds.subtract(displayDevice->getScissor())); + const Region letterbox = bounds.subtract(display->getScissor()); // compute the area to clear - Region region(displayDevice->undefinedRegion.merge(letterbox)); + const Region region = display->undefinedRegion.merge(letterbox); // screen is already cleared here if (!region.isEmpty()) { // can happen with SurfaceView - drawWormhole(displayDevice, region); + drawWormhole(region); } } - const Rect& bounds(displayDevice->getBounds()); - const Rect& scissor(displayDevice->getScissor()); + const Rect& bounds = display->getBounds(); + const Rect& scissor = display->getScissor(); if (scissor != bounds) { // scissor doesn't match the screen's dimensions, so we // need to clear everything outside of it and enable // the GL scissor so we don't draw anything where we shouldn't // enable scissor for this frame - const uint32_t height = displayDevice->getHeight(); - getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom, - scissor.getWidth(), scissor.getHeight()); + getBE().mRenderEngine->setScissor(scissor); } } @@ -3079,24 +3089,23 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev */ ALOGV("Rendering client layers"); - const Transform& displayTransform = displayDevice->getTransform(); + const ui::Transform& displayTransform = display->getTransform(); bool firstLayer = true; - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + for (auto& layer : display->getVisibleLayersSortedByZ()) { const Region clip(bounds.intersect( displayTransform.transform(layer->visibleRegion))); ALOGV("Layer: %s", layer->getName().string()); - ALOGV(" Composition type: %s", - to_string(layer->getCompositionType(hwcId)).c_str()); + ALOGV(" Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str()); if (!clip.isEmpty()) { - switch (layer->getCompositionType(hwcId)) { + switch (layer->getCompositionType(displayId)) { case HWC2::Composition::Cursor: case HWC2::Composition::Device: case HWC2::Composition::Sideband: case HWC2::Composition::SolidColor: { const Layer::State& state(layer->getDrawingState()); - if (layer->getClearClientTarget(hwcId) && !firstLayer && - layer->isOpaque(state) && (state.color.a == 1.0f) - && hasClientComposition) { + if (layer->getClearClientTarget(displayId) && !firstLayer && + layer->isOpaque(state) && (layer->getAlpha() == 1.0f) && + hasClientComposition) { // never clear the very first layer since we're // guaranteed the FB is already cleared layer->clearWithOpenGL(renderArea); @@ -3104,6 +3113,19 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev break; } case HWC2::Composition::Client: { + if (layer->hasColorTransform()) { + mat4 tmpMatrix; + if (applyColorMatrix) { + tmpMatrix = mDrawingState.colorMatrix; + } + tmpMatrix *= layer->getColorTransform(); + if (needsEnhancedColorMatrix) { + tmpMatrix *= mEnhancedSaturationMatrix; + } + getRenderEngine().setColorTransform(tmpMatrix); + } else { + getRenderEngine().setColorTransform(colorMatrix); + } layer->draw(renderArea, clip); break; } @@ -3116,19 +3138,17 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev firstLayer = false; } - if (applyColorMatrix || needsEnhancedColorMatrix) { - getRenderEngine().setupColorTransform(mat4()); - } + // Clear color transform matrix at the end of the frame. + getRenderEngine().setColorTransform(mat4()); // disable scissor at the end of the frame getBE().mRenderEngine->disableScissor(); return true; } -void SurfaceFlinger::drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const { - const int32_t height = displayDevice->getHeight(); +void SurfaceFlinger::drawWormhole(const Region& region) const { auto& engine(getRenderEngine()); - engine.fillRegionWithColor(region, height, 0, 0, 0, 0); + engine.fillRegionWithColor(region, 0, 0, 0, 0); } status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, @@ -3228,20 +3248,20 @@ status_t SurfaceFlinger::removeLayerLocked(const Mutex&, const sp<Layer>& layer, } uint32_t SurfaceFlinger::peekTransactionFlags() { - return android_atomic_release_load(&mTransactionFlags); + return mTransactionFlags; } uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { - return android_atomic_and(~flags, &mTransactionFlags) & flags; + return mTransactionFlags.fetch_and(~flags) & flags; } uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { - return setTransactionFlags(flags, VSyncModulator::TransactionStart::NORMAL); + return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL); } uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, - VSyncModulator::TransactionStart transactionStart) { - uint32_t old = android_atomic_or(flags, &mTransactionFlags); + Scheduler::TransactionStart transactionStart) { + uint32_t old = mTransactionFlags.fetch_or(flags); mVsyncModulator.setTransactionStart(transactionStart); if ((old & flags)==0) { // wake the server up signalTransaction(); @@ -3331,9 +3351,8 @@ void SurfaceFlinger::setTransactionState( } // this triggers the transaction - const auto start = (flags & eEarlyWakeup) - ? VSyncModulator::TransactionStart::EARLY - : VSyncModulator::TransactionStart::NORMAL; + const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY + : Scheduler::TransactionStart::NORMAL; setTransactionFlags(transactionFlags, start); // if this is a synchronous transaction, wait for it to take effect @@ -3357,53 +3376,51 @@ void SurfaceFlinger::setTransactionState( } } -uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) -{ - ssize_t dpyIdx = mCurrentState.displays.indexOfKey(s.token); - if (dpyIdx < 0) - return 0; +uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) { + const ssize_t index = mCurrentState.displays.indexOfKey(s.token); + if (index < 0) return 0; uint32_t flags = 0; - DisplayDeviceState& disp(mCurrentState.displays.editValueAt(dpyIdx)); - if (disp.isValid()) { - const uint32_t what = s.what; - if (what & DisplayState::eSurfaceChanged) { - if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) { - disp.surface = s.surface; - flags |= eDisplayTransactionNeeded; - } + DisplayDeviceState& state = mCurrentState.displays.editValueAt(index); + + const uint32_t what = s.what; + if (what & DisplayState::eSurfaceChanged) { + if (IInterface::asBinder(state.surface) != IInterface::asBinder(s.surface)) { + state.surface = s.surface; + flags |= eDisplayTransactionNeeded; } - if (what & DisplayState::eLayerStackChanged) { - if (disp.layerStack != s.layerStack) { - disp.layerStack = s.layerStack; - flags |= eDisplayTransactionNeeded; - } + } + if (what & DisplayState::eLayerStackChanged) { + if (state.layerStack != s.layerStack) { + state.layerStack = s.layerStack; + flags |= eDisplayTransactionNeeded; } - if (what & DisplayState::eDisplayProjectionChanged) { - if (disp.orientation != s.orientation) { - disp.orientation = s.orientation; - flags |= eDisplayTransactionNeeded; - } - if (disp.frame != s.frame) { - disp.frame = s.frame; - flags |= eDisplayTransactionNeeded; - } - if (disp.viewport != s.viewport) { - disp.viewport = s.viewport; - flags |= eDisplayTransactionNeeded; - } + } + if (what & DisplayState::eDisplayProjectionChanged) { + if (state.orientation != s.orientation) { + state.orientation = s.orientation; + flags |= eDisplayTransactionNeeded; } - if (what & DisplayState::eDisplaySizeChanged) { - if (disp.width != s.width) { - disp.width = s.width; - flags |= eDisplayTransactionNeeded; - } - if (disp.height != s.height) { - disp.height = s.height; - flags |= eDisplayTransactionNeeded; - } + if (state.frame != s.frame) { + state.frame = s.frame; + flags |= eDisplayTransactionNeeded; + } + if (state.viewport != s.viewport) { + state.viewport = s.viewport; + flags |= eDisplayTransactionNeeded; } } + if (what & DisplayState::eDisplaySizeChanged) { + if (state.width != s.width) { + state.width = s.width; + flags |= eDisplayTransactionNeeded; + } + if (state.height != s.height) { + state.height = s.height; + flags |= eDisplayTransactionNeeded; + } + } + return flags; } @@ -3411,9 +3428,8 @@ bool callingThreadHasUnscopedSurfaceFlingerAccess() { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) && - !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) { + !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) { return false; } return true; @@ -3441,7 +3457,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState // If we are deferring transaction, make sure to push the pending state, as otherwise the // pending state will also be deferred. - if (what & layer_state_t::eDeferTransaction) { + if (what & layer_state_t::eDeferTransaction_legacy) { layer->pushPendingState(); } @@ -3499,6 +3515,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (layer->setColor(s.color)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eColorTransformChanged) { + if (layer->setColorTransform(s.colorTransform)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eMatrixChanged) { // TODO: b/109894387 // @@ -3526,12 +3547,8 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eCropChanged) { - if (layer->setCrop(s.crop, !geometryAppliesWithResize)) - flags |= eTraversalNeeded; - } - if (what & layer_state_t::eFinalCropChanged) { - if (layer->setFinalCrop(s.finalCrop, !geometryAppliesWithResize)) + if (what & layer_state_t::eCropChanged_legacy) { + if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize)) flags |= eTraversalNeeded; } if (what & layer_state_t::eLayerStackChanged) { @@ -3553,15 +3570,15 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState flags |= eTransactionNeeded|eTraversalNeeded|eDisplayLayerStackChanged; } } - if (what & layer_state_t::eDeferTransaction) { - if (s.barrierHandle != nullptr) { - layer->deferTransactionUntil(s.barrierHandle, s.frameNumber); - } else if (s.barrierGbp != nullptr) { - const sp<IGraphicBufferProducer>& gbp = s.barrierGbp; + if (what & layer_state_t::eDeferTransaction_legacy) { + if (s.barrierHandle_legacy != nullptr) { + layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy); + } else if (s.barrierGbp_legacy != nullptr) { + const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy; if (authenticateSurfaceTextureLocked(gbp)) { const auto& otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer(); - layer->deferTransactionUntil(otherLayer, s.frameNumber); + layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy); } else { ALOGE("Attempt to defer transaction to to an" " unrecognized GraphicBufferProducer"); @@ -3592,6 +3609,37 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState // We don't trigger a traversal here because if no other state is // changed, we don't want this to cause any more work } + if (what & layer_state_t::eTransformChanged) { + if (layer->setTransform(s.transform)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eTransformToDisplayInverseChanged) { + if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse)) + flags |= eTraversalNeeded; + } + if (what & layer_state_t::eCropChanged) { + if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eBufferChanged) { + if (layer->setBuffer(s.buffer)) 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; + } + if (what & layer_state_t::eHdrMetadataChanged) { + if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eSurfaceDamageRegionChanged) { + if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eApiChanged) { + if (layer->setApi(s.api)) flags |= eTraversalNeeded; + } + if (what & layer_state_t::eSidebandStreamChanged) { + if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; + } return flags; } @@ -3634,17 +3682,24 @@ status_t SurfaceFlinger::createLayer( String8 uniqueName = getUniqueLayerName(name); switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { - case ISurfaceComposerClient::eFXSurfaceNormal: - result = createBufferLayer(client, - uniqueName, w, h, flags, format, - handle, gbp, &layer); + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + result = createBufferQueueLayer(client, uniqueName, w, h, flags, format, handle, gbp, + &layer); break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer); + break; case ISurfaceComposerClient::eFXSurfaceColor: result = createColorLayer(client, uniqueName, w, h, flags, handle, &layer); break; + case ISurfaceComposerClient::eFXSurfaceContainer: + result = createContainerLayer(client, + uniqueName, w, h, flags, + handle, &layer); + break; default: result = BAD_VALUE; break; @@ -3695,15 +3750,17 @@ String8 SurfaceFlinger::getUniqueLayerName(const String8& name) }); } - ALOGD_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), uniqueName.c_str()); + ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(), + uniqueName.c_str()); return uniqueName; } -status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) -{ +status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format, sp<IBinder>* handle, + sp<IGraphicBufferProducer>* gbp, + sp<Layer>* outLayer) { // initialize the surfaces switch (format) { case PIXEL_FORMAT_TRANSPARENT: @@ -3715,27 +3772,50 @@ status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client, break; } - sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags); - status_t err = layer->setBuffers(w, h, format, flags); + sp<BufferQueueLayer> layer = + getFactory().createBufferQueueLayer(LayerCreationArgs(this, client, name, w, h, flags)); + status_t err = layer->setDefaultBufferProperties(w, h, format); if (err == NO_ERROR) { *handle = layer->getHandle(); *gbp = layer->getProducer(); *outLayer = layer; } - ALOGE_IF(err, "createBufferLayer() failed (%s)", strerror(-err)); + ALOGE_IF(err, "createBufferQueueLayer() failed (%s)", strerror(-err)); return err; } +status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + sp<IBinder>* handle, sp<Layer>* outLayer) { + sp<BufferStateLayer> layer = + getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags)); + *handle = layer->getHandle(); + *outLayer = layer; + + return NO_ERROR; +} + status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* handle, sp<Layer>* outLayer) { - *outLayer = new ColorLayer(this, client, name, w, h, flags); + *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags)); + *handle = (*outLayer)->getHandle(); + return NO_ERROR; +} + +status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, + const String8& name, uint32_t w, uint32_t h, uint32_t flags, + sp<IBinder>* handle, sp<Layer>* outLayer) +{ + *outLayer = + getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags)); *handle = (*outLayer)->getHandle(); return NO_ERROR; } + status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle) { // called by a client when it wants to remove a Layer @@ -3766,13 +3846,16 @@ status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer) // --------------------------------------------------------------------------- void SurfaceFlinger::onInitializeDisplays() { + const auto displayToken = mDisplayTokens[DisplayDevice::DISPLAY_PRIMARY]; + if (!displayToken) return; + // reset screen orientation and use primary layer stack Vector<ComposerState> state; Vector<DisplayState> displays; DisplayState d; d.what = DisplayState::eDisplayProjectionChanged | DisplayState::eLayerStackChanged; - d.token = mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]; + d.token = displayToken; d.layerStack = 0; d.orientation = DisplayState::eOrientationDefault; d.frame.makeInvalid(); @@ -3781,66 +3864,65 @@ void SurfaceFlinger::onInitializeDisplays() { d.height = 0; displays.add(d); setTransactionState(state, displays, 0); - setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL, - /*stateLockHeld*/ false); - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const auto display = getDisplayDevice(displayToken); + if (!display) return; + + setPowerModeInternal(display, HWC_POWER_MODE_NORMAL, /*stateLockHeld*/ false); + + const auto activeConfig = getHwComposer().getActiveConfig(display->getId()); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); // Use phase of 0 since phase is not known. // Use latency of 0, which will snap to the ideal latency. - setCompositorTimingSnapped(0, period, 0); + DisplayStatInfo stats{0 /* vsyncTime */, period /* vsyncPeriod */}; + setCompositorTimingSnapped(stats, 0); } void SurfaceFlinger::initializeDisplays() { - class MessageScreenInitialized : public MessageBase { - SurfaceFlinger* flinger; - public: - explicit MessageScreenInitialized(SurfaceFlinger* flinger) : flinger(flinger) { } - virtual bool handler() { - flinger->onInitializeDisplays(); - return true; - } - }; - sp<MessageBase> msg = new MessageScreenInitialized(this); - postMessageAsync(msg); // we may be called from main thread, use async message + // Async since we may be called from the main thread. + postMessageAsync(new LambdaMessage([this] { onInitializeDisplays(); })); } -void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, - int mode, bool stateLockHeld) { - ALOGD("Set power mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), - this); - int32_t type = hw->getDisplayType(); - int currentMode = hw->getPowerMode(); +void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode, + bool stateLockHeld) { + const int32_t displayId = display->getId(); + ALOGD("Setting power mode %d on display %d", mode, displayId); + int currentMode = display->getPowerMode(); if (mode == currentMode) { return; } - hw->setPowerMode(mode); - if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { + if (display->isVirtual()) { ALOGW("Trying to set power mode for virtual display"); return; } + display->setPowerMode(mode); + if (mInterceptor->isEnabled()) { ConditionalLock lock(mStateLock, !stateLockHeld); - ssize_t idx = mCurrentState.displays.indexOfKey(hw->getDisplayToken()); + ssize_t idx = mCurrentState.displays.indexOfKey(display->getDisplayToken()); if (idx < 0) { ALOGW("Surface Interceptor SavePowerMode: invalid display token"); return; } - mInterceptor->savePowerModeUpdate(mCurrentState.displays.valueAt(idx).displayId, mode); + mInterceptor->savePowerModeUpdate(mCurrentState.displays.valueAt(idx).sequenceId, mode); } + int32_t type = display->getDisplayType(); if (currentMode == HWC_POWER_MODE_OFF) { // Turn on the display getHwComposer().setPowerMode(type, mode); - if (type == DisplayDevice::DISPLAY_PRIMARY && - mode != HWC_POWER_MODE_DOZE_SUSPEND) { + if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) { // FIXME: eventthread only knows about the main display right now - mEventThread->onScreenAcquired(); + if (mUseScheduler) { + mScheduler->onScreenAcquired(mAppConnectionHandle); + } else { + mEventThread->onScreenAcquired(); + } resyncToHardwareVsync(true); } @@ -3860,12 +3942,18 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, ALOGW("Couldn't set SCHED_OTHER on display off"); } - if (type == DisplayDevice::DISPLAY_PRIMARY && - currentMode != HWC_POWER_MODE_DOZE_SUSPEND) { - disableHardwareVsync(true); // also cancels any in-progress resync - + if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) { + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + disableHardwareVsync(true); // also cancels any in-progress resync + } // FIXME: eventthread only knows about the main display right now - mEventThread->onScreenReleased(); + if (mUseScheduler) { + mScheduler->onScreenReleased(mAppConnectionHandle); + } else { + mEventThread->onScreenReleased(); + } } getHwComposer().setPowerMode(type, mode); @@ -3875,53 +3963,55 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, mode == HWC_POWER_MODE_NORMAL) { // Update display while dozing getHwComposer().setPowerMode(type, mode); - if (type == DisplayDevice::DISPLAY_PRIMARY && - currentMode == HWC_POWER_MODE_DOZE_SUSPEND) { + if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) { // FIXME: eventthread only knows about the main display right now - mEventThread->onScreenAcquired(); + if (mUseScheduler) { + mScheduler->onScreenAcquired(mAppConnectionHandle); + } else { + mEventThread->onScreenAcquired(); + } resyncToHardwareVsync(true); } } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) { // Leave display going to doze - if (type == DisplayDevice::DISPLAY_PRIMARY) { - disableHardwareVsync(true); // also cancels any in-progress resync + if (display->isPrimary()) { + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + disableHardwareVsync(true); // also cancels any in-progress resync + } // FIXME: eventthread only knows about the main display right now - mEventThread->onScreenReleased(); + if (mUseScheduler) { + mScheduler->onScreenReleased(mAppConnectionHandle); + } else { + mEventThread->onScreenReleased(); + } } getHwComposer().setPowerMode(type, mode); } else { ALOGE("Attempting to set unknown power mode: %d\n", mode); getHwComposer().setPowerMode(type, mode); } - ALOGD("Finished set power mode=%d, type=%d", mode, hw->getDisplayType()); + + if (display->isPrimary()) { + mTimeStats.setPowerMode(mode); + } + + ALOGD("Finished setting power mode %d on display %d", mode, displayId); } -void SurfaceFlinger::setPowerMode(const sp<IBinder>& display, int mode) { - class MessageSetPowerMode: public MessageBase { - SurfaceFlinger& mFlinger; - sp<IBinder> mDisplay; - int mMode; - public: - MessageSetPowerMode(SurfaceFlinger& flinger, - const sp<IBinder>& disp, int mode) : mFlinger(flinger), - mDisplay(disp) { mMode = mode; } - virtual bool handler() { - sp<DisplayDevice> hw(mFlinger.getDisplayDevice(mDisplay)); - if (hw == nullptr) { - ALOGE("Attempt to set power mode = %d for null display %p", - mMode, mDisplay.get()); - } else if (hw->getDisplayType() >= DisplayDevice::DISPLAY_VIRTUAL) { - ALOGW("Attempt to set power mode = %d for virtual display", - mMode); - } else { - mFlinger.setPowerModeInternal( - hw, mMode, /*stateLockHeld*/ false); - } - return true; +void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { + postMessageSync(new LambdaMessage([&] { + const auto display = getDisplayDevice(displayToken); + if (!display) { + ALOGE("Attempt to set power mode %d for invalid display token %p", mode, + displayToken.get()); + } else if (display->isVirtual()) { + ALOGW("Attempt to set power mode %d for virtual display", mode); + } else { + setPowerModeInternal(display, mode, /*stateLockHeld*/ false); } - }; - sp<MessageBase> msg = new MessageSetPowerMode(*this, display, mode); - postMessageSync(msg); + })); } // --------------------------------------------------------------------------- @@ -3979,7 +4069,7 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro if ((index < numArgs) && (args[index] == String16("--dispsync"))) { index++; - mPrimaryDispSync.dump(result); + mPrimaryDispSync->dump(result); dumpAll = false; } @@ -4031,6 +4121,20 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro dumpAll = false; } + if ((index < numArgs) && + (args[index] == String16("--frame-composition"))) { + index++; + dumpFrameCompositionInfo(result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--display-identification"))) { + index++; + dumpDisplayIdentificationData(result); + dumpAll = false; + } + if ((index < numArgs) && (args[index] == String16("--timestats"))) { index++; mTimeStats.parseArgs(asProto, args, index, result); @@ -4072,9 +4176,12 @@ void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index index++; } - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); - const nsecs_t period = activeConfig->getVsyncPeriod(); - result.appendFormat("%" PRId64 "\n", period); + if (const auto displayId = DisplayDevice::DISPLAY_PRIMARY; + getHwComposer().isConnected(displayId)) { + const auto activeConfig = getBE().mHwc->getActiveConfig(displayId); + const nsecs_t period = activeConfig->getVsyncPeriod(); + result.appendFormat("%" PRId64 "\n", period); + } if (name.isEmpty()) { mAnimFrameTracker.dumpStats(result); @@ -4210,33 +4317,100 @@ void SurfaceFlinger::dumpBufferingStats(String8& result) const { result.append("\n"); } +void SurfaceFlinger::dumpDisplayIdentificationData(String8& result) const { + for (const auto& [token, display] : mDisplays) { + const int32_t displayId = display->getId(); + const auto hwcDisplayId = getHwComposer().getHwcDisplayId(displayId); + if (!hwcDisplayId) { + continue; + } + + result.appendFormat("Display %d (HWC display %" PRIu64 "): ", displayId, *hwcDisplayId); + uint8_t port; + DisplayIdentificationData data; + if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) { + result.append("no identification data\n"); + continue; + } + + if (!isEdid(data)) { + result.append("unknown identification data: "); + for (uint8_t byte : data) { + result.appendFormat("%x ", byte); + } + result.append("\n"); + continue; + } + + const auto edid = parseEdid(data); + if (!edid) { + result.append("invalid EDID: "); + for (uint8_t byte : data) { + result.appendFormat("%x ", byte); + } + result.append("\n"); + continue; + } + + result.appendFormat("port=%u pnpId=%s displayName=\"", port, edid->pnpId.data()); + result.append(edid->displayName.data(), edid->displayName.length()); + result.append("\"\n"); + } + result.append("\n"); +} + void SurfaceFlinger::dumpWideColorInfo(String8& result) const { - result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay); + result.appendFormat("Device has wide color display: %d\n", hasWideColorDisplay); + result.appendFormat("Device uses color management: %d\n", useColorManagement); result.appendFormat("DisplayColorSetting: %s\n", decodeDisplayColorSetting(mDisplayColorSetting).c_str()); // TODO: print out if wide-color mode is active or not - for (size_t d = 0; d < mDisplays.size(); d++) { - const sp<const DisplayDevice>& displayDevice(mDisplays[d]); - int32_t hwcId = displayDevice->getHwcDisplayId(); - if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { + for (const auto& [token, display] : mDisplays) { + const int32_t displayId = display->getId(); + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { continue; } - result.appendFormat("Display %d color modes:\n", hwcId); - std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId); + result.appendFormat("Display %d color modes:\n", displayId); + std::vector<ColorMode> modes = getHwComposer().getColorModes(displayId); for (auto&& mode : modes) { result.appendFormat(" %s (%d)\n", decodeColorMode(mode).c_str(), mode); } - ColorMode currentMode = displayDevice->getActiveColorMode(); + ColorMode currentMode = display->getActiveColorMode(); result.appendFormat(" Current color mode: %s (%d)\n", decodeColorMode(currentMode).c_str(), currentMode); } result.append("\n"); } +void SurfaceFlinger::dumpFrameCompositionInfo(String8& result) const { + std::string stringResult; + + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { + continue; + } + + const auto& compositionInfoIt = getBE().mEndOfFrameCompositionInfo.find(displayId); + if (compositionInfoIt == getBE().mEndOfFrameCompositionInfo.end()) { + break; + } + const auto& compositionInfoList = compositionInfoIt->second; + stringResult += base::StringPrintf("Display: %d\n", displayId); + stringResult += base::StringPrintf("numComponents: %zu\n", compositionInfoList.size()); + for (const auto& compositionInfo : compositionInfoList) { + compositionInfo.dump(stringResult, nullptr); + stringResult += base::StringPrintf("\n"); + } + } + + result.append(stringResult.c_str()); +} + LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const { LayersProto layersProto; const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; @@ -4249,23 +4423,22 @@ LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const return layersProto; } -LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(int32_t hwcId) const { +LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& display) const { LayersProto layersProto; - const sp<DisplayDevice>& displayDevice(mDisplays[hwcId]); SizeProto* resolution = layersProto.mutable_resolution(); - resolution->set_w(displayDevice->getWidth()); - resolution->set_h(displayDevice->getHeight()); + resolution->set_w(display.getWidth()); + resolution->set_h(display.getHeight()); - layersProto.set_color_mode(decodeColorMode(displayDevice->getActiveColorMode())); - layersProto.set_color_transform(decodeColorTransform(displayDevice->getColorTransform())); - layersProto.set_global_transform( - static_cast<int32_t>(displayDevice->getOrientationTransform())); + layersProto.set_color_mode(decodeColorMode(display.getActiveColorMode())); + layersProto.set_color_transform(decodeColorTransform(display.getColorTransform())); + layersProto.set_global_transform(static_cast<int32_t>(display.getOrientationTransform())); + const int32_t displayId = display.getId(); mDrawingState.traverseInZOrder([&](Layer* layer) { - if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(hwcId)) { + if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(displayId)) { LayerProto* layerProto = layersProto.add_layers(); - layer->writeToProto(layerProto, hwcId); + layer->writeToProto(layerProto, displayId); } }); @@ -4286,9 +4459,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, // figure out if we're stuck somewhere const nsecs_t now = systemTime(); - const nsecs_t inSwapBuffers(mDebugInSwapBuffers); const nsecs_t inTransaction(mDebugInTransaction); - nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; /* @@ -4303,6 +4474,9 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, appendGuiConfigString(result); result.append("\n"); + result.append("\nDisplay identification data:\n"); + dumpDisplayIdentificationData(result); + result.append("\nWide-Color information:\n"); dumpWideColorInfo(result); @@ -4312,28 +4486,32 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.append(SyncFeatures::getInstance().toString()); result.append("\n"); - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); - colorizer.bold(result); - result.append("DispSync configuration: "); + result.append("DispSync configuration:\n"); colorizer.reset(result); + const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets(); const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets(); - result.appendFormat( - "app phase %" PRId64 " ns, " - "sf phase %" PRId64 " ns, " - "early app phase %" PRId64 " ns, " - "early sf phase %" PRId64 " ns, " - "early app gl phase %" PRId64 " ns, " - "early sf gl phase %" PRId64 " ns, " - "present offset %" PRId64 " ns (refresh %" PRId64 " ns)", - vsyncPhaseOffsetNs, - sfVsyncPhaseOffsetNs, - appEarlyOffset, - sfEarlyOffset, - appEarlyGlOffset, - sfEarlyOffset, - dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod()); + if (const auto displayId = DisplayDevice::DISPLAY_PRIMARY; + getHwComposer().isConnected(displayId)) { + const auto activeConfig = getHwComposer().getActiveConfig(displayId); + result.appendFormat("Display %d: " + "app phase %" PRId64 " ns, " + "sf phase %" PRId64 " ns, " + "early app phase %" PRId64 " ns, " + "early sf phase %" PRId64 " ns, " + "early app gl phase %" PRId64 " ns, " + "early sf gl phase %" PRId64 " ns, " + "present offset %" PRId64 " ns (refresh %" PRId64 " ns)", + displayId, + vsyncPhaseOffsetNs, + sfVsyncPhaseOffsetNs, + appEarlyOffset, + sfEarlyOffset, + appEarlyGlOffset, + sfEarlyGlOffset, + dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod()); + } result.append("\n"); // Dump static screen stats @@ -4341,6 +4519,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, dumpStaticScreenStats(result); result.append("\n"); + result.appendFormat("Missed frame count: %u\n\n", mFrameMissedCount.load()); + dumpBufferingStats(result); /* @@ -4352,9 +4532,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize); colorizer.reset(result); - LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); - auto layerTree = LayerProtoParser::generateLayerTree(layersProto); - result.append(LayerProtoParser::layersToString(std::move(layerTree)).c_str()); + { + LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); + auto layerTree = LayerProtoParser::generateLayerTree(layersProto); + result.append(LayerProtoParser::layerTreeToString(layerTree).c_str()); + result.append("\n"); + } + + result.append("\nFrame-Composition information:\n"); + dumpFrameCompositionInfo(result); result.append("\n"); /* @@ -4364,9 +4550,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, colorizer.bold(result); result.appendFormat("Displays (%zu entries)\n", mDisplays.size()); colorizer.reset(result); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { - const sp<const DisplayDevice>& hw(mDisplays[dpy]); - hw->dump(result); + for (const auto& [token, display] : mDisplays) { + display->dump(result); } result.append("\n"); @@ -4379,42 +4564,40 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, colorizer.reset(result); HWComposer& hwc(getHwComposer()); - sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); + const auto display = getDefaultDisplayDeviceLocked(); getBE().mRenderEngine->dump(result); - if (hw) { - hw->undefinedRegion.dump(result, "undefinedRegion"); - result.appendFormat(" orientation=%d, isDisplayOn=%d\n", - hw->getOrientation(), hw->isDisplayOn()); - } - result.appendFormat( - " last eglSwapBuffers() time: %f us\n" - " last transaction time : %f us\n" - " transaction-flags : %08x\n" - " refresh-rate : %f fps\n" - " x-dpi : %f\n" - " y-dpi : %f\n" - " gpu_to_cpu_unsupported : %d\n" - , - mLastSwapBufferTime/1000.0, - mLastTransactionTime/1000.0, - mTransactionFlags, - 1e9 / activeConfig->getVsyncPeriod(), - activeConfig->getDpiX(), - activeConfig->getDpiY(), - !mGpuToCpuSupported); - - result.appendFormat(" eglSwapBuffers time: %f us\n", - inSwapBuffersDuration/1000.0); + if (display) { + display->undefinedRegion.dump(result, "undefinedRegion"); + result.appendFormat(" orientation=%d, isPoweredOn=%d\n", display->getOrientation(), + display->isPoweredOn()); + } + result.appendFormat(" transaction-flags : %08x\n" + " gpu_to_cpu_unsupported : %d\n", + mTransactionFlags.load(), !mGpuToCpuSupported); + + if (display) { + const auto activeConfig = getHwComposer().getActiveConfig(display->getId()); + result.appendFormat(" refresh-rate : %f fps\n" + " x-dpi : %f\n" + " y-dpi : %f\n", + 1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(), + activeConfig->getDpiY()); + } result.appendFormat(" transaction time: %f us\n", inTransactionDuration/1000.0); + result.appendFormat(" use Scheduler: %s\n", mUseScheduler ? "true" : "false"); /* * VSYNC state */ - mEventThread->dump(result); + if (mUseScheduler) { + mScheduler->dump(mAppConnectionHandle, result); + } else { + mEventThread->dump(result); + } result.append("\n"); /* @@ -4426,18 +4609,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, /* * HWC layer minidump */ - for (size_t d = 0; d < mDisplays.size(); d++) { - const sp<const DisplayDevice>& displayDevice(mDisplays[d]); - int32_t hwcId = displayDevice->getHwcDisplayId(); - if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { + for (const auto& [token, display] : mDisplays) { + const int32_t displayId = display->getId(); + if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { continue; } - result.appendFormat("Display %d HWC layers:\n", hwcId); + result.appendFormat("Display %d HWC layers:\n", displayId); Layer::miniDumpHeader(result); - mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->miniDump(result, hwcId); - }); + mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, displayId); }); result.append("\n"); } @@ -4468,22 +4648,17 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, } } -const Vector< sp<Layer> >& -SurfaceFlinger::getLayerSortedByZForHwcDisplay(int id) { +const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(int32_t displayId) { // Note: mStateLock is held here - wp<IBinder> dpy; - for (size_t i=0 ; i<mDisplays.size() ; i++) { - if (mDisplays.valueAt(i)->getHwcDisplayId() == id) { - dpy = mDisplays.keyAt(i); - break; + for (const auto& [token, display] : mDisplays) { + if (display->getId() == displayId) { + return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ(); } } - if (dpy == nullptr) { - ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id); - // Just use the primary display so we have something to return - dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY); - } - return getDisplayDeviceLocked(dpy)->getVisibleLayersSortedByZ(); + + ALOGE("%s: Invalid display %d", __FUNCTION__, displayId); + static const Vector<sp<Layer>> empty; + return empty; } bool SurfaceFlinger::startDdmConnection() @@ -4529,51 +4704,66 @@ void SurfaceFlinger::updateColorMatrixLocked() { } status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { - switch (code) { - case CREATE_CONNECTION: - case CREATE_DISPLAY: +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wswitch-enum" + switch (static_cast<ISurfaceComposerTag>(code)) { + // These methods should at minimum make sure that the client requested + // access to SF. case BOOT_FINISHED: case CLEAR_ANIMATION_FRAME_STATS: + case CREATE_CONNECTION: + case CREATE_DISPLAY: + case DESTROY_DISPLAY: + case ENABLE_VSYNC_INJECTIONS: + case GET_ACTIVE_COLOR_MODE: case GET_ANIMATION_FRAME_STATS: - case SET_POWER_MODE: case GET_HDR_CAPABILITIES: - case ENABLE_VSYNC_INJECTIONS: + case SET_ACTIVE_CONFIG: + case SET_ACTIVE_COLOR_MODE: case INJECT_VSYNC: - { - // codes that require permission check + case SET_POWER_MODE: { if (!callingThreadHasUnscopedSurfaceFlingerAccess()) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", ipc->getCallingPid(), ipc->getCallingUid()); return PERMISSION_DENIED; } - break; - } - /* - * Calling setTransactionState is safe, because you need to have been - * granted a reference to Client* and Handle* to do anything with it. - * - * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h - */ - case SET_TRANSACTION_STATE: - case CREATE_SCOPED_CONNECTION: - { return OK; } - case CAPTURE_SCREEN: - { - // codes that require permission check + case GET_LAYER_DEBUG_INFO: { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_GRAPHICS) && - !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { - ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } - break; + return OK; + } + // Used by apps to hook Choreographer to SurfaceFlinger. + case CREATE_DISPLAY_EVENT_CONNECTION: + // The following calls are currently used by clients that do not + // request necessary permissions. However, they do not expose any secret + // information, so it is OK to pass them. + case AUTHENTICATE_SURFACE: + case GET_ACTIVE_CONFIG: + case GET_BUILT_IN_DISPLAY: + case GET_DISPLAY_COLOR_MODES: + case GET_DISPLAY_CONFIGS: + case GET_DISPLAY_STATS: + case GET_SUPPORTED_FRAME_TIMESTAMPS: + // Calling setTransactionState is safe, because you need to have been + // granted a reference to Client* and Handle* to do anything with it. + case SET_TRANSACTION_STATE: + // Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h + case CREATE_SCOPED_CONNECTION: + case IS_COLOR_MANAGEMET_USED: + case GET_COMPOSITION_PREFERENCE: { + return OK; } - case CAPTURE_LAYERS: { + case CAPTURE_LAYERS: + case CAPTURE_SCREEN: { + // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); @@ -4582,15 +4772,37 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } - break; + return OK; + } + // The following codes are deprecated and should never be allowed to access SF. + case CONNECT_DISPLAY_UNUSED: + case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: { + ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code); + return PERMISSION_DENIED; } } - return OK; + + // These codes are used for the IBinder protocol to either interrogate the recipient + // side of the transaction for its canonical interface descriptor or to dump its state. + // We let them pass by default. + if (code == IBinder::INTERFACE_TRANSACTION || code == IBinder::DUMP_TRANSACTION || + code == IBinder::PING_TRANSACTION || code == IBinder::SHELL_COMMAND_TRANSACTION || + code == IBinder::SYSPROPS_TRANSACTION) { + return OK; + } + // Numbers from 1000 to 1030 are currently use for backdoors. The code + // in onTransact verifies that the user is root, and has access to use SF. + if (code >= 1000 && code <= 1030) { + ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); + return OK; + } + ALOGE("Permission Denial: SurfaceFlinger did not recognize request code: %u", code); + return PERMISSION_DENIED; +#pragma clang diagnostic pop } -status_t SurfaceFlinger::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ +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; @@ -4655,8 +4867,12 @@ status_t SurfaceFlinger::onTransact( reply->writeInt32(mDebugDisableHWC); return NO_ERROR; case 1013: { - sp<const DisplayDevice> hw(getDefaultDisplayDevice()); - reply->writeInt32(hw->getPageFlipCount()); + const auto display = getDefaultDisplayDevice(); + if (!display) { + return NAME_NOT_FOUND; + } + + reply->writeInt32(display->getPageFlipCount()); return NO_ERROR; } case 1014: { @@ -4715,7 +4931,8 @@ status_t SurfaceFlinger::onTransact( // Needs to be shifted to proper binder interface when we productize case 1016: { n = data.readInt32(); - mPrimaryDispSync.setRefreshSkipCount(n); + // TODO(b/113612090): Evaluate if this can be removed. + mPrimaryDispSync->setRefreshSkipCount(n); return NO_ERROR; } case 1017: { @@ -4725,12 +4942,20 @@ status_t SurfaceFlinger::onTransact( } case 1018: { // Modify Choreographer's phase offset n = data.readInt32(); - mEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + if (mUseScheduler) { + mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n)); + } else { + mEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + } return NO_ERROR; } case 1019: { // Modify SurfaceFlinger's phase offset n = data.readInt32(); - mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + if (mUseScheduler) { + mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n)); + } else { + mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + } return NO_ERROR; } case 1020: { // Layer updates interceptor @@ -4763,9 +4988,9 @@ status_t SurfaceFlinger::onTransact( repaintEverything(); return NO_ERROR; } - case 1024: { // Is wide color gamut rendering/color management supported? - reply->writeBool(hasWideColorDisplay); - return NO_ERROR; + // Deprecate, use 1030 to check whether the device is color managed. + case 1024: { + return NAME_NOT_FOUND; } case 1025: { // Set layer tracing n = data.readInt32(); @@ -4787,35 +5012,47 @@ status_t SurfaceFlinger::onTransact( } // Is a DisplayColorSetting supported? case 1027: { - sp<const DisplayDevice> hw(getDefaultDisplayDevice()); - if (!hw) { + const auto display = getDefaultDisplayDevice(); + if (!display) { return NAME_NOT_FOUND; } DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32()); switch (setting) { case DisplayColorSetting::MANAGED: - reply->writeBool(hasWideColorDisplay); + reply->writeBool(useColorManagement); break; case DisplayColorSetting::UNMANAGED: reply->writeBool(true); break; case DisplayColorSetting::ENHANCED: - reply->writeBool(hw->hasRenderIntent(RenderIntent::ENHANCE)); + reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE)); break; default: // vendor display color setting - reply->writeBool(hw->hasRenderIntent(static_cast<RenderIntent>(setting))); + reply->writeBool( + display->hasRenderIntent(static_cast<RenderIntent>(setting))); break; } return NO_ERROR; } + // Is VrFlinger active? + case 1028: { + Mutex::Autolock _l(mStateLock); + reply->writeBool(getBE().mHwc->isUsingVrComposer()); + return NO_ERROR; + } + // Is device color managed? + case 1030: { + reply->writeBool(useColorManagement); + return NO_ERROR; + } } } return err; } void SurfaceFlinger::repaintEverything() { - android_atomic_or(1, &mRepaintEverything); + mRepaintEverything = true; signalTransaction(); } @@ -4832,58 +5069,70 @@ private: const int mApi; }; -status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, +status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, + sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { ATRACE_CALL(); - if (CC_UNLIKELY(display == 0)) return BAD_VALUE; + if (!displayToken) return BAD_VALUE; - const sp<const DisplayDevice> device(getDisplayDeviceLocked(display)); - if (CC_UNLIKELY(device == 0)) return BAD_VALUE; + auto renderAreaRotation = fromSurfaceComposerRotation(rotation); - const Rect& dispScissor = device->getScissor(); - if (!dispScissor.isEmpty()) { - sourceCrop.set(dispScissor); - // adb shell screencap will default reqWidth and reqHeight to zeros. + sp<DisplayDevice> display; + { + Mutex::Autolock _l(mStateLock); + + display = getDisplayDeviceLocked(displayToken); + if (!display) return BAD_VALUE; + + // set the requested width/height to the logical display viewport size + // by default if (reqWidth == 0 || reqHeight == 0) { - reqWidth = uint32_t(device->getViewport().width()); - reqHeight = uint32_t(device->getViewport().height()); + reqWidth = uint32_t(display->getViewport().width()); + reqHeight = uint32_t(display->getViewport().height()); } } - DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation); + DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace, + renderAreaRotation); auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this, - device, minLayerZ, maxLayerZ, std::placeholders::_1); - return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform); + display, std::placeholders::_1); + return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, + useIdentityTransform); } status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) { ATRACE_CALL(); class LayerRenderArea : public RenderArea { public: LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop, - int32_t reqWidth, int32_t reqHeight, bool childrenOnly) - : RenderArea(reqHeight, reqWidth, CaptureFill::CLEAR), + int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace, + bool childrenOnly) + : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace), mLayer(layer), mCrop(crop), + mNeedsFiltering(false), mFlinger(flinger), mChildrenOnly(childrenOnly) {} - const Transform& getTransform() const override { return mTransform; } + const ui::Transform& getTransform() const override { return mTransform; } Rect getBounds() const override { const Layer::State& layerState(mLayer->getDrawingState()); - return Rect(layerState.active.w, layerState.active.h); + return Rect(mLayer->getActiveWidth(layerState), mLayer->getActiveHeight(layerState)); + } + int getHeight() const override { + return mLayer->getActiveHeight(mLayer->getDrawingState()); } - int getHeight() const override { return mLayer->getDrawingState().active.h; } - int getWidth() const override { return mLayer->getDrawingState().active.w; } + int getWidth() const override { return mLayer->getActiveWidth(mLayer->getDrawingState()); } bool isSecure() const override { return false; } - bool needsFiltering() const override { return false; } + bool needsFiltering() const override { return mNeedsFiltering; } Rect getSourceCrop() const override { if (mCrop.isEmpty()) { return getBounds(); @@ -4904,14 +5153,19 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, }; void render(std::function<void()> drawLayers) override { + const Rect sourceCrop = getSourceCrop(); + // no need to check rotation because there is none + mNeedsFiltering = sourceCrop.width() != getReqWidth() || + sourceCrop.height() != getReqHeight(); + if (!mChildrenOnly) { mTransform = mLayer->getTransform().inverse(); drawLayers(); } else { Rect bounds = getBounds(); - screenshotParentLayer = - new ContainerLayer(mFlinger, nullptr, String8("Screenshot Parent"), - bounds.getWidth(), bounds.getHeight(), 0); + screenshotParentLayer = mFlinger->getFactory().createContainerLayer( + LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), + bounds.getWidth(), bounds.getHeight(), 0)); ReparentForDrawing reparent(mLayer, screenshotParentLayer); drawLayers(); @@ -4925,7 +5179,8 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, // 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; - Transform mTransform; + ui::Transform mTransform; + bool mNeedsFiltering; SurfaceFlinger* mFlinger; const bool mChildrenOnly; @@ -4949,18 +5204,26 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, Rect crop(sourceCrop); if (sourceCrop.width() <= 0) { crop.left = 0; - crop.right = parent->getCurrentState().active.w; + crop.right = parent->getActiveWidth(parent->getCurrentState()); } if (sourceCrop.height() <= 0) { crop.top = 0; - crop.bottom = parent->getCurrentState().active.h; + crop.bottom = parent->getActiveHeight(parent->getCurrentState()); } int32_t reqWidth = crop.width() * frameScale; int32_t reqHeight = crop.height() * frameScale; - LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly); + // really small crop or frameScale + if (reqWidth <= 0) { + reqWidth = 1; + } + if (reqHeight <= 0) { + reqHeight = 1; + } + + LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly); auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) { parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { @@ -4972,21 +5235,23 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, visitor(layer); }); }; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, false); + return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false); } status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, sp<GraphicBuffer>* outBuffer, + const ui::PixelFormat reqPixelFormat, bool useIdentityTransform) { ATRACE_CALL(); - renderArea.updateDimensions(mPrimaryDisplayOrientation); - + // TODO(b/116112787) Make buffer usage a parameter. const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - *outBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), - HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot"); + *outBuffer = + getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), + static_cast<android_pixel_format>(reqPixelFormat), 1, + usage, "screenshot"); // This mutex protects syncFd and captureResult for communication of the return values from the // main thread back to this Binder thread @@ -4999,7 +5264,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, const int uid = IPCThreadState::self()->getCallingUid(); const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; - sp<LambdaMessage> message = new LambdaMessage([&]() { + sp<LambdaMessage> message = new LambdaMessage([&] { // If there is a refresh pending, bug out early and tell the binder thread to try again // after the refresh. if (mRefreshPending) { @@ -5014,7 +5279,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, int fd = -1; { Mutex::Autolock _l(mStateLock); - renderArea.render([&]() { + renderArea.render([&] { result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(), useIdentityTransform, forSystem, &fd); }); @@ -5030,14 +5295,14 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, status_t result = postMessageAsync(message); if (result == NO_ERROR) { - captureCondition.wait(captureLock, [&]() { return captureResult; }); + captureCondition.wait(captureLock, [&] { return captureResult; }); while (*captureResult == EAGAIN) { captureResult.reset(); result = postMessageAsync(message); if (result != NO_ERROR) { return result; } - captureCondition.wait(captureLock, [&]() { return captureResult; }); + captureCondition.wait(captureLock, [&] { return captureResult; }); } result = *captureResult; } @@ -5051,106 +5316,25 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, } void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, - TraverseLayersFunction traverseLayers, bool yswap, + TraverseLayersFunction traverseLayers, bool useIdentityTransform) { ATRACE_CALL(); auto& engine(getRenderEngine()); - // get screen geometry - const auto raWidth = renderArea.getWidth(); - const auto raHeight = renderArea.getHeight(); - const auto reqWidth = renderArea.getReqWidth(); const auto reqHeight = renderArea.getReqHeight(); - Rect sourceCrop = renderArea.getSourceCrop(); - - bool filtering = false; - if (mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) { - filtering = static_cast<int32_t>(reqWidth) != raHeight || - static_cast<int32_t>(reqHeight) != raWidth; - } else { - filtering = static_cast<int32_t>(reqWidth) != raWidth || - static_cast<int32_t>(reqHeight) != raHeight; - } - - // if a default or invalid sourceCrop is passed in, set reasonable values - if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) { - sourceCrop.setLeftTop(Point(0, 0)); - sourceCrop.setRightBottom(Point(raWidth, raHeight)); - } else if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) { - Transform tr; - uint32_t flags = 0x00; - switch (mPrimaryDisplayOrientation) { - case DisplayState::eOrientation90: - flags = Transform::ROT_90; - break; - case DisplayState::eOrientation180: - flags = Transform::ROT_180; - break; - case DisplayState::eOrientation270: - flags = Transform::ROT_270; - break; - } - tr.set(flags, raWidth, raHeight); - sourceCrop = tr.transform(sourceCrop); - } - - // ensure that sourceCrop is inside screen - if (sourceCrop.left < 0) { - ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left); - } - if (sourceCrop.right > raWidth) { - ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth); - } - if (sourceCrop.top < 0) { - ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top); - } - if (sourceCrop.bottom > raHeight) { - ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight); - } + const auto sourceCrop = renderArea.getSourceCrop(); + const auto rotation = renderArea.getRotationFlags(); - // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC - engine.setOutputDataSpace(Dataspace::SRGB); + engine.setOutputDataSpace(renderArea.getReqDataSpace()); engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance); // make sure to clear all GL error flags engine.checkErrors(); - Transform::orientation_flags rotation = renderArea.getRotationFlags(); - if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) { - // convert hw orientation into flag presentation - // here inverse transform needed - uint8_t hw_rot_90 = 0x00; - uint8_t hw_flip_hv = 0x00; - switch (mPrimaryDisplayOrientation) { - case DisplayState::eOrientation90: - hw_rot_90 = Transform::ROT_90; - hw_flip_hv = Transform::ROT_180; - break; - case DisplayState::eOrientation180: - hw_flip_hv = Transform::ROT_180; - break; - case DisplayState::eOrientation270: - hw_rot_90 = Transform::ROT_90; - break; - } - - // transform flags operation - // 1) flip H V if both have ROT_90 flag - // 2) XOR these flags - uint8_t rotation_rot_90 = rotation & Transform::ROT_90; - uint8_t rotation_flip_hv = rotation & Transform::ROT_180; - if (rotation_rot_90 & hw_rot_90) { - rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180; - } - rotation = static_cast<Transform::orientation_flags> - ((rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv)); - } - // set-up our viewport - engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap, - rotation); + engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, rotation); engine.disableTexturing(); const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill()); @@ -5158,9 +5342,9 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, engine.clearWithColor(0, 0, 0, alpha); traverseLayers([&](Layer* layer) { - if (filtering) layer->setFiltering(true); + engine.setColorTransform(layer->getColorTransform()); layer->draw(renderArea, useIdentityTransform); - if (filtering) layer->setFiltering(false); + engine.setColorTransform(mat4()); }); } @@ -5188,7 +5372,7 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, // this binds the given EGLImage as a framebuffer for the // duration of this scope. - RE::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer); + renderengine::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer); if (bufferBond.getStatus() != NO_ERROR) { ALOGE("got ANWB binding error while taking screenshot"); return INVALID_OPERATION; @@ -5198,53 +5382,17 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, // via an FBO, which means we didn't have to create // an EGLSurface and therefore we're not // dependent on the context's EGLConfig. - renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform); + renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform); - if (DEBUG_SCREENSHOTS) { + base::unique_fd syncFd = getRenderEngine().flush(); + if (syncFd < 0) { getRenderEngine().finish(); - *outSyncFd = -1; - - const auto reqWidth = renderArea.getReqWidth(); - const auto reqHeight = renderArea.getReqHeight(); - - uint32_t* pixels = new uint32_t[reqWidth*reqHeight]; - getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels); - checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers); - delete [] pixels; - } else { - base::unique_fd syncFd = getRenderEngine().flush(); - if (syncFd < 0) { - getRenderEngine().finish(); - } - *outSyncFd = syncFd.release(); } + *outSyncFd = syncFd.release(); return NO_ERROR; } -void SurfaceFlinger::checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, - TraverseLayersFunction traverseLayers) { - if (DEBUG_SCREENSHOTS) { - for (size_t y = 0; y < h; y++) { - uint32_t const* p = (uint32_t const*)vaddr + y * s; - for (size_t x = 0; x < w; x++) { - if (p[x] != 0xFF000000) return; - } - } - ALOGE("*** we just took a black screenshot ***"); - - size_t i = 0; - traverseLayers([&](Layer* layer) { - const Layer::State& state(layer->getDrawingState()); - ALOGE("%c index=%zu, name=%s, layerStack=%d, z=%d, visible=%d, flags=%x, alpha=%.3f", - layer->isVisible() ? '+' : '-', i, layer->getName().string(), - layer->getLayerStack(), state.z, layer->isVisible(), state.flags, - static_cast<float>(state.color.a)); - i++; - }); - } -} - // --------------------------------------------------------------------------- void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const { @@ -5255,22 +5403,17 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& layersSortedByZ.traverseInReverseZOrder(stateSet, visitor); } -void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& hw, int32_t minLayerZ, - int32_t maxLayerZ, +void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display, const LayerVector::Visitor& visitor) { // We loop through the first level of layers without traversing, - // as we need to interpret min/max layer Z in the top level Z space. + // as we need to determine which layers belong to the requested display. for (const auto& layer : mDrawingState.layersSortedByZ) { - if (!layer->belongsToDisplay(hw->getLayerStack(), false)) { + if (!layer->belongsToDisplay(display->getLayerStack(), false)) { continue; } - const Layer::State& state(layer->getDrawingState()); // relative layers are traversed in Layer::traverseInZOrder - if (state.zOrderRelativeOf != nullptr || state.z < minLayerZ || state.z > maxLayerZ) { - continue; - } layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (!layer->belongsToDisplay(hw->getLayerStack(), false)) { + if (!layer->belongsToDisplay(display->getLayerStack(), false)) { return; } if (!layer->isVisible()) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 0148ab6754..d60765cc1a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -45,6 +45,7 @@ #include <gui/LayerState.h> #include <gui/OccupancyTracker.h> +#include <gui/BufferQueue.h> #include <hardware/hwcomposer_defs.h> @@ -54,22 +55,23 @@ #include "Barrier.h" #include "DisplayDevice.h" -#include "DispSync.h" -#include "EventThread.h" #include "FrameTracker.h" +#include "LayerBE.h" #include "LayerStats.h" #include "LayerVector.h" -#include "MessageQueue.h" +#include "SurfaceFlingerFactory.h" #include "SurfaceInterceptor.h" #include "SurfaceTracing.h" -#include "StartPropertySetThread.h" -#include "TimeStats/TimeStats.h" -#include "VSyncModulator.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/HWComposer.h" - #include "Effects/Daltonizer.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/Scheduler.h" +#include "Scheduler/VSyncModulator.h" +#include "TimeStats/TimeStats.h" #include <map> #include <mutex> @@ -99,12 +101,13 @@ class Layer; class Surface; class SurfaceFlingerBE; class VSyncSource; +struct CompositionInfo; namespace impl { class EventThread; } // namespace impl -namespace RE { +namespace renderengine { class RenderEngine; } @@ -114,6 +117,10 @@ namespace dvr { class VrFlinger; } // namespace dvr +namespace surfaceflinger { +class NativeWindowSurface; +} // namespace surfaceflinger + // --------------------------------------------------------------------------- enum { @@ -130,18 +137,6 @@ enum class DisplayColorSetting : int32_t { ENHANCED = 2, }; -// A thin interface to abstract creating instances of Surface (gui/Surface.h) to -// use as a NativeWindow. -class NativeWindowSurface { -public: - virtual ~NativeWindowSurface(); - - // Gets the NativeWindow to use for the surface. - virtual sp<ANativeWindow> getNativeWindow() const = 0; - - // Indicates that the surface should allocate its buffers now. - virtual void preallocateBuffers() = 0; -}; class SurfaceFlingerBE { @@ -173,7 +168,7 @@ public: const std::string mHwcServiceName; // "default" for real use, something else for testing. // constant members (no synchronization needed for access) - std::unique_ptr<RE::RenderEngine> mRenderEngine; + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; EGLContext mEGLContext; EGLDisplay mEGLDisplay; @@ -196,6 +191,9 @@ public: nsecs_t mTotalTime; std::atomic<nsecs_t> mLastSwapTime; + // Synchronization fence from a GL composition. + sp<Fence> flushFence = Fence::NO_FENCE; + // Double- vs. triple-buffering stats struct BufferingStats { BufferingStats() @@ -223,6 +221,9 @@ public: // use to differentiate callbacks from different hardware composer // instances. Each hardware composer instance gets a different sequence id. int32_t mComposerSequenceId; + + std::unordered_map<int32_t, std::vector<CompositionInfo>> mCompositionInfo; + std::unordered_map<int32_t, std::vector<CompositionInfo>> mEndOfFrameCompositionInfo; }; @@ -279,21 +280,37 @@ public: // FramebufferSurface static int64_t maxFrameBufferAcquiredBuffers; - // Indicate if platform supports color management on its - // wide-color display. This is typically found on devices - // with wide gamut (e.g. Display-P3) display. - // This also allows devices with wide-color displays that don't - // want to support color management to disable color management. + // Indicate if a device has wide color gamut display. This is typically + // found on devices with wide color gamut (e.g. Display-P3) display. static bool hasWideColorDisplay; + static int primaryDisplayOrientation; + + // Indicate if device wants color management on its display. + static bool useColorManagement; + + static bool useContextPriority; + + // The data space and pixel format that SurfaceFlinger expects hardware composer + // to composite efficiently. Meaning under most scenarios, hardware composer + // will accept layers with the data space and pixel format. + static ui::Dataspace defaultCompositionDataspace; + static ui::PixelFormat defaultCompositionPixelFormat; + + // The data space and pixel format that SurfaceFlinger expects hardware composer + // to composite efficiently for wide color gamut surfaces. Meaning under most scenarios, + // hardware composer will accept layers with the data space and pixel format. + static ui::Dataspace wideColorGamutCompositionDataspace; + static ui::PixelFormat wideColorGamutCompositionPixelFormat; + static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } struct SkipInitializationTag {}; static constexpr SkipInitializationTag SkipInitialization; - explicit SurfaceFlinger(SkipInitializationTag) ANDROID_API; - SurfaceFlinger() ANDROID_API; + SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API; + explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API; // must be called before clients can connect void init() ANDROID_API; @@ -314,6 +331,8 @@ public: // force full composition on all displays void repaintEverything(); + surfaceflinger::Factory& getFactory() { return mFactory; } + // returns the default Display sp<const DisplayDevice> getDefaultDisplayDevice() const { Mutex::Autolock _l(mStateLock); @@ -340,19 +359,19 @@ public: // TODO: this should be made accessible only to HWComposer const Vector< sp<Layer> >& getLayerSortedByZForHwcDisplay(int id); - RE::RenderEngine& getRenderEngine() const { return *getBE().mRenderEngine; } + renderengine::RenderEngine& getRenderEngine() const { return *getBE().mRenderEngine; } bool authenticateSurfaceTextureLocked( const sp<IGraphicBufferProducer>& bufferProducer) const; - int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; } - private: friend class Client; friend class DisplayEventConnection; friend class impl::EventThread; friend class Layer; friend class BufferLayer; + friend class BufferQueueLayer; + friend class BufferStateLayer; friend class MonitoredProducer; // For unit tests @@ -410,7 +429,7 @@ private: virtual sp<ISurfaceComposerClient> createConnection(); virtual sp<ISurfaceComposerClient> createScopedConnection(const sp<IGraphicBufferProducer>& gbp); virtual sp<IBinder> createDisplay(const String8& displayName, bool secure); - virtual void destroyDisplay(const sp<IBinder>& display); + virtual void destroyDisplay(const sp<IBinder>& displayToken); virtual sp<IBinder> getBuiltInDisplay(int32_t id); virtual void setTransactionState(const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags); @@ -421,32 +440,36 @@ private: std::vector<FrameEvent>* outSupported) const; virtual sp<IDisplayEventConnection> createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp); - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + virtual status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation); virtual status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer, - const Rect& sourceCrop, float frameScale, bool childrenOnly); - virtual status_t getDisplayStats(const sp<IBinder>& display, - DisplayStatInfo* stats); - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport); - virtual status_t getDisplayConfigs(const sp<IBinder>& display, - Vector<DisplayInfo>* configs); - virtual int getActiveConfig(const sp<IBinder>& display); - virtual status_t getDisplayColorModes(const sp<IBinder>& display, - Vector<ui::ColorMode>* configs); - virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display); - virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode); - virtual void setPowerMode(const sp<IBinder>& display, int mode); - virtual status_t setActiveConfig(const sp<IBinder>& display, int id); + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, + float frameScale, bool childrenOnly); + virtual status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); + virtual status_t getDisplayConfigs(const sp<IBinder>& displayToken, + Vector<DisplayInfo>* configs); + virtual int getActiveConfig(const sp<IBinder>& displayToken); + virtual status_t getDisplayColorModes(const sp<IBinder>& displayToken, + Vector<ui::ColorMode>* configs); + virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken); + virtual status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode); + virtual void setPowerMode(const sp<IBinder>& displayToken, int mode); + virtual status_t setActiveConfig(const sp<IBinder>& displayToken, int id); virtual status_t clearAnimationFrameStats(); virtual status_t getAnimationFrameStats(FrameStats* outStats) const; - virtual status_t getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) const; + virtual status_t getHdrCapabilities(const sp<IBinder>& displayToken, + HdrCapabilities* outCapabilities) const; virtual status_t enableVSyncInjections(bool enable); virtual status_t injectVSync(nsecs_t when); virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const; - + virtual bool isColorManagementUsed() const; + status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, + ui::Dataspace* outWideColorGamutDataspace, + ui::PixelFormat* outWideColorGamutPixelFormat) const override; /* ------------------------------------------------------------------------ * DeathRecipient interface @@ -461,11 +484,11 @@ private: /* ------------------------------------------------------------------------ * HWC2::ComposerCallback / HWComposer::EventHandler interface */ - void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, + void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, int64_t timestamp) override; - void onHotplugReceived(int32_t sequenceId, hwc2_display_t display, + void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, HWC2::Connection connection) override; - void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override; + void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override; /* ------------------------------------------------------------------------ * Message handling @@ -480,16 +503,13 @@ private: // called on the main thread in response to initializeDisplays() void onInitializeDisplays(); // called on the main thread in response to setActiveConfig() - void setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode); + void setActiveConfigInternal(const sp<DisplayDevice>& display, int mode); // called on the main thread in response to setPowerMode() - void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, - bool stateLockHeld); + void setPowerModeInternal(const sp<DisplayDevice>& display, int mode, bool stateLockHeld); // Called on the main thread in response to setActiveColorMode() - void setActiveColorModeInternal(const sp<DisplayDevice>& hw, - ui::ColorMode colorMode, - ui::Dataspace dataSpace, - ui::RenderIntent renderIntent); + void setActiveColorModeInternal(const sp<DisplayDevice>& display, ui::ColorMode colorMode, + ui::Dataspace dataSpace, ui::RenderIntent renderIntent); // Returns whether the transaction actually modified any state bool handleMessageTransaction(); @@ -517,7 +537,7 @@ private: uint32_t peekTransactionFlags(); // Can only be called from the main thread or with mStateLock held uint32_t setTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags, VSyncModulator::TransactionStart transactionStart); + uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart); void commitTransaction(); bool containsAnyInvalidClientState(const Vector<ComposerState>& states); uint32_t setClientStateLocked(const ComposerState& composerState); @@ -532,15 +552,23 @@ private: int32_t windowType, int32_t ownerUid, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent); - status_t createBufferLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, PixelFormat& format, + sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, + sp<Layer>* outLayer); + + status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, sp<IBinder>* outHandle, + sp<Layer>* outLayer); status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, sp<Layer>* outLayer); + status_t createContainerLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, + sp<Layer>* outLayer); + String8 getUniqueLayerName(const String8& name); // called in response to the window-manager calling @@ -570,18 +598,18 @@ private: void startBootAnim(); void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - bool yswap, bool useIdentityTransform); + bool useIdentityTransform); status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, + sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat, bool useIdentityTransform); status_t captureScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd); - void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ, - int32_t maxLayerZ, const LayerVector::Visitor& visitor); + void traverseLayersInDisplay(const sp<const DisplayDevice>& display, + const LayerVector::Visitor& visitor); - sp<StartPropertySetThread> mStartPropertySetThread = nullptr; + sp<StartPropertySetThread> mStartPropertySetThread; /* ------------------------------------------------------------------------ * Properties @@ -600,38 +628,33 @@ private: // called when starting, or restarting after system_server death void initializeDisplays(); - sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const { - Mutex::Autolock _l(mStateLock); - return getDisplayDeviceLocked(dpy); + sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) const { + Mutex::Autolock _l(mStateLock); + return getDisplayDeviceLocked(displayToken); } - sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) { - Mutex::Autolock _l(mStateLock); - return getDisplayDeviceLocked(dpy); + sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) { + Mutex::Autolock _l(mStateLock); + return getDisplayDeviceLocked(displayToken); } // NOTE: can only be called from the main thread or with mStateLock held - sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) const { - return mDisplays.valueFor(dpy); + sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const { + return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken); } // NOTE: can only be called from the main thread or with mStateLock held - sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& dpy) { - return mDisplays.valueFor(dpy); + sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) { + const auto it = mDisplays.find(displayToken); + return it == mDisplays.end() ? nullptr : it->second; } sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const { - return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]); + return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked(); } - int32_t getDisplayType(const sp<IBinder>& display) { - if (!display.get()) return NAME_NOT_FOUND; - for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) { - if (display == mBuiltinDisplays[i]) { - return i; - } - } - return NAME_NOT_FOUND; + sp<DisplayDevice> getDefaultDisplayDeviceLocked() { + return getDisplayDeviceLocked(mDisplayTokens[DisplayDevice::DISPLAY_PRIMARY]); } // mark a region of a layer stack dirty. this updates the dirty @@ -648,50 +671,58 @@ private: * Compositing */ void invalidateHwcGeometry(); - void computeVisibleRegions(const sp<const DisplayDevice>& displayDevice, - Region& dirtyRegion, Region& opaqueRegion); - - void preComposition(nsecs_t refreshStartTime); - void postComposition(nsecs_t refreshStartTime); - void updateCompositorTiming( - nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, - std::shared_ptr<FenceTime>& presentFenceTime); - void setCompositorTimingSnapped( - nsecs_t vsyncPhase, nsecs_t vsyncInterval, - nsecs_t compositeToPresentLatency); + void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion, + Region& opaqueRegion); + + void preComposition(); + void postComposition(); + void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime); + void setCompositorTimingSnapped(const DisplayStatInfo& stats, + nsecs_t compositeToPresentLatency); void rebuildLayerStacks(); - ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice, + ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& display, ui::Dataspace* outHdrDataSpace) const; // Returns the appropriate ColorMode, Dataspace and RenderIntent for the // DisplayDevice. The function only returns the supported ColorMode, // Dataspace and RenderIntent. - void pickColorMode(const sp<DisplayDevice>& displayDevice, - ui::ColorMode* outMode, - ui::Dataspace* outDataSpace, - ui::RenderIntent* outRenderIntent) const; - - void setUpHWComposer(); - void doComposition(); - void doDebugFlashRegions(); + void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode, + ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const; + + void calculateWorkingSet(); + /* + * beginFrame - This function handles any pre-frame processing that needs to be + * prior to any CompositionInfo handling and is not dependent on data in + * CompositionInfo + */ + void beginFrame(const sp<DisplayDevice>& display); + /* prepareFrame - This function will call into the DisplayDevice to prepare a + * frame after CompositionInfo has been programmed. This provides a mechanism + * to prepare the hardware composer + */ + void prepareFrame(const sp<DisplayDevice>& display); + void doComposition(const sp<DisplayDevice>& display, bool repainEverything); + void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything); void doTracing(const char* where); void logLayerStats(); - void doDisplayComposition(const sp<const DisplayDevice>& displayDevice, const Region& dirtyRegion); + void doDisplayComposition(const sp<const DisplayDevice>& display, const Region& dirtyRegion); - // compose surfaces for display hw. this fails if using GL and the surface - // has been destroyed and is no longer valid. - bool doComposeSurfaces(const sp<const DisplayDevice>& displayDevice); + // This fails if using GL and the surface has been destroyed. + bool doComposeSurfaces(const sp<const DisplayDevice>& display); - void postFramebuffer(); - void drawWormhole(const sp<const DisplayDevice>& displayDevice, const Region& region) const; + void postFramebuffer(const sp<DisplayDevice>& display); + void postFrame(); + void drawWormhole(const Region& region) const; /* ------------------------------------------------------------------------ * Display management */ - DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display, - HWC2::Connection connection) const; - sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId, + DisplayDevice::DisplayType determineDisplayType(hwc2_display_t hwcDisplayId, + HWC2::Connection connection) const; + sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken, + int32_t displayId, const DisplayDeviceState& state, const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer); @@ -729,8 +760,6 @@ private: void dumpAllLocked(const Vector<String16>& args, size_t& index, String8& result) const; bool startDdmConnection(); void appendSfConfigString(String8& result) const; - void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, - TraverseLayersFunction traverseLayers); void logFrameStats(); @@ -741,9 +770,11 @@ private: void recordBufferingStats(const char* layerName, std::vector<OccupancyTracker::Segment>&& history); void dumpBufferingStats(String8& result) const; + void dumpDisplayIdentificationData(String8& result) const; void dumpWideColorInfo(String8& result) const; + void dumpFrameCompositionInfo(String8& result) const; LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const; - LayersProto dumpVisibleLayersProtoInfo(int32_t hwcId) const; + LayersProto dumpVisibleLayersProtoInfo(const DisplayDevice& display) const; bool isLayerTripleBufferingDisabled() const { return this->mLayerTripleBufferingDisabled; @@ -764,10 +795,12 @@ private: * Attributes */ + surfaceflinger::Factory& mFactory; + // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState{LayerVector::StateSet::Current}; - volatile int32_t mTransactionFlags; + std::atomic<int32_t> mTransactionFlags{0}; Condition mTransactionCV; bool mTransactionPending; bool mAnimTransactionPending; @@ -786,8 +819,7 @@ private: bool mLayersRemoved; bool mLayersAdded; - // access must be protected by mInvalidateLock - volatile int32_t mRepaintEverything; + std::atomic<bool> mRepaintEverything{false}; // constant members (no synchronization needed for access) nsecs_t mBootTime; @@ -799,7 +831,7 @@ private: std::unique_ptr<VSyncSource> mSfEventThreadSource; std::unique_ptr<InjectVSyncSource> mVSyncInjector; std::unique_ptr<EventControlThread> mEventControlThread; - sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; + sp<IBinder> mDisplayTokens[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; VSyncModulator mVsyncModulator; @@ -821,7 +853,7 @@ private: BootStage mBootStage; struct HotplugEvent { - hwc2_display_t display; + hwc2_display_t hwcDisplayId; HWC2::Connection connection = HWC2::Connection::Invalid; }; // protected by mStateLock @@ -829,7 +861,7 @@ private: // this may only be written from the main thread with mStateLock held // it may be read from other threads with mStateLock held - DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays; + std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays; // don't use a lock for these, we don't care int mDebugRegion; @@ -837,26 +869,25 @@ private: int mDebugDisableHWC; int mDebugDisableTransformHint; volatile nsecs_t mDebugInSwapBuffers; - nsecs_t mLastSwapBufferTime; volatile nsecs_t mDebugInTransaction; nsecs_t mLastTransactionTime; + nsecs_t mPostFramebufferTime; bool mForceFullDamage; bool mPropagateBackpressure = true; - std::unique_ptr<SurfaceInterceptor> mInterceptor = - std::make_unique<impl::SurfaceInterceptor>(this); + std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)}; SurfaceTracing mTracing; LayerStats mLayerStats; TimeStats& mTimeStats = TimeStats::getInstance(); bool mUseHwcVirtualDisplays = false; + std::atomic<uint32_t> mFrameMissedCount{0}; // Restrict layers to use two buffers in their bufferqueues. bool mLayerTripleBufferingDisabled = false; // these are thread safe - mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()}; + mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()}; FrameTracker mAnimFrameTracker; - DispSync mPrimaryDispSync; - int mPrimaryDisplayOrientation = DisplayState::eOrientationDefault; + std::unique_ptr<DispSync> mPrimaryDispSync; // protected by mDestroyedLayerLock; mutable Mutex mDestroyedLayerLock; @@ -866,6 +897,7 @@ private: Mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled; bool mHWVsyncAvailable; + nsecs_t mRefreshStartTime; std::atomic<bool> mRefreshPending{false}; @@ -900,17 +932,12 @@ private: // Applied on Display P3 layers when the render intent is non-colorimetric. mat4 mEnhancedSaturationMatrix; - using CreateBufferQueueFunction = - std::function<void(sp<IGraphicBufferProducer>* /* outProducer */, - sp<IGraphicBufferConsumer>* /* outConsumer */, - bool /* consumerIsSurfaceFlinger */)>; - CreateBufferQueueFunction mCreateBufferQueue; - - using CreateNativeWindowSurfaceFunction = - std::function<std::unique_ptr<NativeWindowSurface>(const sp<IGraphicBufferProducer>&)>; - CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface; - SurfaceFlingerBE mBE; + + bool mUseScheduler = false; + std::unique_ptr<Scheduler> mScheduler; + sp<Scheduler::ConnectionHandle> mAppConnectionHandle; + sp<Scheduler::ConnectionHandle> mSfConnectionHandle; }; }; // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp new file mode 100644 index 0000000000..1261aee4d5 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/GraphicBuffer.h> + +#include "BufferQueueLayer.h" +#include "BufferStateLayer.h" +#include "ColorLayer.h" +#include "ContainerLayer.h" +#include "DisplayDevice.h" +#include "Layer.h" +#include "NativeWindowSurface.h" +#include "StartPropertySetThread.h" +#include "SurfaceFlinger.h" +#include "SurfaceFlingerFactory.h" +#include "SurfaceInterceptor.h" + +#include "DisplayHardware/ComposerHal.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/Scheduler.h" + +namespace android::surfaceflinger { + +sp<SurfaceFlinger> createSurfaceFlinger() { + class Factory final : public surfaceflinger::Factory { + public: + Factory() = default; + ~Factory() = default; + + std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework, + int64_t dispSyncPresentTimeOffset) override { + // Note: We create a local temporary with the real DispSync implementation + // type temporarily so we can initialize it with the configured values, + // before storing it for more generic use using the interface type. + auto primaryDispSync = std::make_unique<android::impl::DispSync>(name); + primaryDispSync->init(hasSyncFramework, dispSyncPresentTimeOffset); + return primaryDispSync; + } + + std::unique_ptr<EventControlThread> createEventControlThread( + std::function<void(bool)> setVSyncEnabled) override { + return std::make_unique<android::impl::EventControlThread>(setVSyncEnabled); + } + + std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override { + return std::make_unique<HWComposer>( + std::make_unique<Hwc2::impl::Composer>(serviceName)); + } + + std::unique_ptr<MessageQueue> createMessageQueue() override { + return std::make_unique<android::impl::MessageQueue>(); + } + + std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) override { + return std::make_unique<Scheduler>(callback); + } + + std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor( + SurfaceFlinger* flinger) override { + return std::make_unique<android::impl::SurfaceInterceptor>(flinger); + } + + sp<StartPropertySetThread> createStartPropertySetThread( + bool timestampPropertyValue) override { + return new StartPropertySetThread(timestampPropertyValue); + } + + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override { + return new DisplayDevice(std::move(creationArgs)); + } + + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override { + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + } + + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) override { + BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + } + + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) override { + return surfaceflinger::impl::createNativeWindowSurface(producer); + } + + sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override { + return new ContainerLayer(args); + } + + sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override { + return new BufferQueueLayer(args); + } + + sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override { + return new BufferStateLayer(args); + } + + sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override { + return new ColorLayer(args); + } + }; + static Factory factory; + + return new SurfaceFlinger(factory); +} + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h new file mode 100644 index 0000000000..496bbf0349 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -0,0 +1,92 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cinttypes> +#include <functional> +#include <memory> +#include <string> + +#include <cutils/compiler.h> +#include <utils/StrongPointer.h> + +namespace android { + +typedef int32_t PixelFormat; + +class BufferQueueLayer; +class BufferStateLayer; +class ColorLayer; +class ContainerLayer; +class DisplayDevice; +class DispSync; +class EventControlThread; +class GraphicBuffer; +class HWComposer; +class IGraphicBufferConsumer; +class IGraphicBufferProducer; +class MessageQueue; +class Scheduler; +class StartPropertySetThread; +class SurfaceFlinger; +class SurfaceInterceptor; + +struct DisplayDeviceCreationArgs; +struct LayerCreationArgs; + +namespace surfaceflinger { + +class NativeWindowSurface; + +// The interface that SurfaceFlinger uses to create all of the implementations +// of each interface. +class Factory { +public: + virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework, + int64_t dispSyncPresentTimeOffset) = 0; + virtual std::unique_ptr<EventControlThread> createEventControlThread( + std::function<void(bool)> setVSyncEnabled) = 0; + virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0; + virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0; + virtual std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)> callback) = 0; + virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0; + + virtual sp<StartPropertySetThread> createStartPropertySetThread( + bool timestampPropertyValue) = 0; + virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0; + virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, std::string requestorName) = 0; + virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) = 0; + virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>&) = 0; + + virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0; + virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0; + virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0; + virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0; + +protected: + ~Factory() = default; +}; + +ANDROID_API sp<SurfaceFlinger> createSurfaceFlinger(); + +} // namespace surfaceflinger +} // namespace android diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 4596a21f12..57bda5a8c9 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -30,6 +30,7 @@ namespace android { // ---------------------------------------------------------------------------- +// TODO(marissaw): add new layer state values to SurfaceInterceptor SurfaceInterceptor::~SurfaceInterceptor() = default; @@ -95,22 +96,24 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, const sp<const Layer>& layer) { Transaction* transaction(increment->mutable_transaction()); - transaction->set_synchronous(layer->mTransactionFlags & BnSurfaceComposer::eSynchronous); - transaction->set_animation(layer->mTransactionFlags & BnSurfaceComposer::eAnimation); + const uint32_t layerFlags = layer->getTransactionFlags(); + transaction->set_synchronous(layerFlags & BnSurfaceComposer::eSynchronous); + transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation); const int32_t layerId(getLayerId(layer)); - addPositionLocked(transaction, layerId, layer->mCurrentState.active.transform.tx(), - layer->mCurrentState.active.transform.ty()); + addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(), + layer->mCurrentState.active_legacy.transform.ty()); addDepthLocked(transaction, layerId, layer->mCurrentState.z); addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a); - addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion); + addTransparentRegionLocked(transaction, layerId, + layer->mCurrentState.activeTransparentRegion_legacy); addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack); - addCropLocked(transaction, layerId, layer->mCurrentState.crop); - if (layer->mCurrentState.barrierLayer != nullptr) { - addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(), - layer->mCurrentState.frameNumber); + addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy); + if (layer->mCurrentState.barrierLayer_legacy != nullptr) { + addDeferTransactionLocked(transaction, layerId, + layer->mCurrentState.barrierLayer_legacy.promote(), + layer->mCurrentState.frameNumber_legacy); } - addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop); addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode()); addFlagsLocked(transaction, layerId, layer->mCurrentState.flags); } @@ -122,10 +125,10 @@ void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, transaction->set_synchronous(false); transaction->set_animation(false); - addDisplaySurfaceLocked(transaction, display.displayId, display.surface); - addDisplayLayerStackLocked(transaction, display.displayId, display.layerStack); - addDisplaySizeLocked(transaction, display.displayId, display.width, display.height); - addDisplayProjectionLocked(transaction, display.displayId, display.orientation, + addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface); + addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack); + addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height); + addDisplayProjectionLocked(transaction, display.sequenceId, display.orientation, display.viewport, display.frame); } @@ -177,10 +180,10 @@ SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transa } DisplayChange* SurfaceInterceptor::createDisplayChangeLocked(Transaction* transaction, - int32_t displayId) + int32_t sequenceId) { DisplayChange* dispChange(transaction->add_display_change()); - dispChange->set_id(displayId); + dispChange->set_id(sequenceId); return dispChange; } @@ -286,15 +289,6 @@ void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId setProtoRectLocked(protoRect, rect); } -void SurfaceInterceptor::addFinalCropLocked(Transaction* transaction, int32_t layerId, - const Rect& rect) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - FinalCropChange* finalCropChange(change->mutable_final_crop()); - Rectangle* protoRect(finalCropChange->mutable_rectangle()); - setProtoRectLocked(protoRect, rect); -} - void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber) { @@ -353,25 +347,23 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eLayerStackChanged) { addLayerStackLocked(transaction, layerId, state.layerStack); } - if (state.what & layer_state_t::eCropChanged) { - addCropLocked(transaction, layerId, state.crop); + if (state.what & layer_state_t::eCropChanged_legacy) { + addCropLocked(transaction, layerId, state.crop_legacy); } - if (state.what & layer_state_t::eDeferTransaction) { + if (state.what & layer_state_t::eDeferTransaction_legacy) { sp<Layer> otherLayer = nullptr; - if (state.barrierHandle != nullptr) { - otherLayer = static_cast<Layer::Handle*>(state.barrierHandle.get())->owner.promote(); - } else if (state.barrierGbp != nullptr) { - auto const& gbp = state.barrierGbp; + if (state.barrierHandle_legacy != nullptr) { + otherLayer = + static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote(); + } else if (state.barrierGbp_legacy != nullptr) { + auto const& gbp = state.barrierGbp_legacy; if (mFlinger->authenticateSurfaceTextureLocked(gbp)) { otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer(); } else { ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer"); } } - addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber); - } - if (state.what & layer_state_t::eFinalCropChanged) { - addFinalCropLocked(transaction, layerId, state.finalCrop); + addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy); } if (state.what & layer_state_t::eOverrideScalingModeChanged) { addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode); @@ -379,19 +371,19 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, } void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, - const DisplayState& state, int32_t displayId) + const DisplayState& state, int32_t sequenceId) { if (state.what & DisplayState::eSurfaceChanged) { - addDisplaySurfaceLocked(transaction, displayId, state.surface); + addDisplaySurfaceLocked(transaction, sequenceId, state.surface); } if (state.what & DisplayState::eLayerStackChanged) { - addDisplayLayerStackLocked(transaction, displayId, state.layerStack); + addDisplayLayerStackLocked(transaction, sequenceId, state.layerStack); } if (state.what & DisplayState::eDisplaySizeChanged) { - addDisplaySizeLocked(transaction, displayId, state.width, state.height); + addDisplaySizeLocked(transaction, sequenceId, state.width, state.height); } if (state.what & DisplayState::eDisplayProjectionChanged) { - addDisplayProjectionLocked(transaction, displayId, state.orientation, state.viewport, + addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport, state.frame); } } @@ -411,7 +403,7 @@ void SurfaceInterceptor::addTransactionLocked(Increment* increment, ssize_t dpyIdx = displays.indexOfKey(disp.token); if (dpyIdx >= 0) { const DisplayDeviceState& dispState(displays.valueAt(dpyIdx)); - addDisplayChangesLocked(transaction, disp, dispState.displayId); + addDisplayChangesLocked(transaction, disp, dispState.sequenceId); } } } @@ -422,8 +414,8 @@ void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment, SurfaceCreation* creation(increment->mutable_surface_creation()); creation->set_id(getLayerId(layer)); creation->set_name(getLayerName(layer)); - creation->set_w(layer->mCurrentState.active.w); - creation->set_h(layer->mCurrentState.active.h); + creation->set_w(layer->mCurrentState.active_legacy.w); + creation->set_h(layer->mCurrentState.active_legacy.h); } void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment, @@ -448,7 +440,7 @@ void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t time event->set_when(timestamp); } -void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId, +void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32_t sequenceId, const sp<const IGraphicBufferProducer>& surface) { if (surface == nullptr) { @@ -457,7 +449,7 @@ void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32 uint64_t bufferQueueId = 0; status_t err(surface->getUniqueId(&bufferQueueId)); if (err == NO_ERROR) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId)); + DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); DispSurfaceChange* surfaceChange(dispChange->mutable_surface()); surfaceChange->set_buffer_queue_id(bufferQueueId); surfaceChange->set_buffer_queue_name(surface->getConsumerName().string()); @@ -469,26 +461,26 @@ void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32 } void SurfaceInterceptor::addDisplayLayerStackLocked(Transaction* transaction, - int32_t displayId, uint32_t layerStack) + int32_t sequenceId, uint32_t layerStack) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId)); + DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); LayerStackChange* layerStackChange(dispChange->mutable_layer_stack()); layerStackChange->set_layer_stack(layerStack); } -void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t displayId, +void SurfaceInterceptor::addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, uint32_t h) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId)); + DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); SizeChange* sizeChange(dispChange->mutable_size()); sizeChange->set_w(w); sizeChange->set_h(h); } void SurfaceInterceptor::addDisplayProjectionLocked(Transaction* transaction, - int32_t displayId, int32_t orientation, const Rect& viewport, const Rect& frame) + int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame) { - DisplayChange* dispChange(createDisplayChangeLocked(transaction, displayId)); + DisplayChange* dispChange(createDisplayChangeLocked(transaction, sequenceId)); ProjectionChange* projectionChange(dispChange->mutable_projection()); projectionChange->set_orientation(orientation); Rectangle* viewportRect(projectionChange->mutable_viewport()); @@ -501,22 +493,22 @@ void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info) { DisplayCreation* creation(increment->mutable_display_creation()); - creation->set_id(info.displayId); + creation->set_id(info.sequenceId); creation->set_name(info.displayName); creation->set_type(info.type); creation->set_is_secure(info.isSecure); } -void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t displayId) { +void SurfaceInterceptor::addDisplayDeletionLocked(Increment* increment, int32_t sequenceId) { DisplayDeletion* deletion(increment->mutable_display_deletion()); - deletion->set_id(displayId); + deletion->set_id(sequenceId); } -void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t displayId, +void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode) { PowerModeUpdate* powerModeUpdate(increment->mutable_power_mode_update()); - powerModeUpdate->set_id(displayId); + powerModeUpdate->set_id(sequenceId); powerModeUpdate->set_mode(mode); } @@ -579,22 +571,22 @@ void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) { addDisplayCreationLocked(createTraceIncrementLocked(), info); } -void SurfaceInterceptor::saveDisplayDeletion(int32_t displayId) { +void SurfaceInterceptor::saveDisplayDeletion(int32_t sequenceId) { if (!mEnabled) { return; } ATRACE_CALL(); std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addDisplayDeletionLocked(createTraceIncrementLocked(), displayId); + addDisplayDeletionLocked(createTraceIncrementLocked(), sequenceId); } -void SurfaceInterceptor::savePowerModeUpdate(int32_t displayId, int32_t mode) { +void SurfaceInterceptor::savePowerModeUpdate(int32_t sequenceId, int32_t mode) { if (!mEnabled) { return; } ATRACE_CALL(); std::lock_guard<std::mutex> protoGuard(mTraceMutex); - addPowerModeUpdateLocked(createTraceIncrementLocked(), displayId, mode); + addPowerModeUpdateLocked(createTraceIncrementLocked(), sequenceId, mode); } } // namespace impl diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 96defcc265..394b99b415 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -66,8 +66,8 @@ public: // Intercept display data virtual void saveDisplayCreation(const DisplayDeviceState& info) = 0; - virtual void saveDisplayDeletion(int32_t displayId) = 0; - virtual void savePowerModeUpdate(int32_t displayId, int32_t mode) = 0; + virtual void saveDisplayDeletion(int32_t sequenceId) = 0; + virtual void savePowerModeUpdate(int32_t sequenceId, int32_t mode) = 0; virtual void saveVSyncEvent(nsecs_t timestamp) = 0; }; @@ -101,8 +101,8 @@ public: // Intercept display data void saveDisplayCreation(const DisplayDeviceState& info) override; - void saveDisplayDeletion(int32_t displayId) override; - void savePowerModeUpdate(int32_t displayId, int32_t mode) override; + void saveDisplayDeletion(int32_t sequenceId) override; + void savePowerModeUpdate(int32_t sequenceId, int32_t mode) override; void saveVSyncEvent(nsecs_t timestamp) override; private: @@ -127,8 +127,8 @@ private: uint32_t height, uint64_t frameNumber); void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp); void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info); - void addDisplayDeletionLocked(Increment* increment, int32_t displayId); - void addPowerModeUpdateLocked(Increment* increment, int32_t displayId, int32_t mode); + void addDisplayDeletionLocked(Increment* increment, int32_t sequenceId); + void addPowerModeUpdateLocked(Increment* increment, int32_t sequenceId, int32_t mode); // Add surface transactions to the trace SurfaceChange* createSurfaceChangeLocked(Transaction* transaction, int32_t layerId); @@ -146,7 +146,6 @@ private: void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); void addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber); - void addFinalCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId, int32_t overrideScalingMode); void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state); @@ -155,17 +154,17 @@ private: const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags); // Add display transactions to the trace - DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t displayId); - void addDisplaySurfaceLocked(Transaction* transaction, int32_t displayId, + 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 displayId, + void addDisplayLayerStackLocked(Transaction* transaction, int32_t sequenceId, uint32_t layerStack); - void addDisplaySizeLocked(Transaction* transaction, int32_t displayId, uint32_t w, + void addDisplaySizeLocked(Transaction* transaction, int32_t sequenceId, uint32_t w, uint32_t h); - void addDisplayProjectionLocked(Transaction* transaction, int32_t displayId, + void addDisplayProjectionLocked(Transaction* transaction, int32_t sequenceId, int32_t orientation, const Rect& viewport, const Rect& frame); void addDisplayChangesLocked(Transaction* transaction, - const DisplayState& state, int32_t displayId); + const DisplayState& state, int32_t sequenceId); bool mEnabled {false}; diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index d4f1e29042..ace7c1b454 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -24,6 +24,7 @@ #include <log/log.h> #include <utils/String8.h> +#include <utils/Timers.h> #include <utils/Trace.h> #include <algorithm> @@ -32,11 +33,8 @@ namespace android { TimeStats& TimeStats::getInstance() { - static std::unique_ptr<TimeStats> sInstance; - static std::once_flag sOnceFlag; - - std::call_once(sOnceFlag, [] { sInstance.reset(new TimeStats); }); - return *sInstance.get(); + static TimeStats sInstance; + return sInstance; } void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index, @@ -85,7 +83,7 @@ void TimeStats::incrementTotalFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - timeStats.totalFrames++; + mTimeStats.totalFrames++; } void TimeStats::incrementMissedFrames() { @@ -94,7 +92,7 @@ void TimeStats::incrementMissedFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - timeStats.missedFrames++; + mTimeStats.missedFrames++; } void TimeStats::incrementClientCompositionFrames() { @@ -103,13 +101,13 @@ void TimeStats::incrementClientCompositionFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - timeStats.clientCompositionFrames++; + mTimeStats.clientCompositionFrames++; } -bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) { +bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) { if (!timeRecord->ready) { - ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(), - timeRecord->frameNumber); + ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID, + timeRecord->frameTime.frameNumber); return false; } @@ -118,11 +116,11 @@ bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* time return false; } if (timeRecord->acquireFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) { - timeRecord->acquireTime = timeRecord->acquireFence->getSignalTime(); + timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime(); timeRecord->acquireFence = nullptr; } else { - ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(), - timeRecord->frameNumber); + ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID, + timeRecord->frameTime.frameNumber); } } @@ -131,11 +129,11 @@ bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* time return false; } if (timeRecord->presentFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) { - timeRecord->presentTime = timeRecord->presentFence->getSignalTime(); + timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime(); timeRecord->presentFence = nullptr; } else { - ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(), - timeRecord->frameNumber); + ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID, + timeRecord->frameTime.frameNumber); } } @@ -148,14 +146,15 @@ static int32_t msBetween(nsecs_t start, nsecs_t end) { return static_cast<int32_t>(delta); } +// This regular expression captures the following for instance: +// StatusBar in StatusBar#0 +// com.appname in com.appname/com.appname.activity#0 +// com.appname in SurfaceView - com.appname/com.appname.activity#0 +static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+"); + static std::string getPackageName(const std::string& layerName) { - // This regular expression captures the following for instance: - // StatusBar in StatusBar#0 - // com.appname in com.appname/com.appname.activity#0 - // com.appname in SurfaceView - com.appname/com.appname.activity#0 - const std::regex re("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+"); std::smatch match; - if (std::regex_match(layerName.begin(), layerName.end(), match, re)) { + if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) { // There must be a match for group 1 otherwise the whole string is not // matched and the above will return false return match[1]; @@ -163,57 +162,63 @@ static std::string getPackageName(const std::string& layerName) { return ""; } -void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) { +void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) { ATRACE_CALL(); - LayerRecord& layerRecord = timeStatsTracker[layerName]; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; while (!timeRecords.empty()) { - if (!recordReadyLocked(layerName, &timeRecords[0])) break; - ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(), - timeRecords[0].frameNumber, timeRecords[0].presentTime); + if (!recordReadyLocked(layerID, &timeRecords[0])) break; + ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID, + timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime); if (prevTimeRecord.ready) { - if (!timeStats.stats.count(layerName)) { - timeStats.stats[layerName].layerName = layerName; - timeStats.stats[layerName].packageName = getPackageName(layerName); - timeStats.stats[layerName].statsStart = static_cast<int64_t>(std::time(0)); + const std::string& layerName = layerRecord.layerName; + if (!mTimeStats.stats.count(layerName)) { + mTimeStats.stats[layerName].layerName = layerName; + mTimeStats.stats[layerName].packageName = getPackageName(layerName); } - TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName]; + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName]; timeStatsLayer.totalFrames++; - - const int32_t postToPresentMs = - msBetween(timeRecords[0].postTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, postToPresentMs); + timeStatsLayer.droppedFrames += layerRecord.droppedFrames; + layerRecord.droppedFrames = 0; + + const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime, + timeRecords[0].frameTime.acquireTime); + ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID, + timeRecords[0].frameTime.frameNumber, postToAcquireMs); + timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs); + + const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, postToPresentMs); timeStatsLayer.deltas["post2present"].insert(postToPresentMs); - const int32_t acquireToPresentMs = - msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, acquireToPresentMs); + const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, acquireToPresentMs); timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs); - const int32_t latchToPresentMs = - msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, latchToPresentMs); + const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, latchToPresentMs); timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs); - const int32_t desiredToPresentMs = - msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, desiredToPresentMs); + const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, desiredToPresentMs); timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs); - const int32_t presentToPresentMs = - msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, presentToPresentMs); + const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); - - timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0)); } prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); @@ -221,45 +226,53 @@ void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) } } +// This regular expression captures the following layer names for instance: +// 1) StatusBat#0 +// 2) NavigationBar#1 +// 3) co(m).*#0 +// 4) SurfaceView - co(m).*#0 +// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).* +// is a bit more robust in case there's a slight change. +// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases. +static const std::regex layerNameRegex( + "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+"); + static bool layerNameIsValid(const std::string& layerName) { - // This regular expression captures the following layer names for instance: - // 1) StatusBat#0 - // 2) NavigationBar#1 - // 3) com.*#0 - // 4) SurfaceView - com.*#0 - // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is - // a bit more robust in case there's a slight change. - // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases. - std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+"); - return std::regex_match(layerName.begin(), layerName.end(), re); + return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex); } -void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) { +void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, + nsecs_t postTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime); + ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(), + postTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) { - return; + if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) { + mTimeStatsTracker[layerID].layerName = layerName; } - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) { - ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(), - MAX_NUM_TIME_RECORDS); - // TODO(zzyiwei): if this happens, there must be a present fence missing - // or waitData is not in the correct position. Need to think out a - // reasonable way to recover from this state. + ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.", + layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS); + layerRecord.timeRecords.clear(); + layerRecord.prevTimeRecord.ready = false; + layerRecord.waitData = -1; return; } // For most media content, the acquireFence is invalid because the buffer is // ready at the queueBuffer stage. In this case, acquireTime should be given // a default value as postTime. TimeRecord timeRecord = { - .frameNumber = frameNumber, - .postTime = postTime, - .acquireTime = postTime, + .frameTime = + { + .frameNumber = frameNumber, + .postTime = postTime, + .acquireTime = postTime, + }, }; layerRecord.timeRecords.push_back(timeRecord); if (layerRecord.waitData < 0 || @@ -267,160 +280,262 @@ void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, layerRecord.waitData = layerRecord.timeRecords.size() - 1; } -void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t latchTime) { +void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime); + ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.latchTime = latchTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.latchTime = latchTime; } } -void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t desiredTime) { +void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber, - desiredTime); + ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.desiredTime = desiredTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.desiredTime = desiredTime; } } -void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t acquireTime) { +void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber, - acquireTime); + ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.acquireTime = acquireTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.acquireTime = acquireTime; } } -void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber, +void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber, + ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber, acquireFence->getSignalTime()); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { + if (timeRecord.frameTime.frameNumber == frameNumber) { timeRecord.acquireFence = acquireFence; } } -void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t presentTime) { +void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber, - presentTime); + ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.presentTime = presentTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.presentTime = presentTime; timeRecord.ready = true; layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerName); + flushAvailableRecordsToStatsLocked(layerID); } -void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber, +void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber, + ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber, presentFence->getSignalTime()); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { + if (timeRecord.frameTime.frameNumber == frameNumber) { timeRecord.presentFence = presentFence; timeRecord.ready = true; layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerName); + flushAvailableRecordsToStatsLocked(layerID); } -void TimeStats::onDisconnect(const std::string& layerName) { +void TimeStats::onDisconnect(int32_t layerID) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-onDisconnect", layerName.c_str()); + ALOGV("[%d]-onDisconnect", layerID); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - flushAvailableRecordsToStatsLocked(layerName); - timeStatsTracker.erase(layerName); + if (!mTimeStatsTracker.count(layerID)) return; + flushAvailableRecordsToStatsLocked(layerID); + mTimeStatsTracker.erase(layerID); } -void TimeStats::clearLayerRecord(const std::string& layerName) { +void TimeStats::onDestroy(int32_t layerID) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-clearLayerRecord", layerName.c_str()); + ALOGV("[%d]-onDestroy", layerID); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + flushAvailableRecordsToStatsLocked(layerID); + mTimeStatsTracker.erase(layerID); +} + +void TimeStats::clearLayerRecord(int32_t layerID) { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + ALOGV("[%d]-clearLayerRecord", layerID); + + std::lock_guard<std::mutex> lock(mMutex); + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; layerRecord.timeRecords.clear(); layerRecord.prevTimeRecord.ready = false; layerRecord.waitData = -1; + layerRecord.droppedFrames = 0; } -void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) { +void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber); + ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; size_t removeAt = 0; for (const TimeRecord& record : layerRecord.timeRecords) { - if (record.frameNumber == frameNumber) break; + if (record.frameTime.frameNumber == frameNumber) break; removeAt++; } if (removeAt == layerRecord.timeRecords.size()) return; layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt); if (layerRecord.waitData > static_cast<int32_t>(removeAt)) { - --layerRecord.waitData; + layerRecord.waitData--; } + layerRecord.droppedFrames++; +} + +void TimeStats::flushPowerTimeLocked() { + if (!mEnabled.load()) return; + + nsecs_t curTime = systemTime(); + // elapsedTime is in milliseconds. + int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000; + + switch (mPowerTime.powerMode) { + case HWC_POWER_MODE_NORMAL: + mTimeStats.displayOnTime += elapsedTime; + break; + case HWC_POWER_MODE_OFF: + case HWC_POWER_MODE_DOZE: + case HWC_POWER_MODE_DOZE_SUSPEND: + default: + break; + } + + mPowerTime.prevTime = curTime; +} + +void TimeStats::setPowerMode(int32_t powerMode) { + if (!mEnabled.load()) { + std::lock_guard<std::mutex> lock(mMutex); + mPowerTime.powerMode = powerMode; + return; + } + + std::lock_guard<std::mutex> lock(mMutex); + if (powerMode == mPowerTime.powerMode) return; + + flushPowerTimeLocked(); + mPowerTime.powerMode = powerMode; +} + +void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { + ATRACE_CALL(); + + while (!mGlobalRecord.presentFences.empty()) { + const nsecs_t curPresentTime = mGlobalRecord.presentFences.front()->getSignalTime(); + if (curPresentTime == Fence::SIGNAL_TIME_PENDING) break; + + if (curPresentTime == Fence::SIGNAL_TIME_INVALID) { + ALOGE("GlobalPresentFence is invalid!"); + mGlobalRecord.prevPresentTime = 0; + mGlobalRecord.presentFences.pop_front(); + continue; + } + + ALOGV("GlobalPresentFenceTime[%" PRId64 "]", + mGlobalRecord.presentFences.front()->getSignalTime()); + + if (mGlobalRecord.prevPresentTime != 0) { + const int32_t presentToPresentMs = + msBetween(mGlobalRecord.prevPresentTime, curPresentTime); + ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]", + presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime); + mTimeStats.presentToPresent.insert(presentToPresentMs); + } + + mGlobalRecord.prevPresentTime = curPresentTime; + mGlobalRecord.presentFences.pop_front(); + } +} + +void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + if (presentFence == nullptr || !presentFence->isValid()) { + mGlobalRecord.prevPresentTime = 0; + return; + } + + if (mPowerTime.powerMode != HWC_POWER_MODE_NORMAL) { + // Try flushing the last present fence on HWC_POWER_MODE_NORMAL. + flushAvailableGlobalRecordsToStatsLocked(); + mGlobalRecord.presentFences.clear(); + mGlobalRecord.prevPresentTime = 0; + return; + } + + if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) { + // The front presentFence must be trapped in pending status in this + // case. Try dequeuing the front one to recover. + ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS); + mGlobalRecord.prevPresentTime = 0; + mGlobalRecord.presentFences.pop_front(); + } + + mGlobalRecord.presentFences.emplace_back(presentFence); + flushAvailableGlobalRecordsToStatsLocked(); } void TimeStats::enable() { @@ -429,9 +544,10 @@ void TimeStats::enable() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - ALOGD("Enabled"); mEnabled.store(true); - timeStats.statsStart = static_cast<int64_t>(std::time(0)); + mTimeStats.statsStart = static_cast<int64_t>(std::time(0)); + mPowerTime.prevTime = systemTime(); + ALOGD("Enabled"); } void TimeStats::disable() { @@ -440,22 +556,29 @@ void TimeStats::disable() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - ALOGD("Disabled"); + flushPowerTimeLocked(); mEnabled.store(false); - timeStats.statsEnd = static_cast<int64_t>(std::time(0)); + mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); + ALOGD("Disabled"); } void TimeStats::clear() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); + mTimeStatsTracker.clear(); + mTimeStats.stats.clear(); + mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0); + mTimeStats.statsEnd = 0; + mTimeStats.totalFrames = 0; + mTimeStats.missedFrames = 0; + mTimeStats.clientCompositionFrames = 0; + mTimeStats.displayOnTime = 0; + mTimeStats.presentToPresent.hist.clear(); + mPowerTime.prevTime = systemTime(); + mGlobalRecord.prevPresentTime = 0; + mGlobalRecord.presentFences.clear(); ALOGD("Cleared"); - timeStats.stats.clear(); - timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0); - timeStats.statsEnd = 0; - timeStats.totalFrames = 0; - timeStats.missedFrames = 0; - timeStats.clientCompositionFrames = 0; } bool TimeStats::isEnabled() { @@ -466,19 +589,21 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, String8& r ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - if (timeStats.statsStart == 0) { + if (mTimeStats.statsStart == 0) { return; } - timeStats.statsEnd = static_cast<int64_t>(std::time(0)); + mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); + + flushPowerTimeLocked(); if (asProto) { ALOGD("Dumping TimeStats as proto"); - SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto(maxLayers); + SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers); result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize()); } else { ALOGD("Dumping TimeStats as text"); - result.append(timeStats.toString(maxLayers).c_str()); + result.append(mTimeStats.toString(maxLayers).c_str()); result.append("\n"); } } diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 831821000f..184bf40967 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -19,6 +19,8 @@ #include <timestatsproto/TimeStatsHelper.h> #include <timestatsproto/TimeStatsProtoHeader.h> +#include <hardware/hwcomposer_defs.h> + #include <ui/FenceTime.h> #include <utils/String16.h> @@ -40,27 +42,43 @@ class TimeStats { // static const size_t MAX_NUM_LAYER_RECORDS = 200; static const size_t MAX_NUM_TIME_RECORDS = 64; - struct TimeRecord { - bool ready = false; + struct FrameTime { uint64_t frameNumber = 0; nsecs_t postTime = 0; nsecs_t latchTime = 0; nsecs_t acquireTime = 0; nsecs_t desiredTime = 0; nsecs_t presentTime = 0; + }; + + struct TimeRecord { + bool ready = false; + FrameTime frameTime; std::shared_ptr<FenceTime> acquireFence; std::shared_ptr<FenceTime> presentFence; }; struct LayerRecord { + std::string layerName; // 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. int32_t waitData = -1; + uint32_t droppedFrames = 0; TimeRecord prevTimeRecord; std::deque<TimeRecord> timeRecords; }; + struct PowerTime { + int32_t powerMode = HWC_POWER_MODE_OFF; + nsecs_t prevTime = 0; + }; + + struct GlobalRecord { + nsecs_t prevPresentTime = 0; + std::deque<std::shared_ptr<FenceTime>> presentFences; + }; + public: static TimeStats& getInstance(); void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result); @@ -68,24 +86,35 @@ public: void incrementMissedFrames(); void incrementClientCompositionFrames(); - void setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime); - void setLatchTime(const std::string& layerName, uint64_t frameNumber, nsecs_t latchTime); - void setDesiredTime(const std::string& layerName, uint64_t frameNumber, nsecs_t desiredTime); - void setAcquireTime(const std::string& layerName, uint64_t frameNumber, nsecs_t acquireTime); - void setAcquireFence(const std::string& layerName, uint64_t frameNumber, + void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, + nsecs_t postTime); + void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime); + void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime); + void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime); + void setAcquireFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence); - void setPresentTime(const std::string& layerName, uint64_t frameNumber, nsecs_t presentTime); - void setPresentFence(const std::string& layerName, uint64_t frameNumber, + void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime); + void setPresentFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence); - void onDisconnect(const std::string& layerName); - void clearLayerRecord(const std::string& layerName); - void removeTimeRecord(const std::string& layerName, uint64_t frameNumber); + // On producer disconnect with BufferQueue. + void onDisconnect(int32_t layerID); + // On layer tear down. + void onDestroy(int32_t layerID); + // When SF is cleaning up the queue, clear the LayerRecord as well. + void clearLayerRecord(int32_t layerID); + // If SF skips or rejects a buffer, remove the corresponding TimeRecord. + void removeTimeRecord(int32_t layerID, uint64_t frameNumber); + + void setPowerMode(int32_t powerMode); + void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence); private: TimeStats() = default; - bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord); - void flushAvailableRecordsToStatsLocked(const std::string& layerName); + bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord); + void flushAvailableRecordsToStatsLocked(int32_t layerID); + void flushPowerTimeLocked(); + void flushAvailableGlobalRecordsToStatsLocked(); void enable(); void disable(); @@ -95,8 +124,11 @@ private: std::atomic<bool> mEnabled = false; std::mutex mMutex; - TimeStatsHelper::TimeStatsGlobal timeStats; - std::unordered_map<std::string, LayerRecord> timeStatsTracker; + TimeStatsHelper::TimeStatsGlobal mTimeStats; + // Hashmap for LayerRecord with layerID as the hash key + std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker; + PowerTime mPowerTime; + GlobalRecord mGlobalRecord; }; } // namespace android diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index 21f3ef3d7e..599ff5c2ac 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -39,7 +39,7 @@ void TimeStatsHelper::Histogram::insert(int32_t delta) { if (delta < 0) return; // std::lower_bound won't work on out of range values if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) { - hist[histogramConfig[HISTOGRAM_SIZE - 1]]++; + hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1]; return; } auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta); @@ -49,7 +49,7 @@ void TimeStatsHelper::Histogram::insert(int32_t delta) { float TimeStatsHelper::Histogram::averageTime() const { int64_t ret = 0; int64_t count = 0; - for (auto& ele : hist) { + for (const auto& ele : hist) { count += ele.second; ret += ele.first * ele.second; } @@ -68,19 +68,18 @@ std::string TimeStatsHelper::Histogram::toString() const { } std::string TimeStatsHelper::TimeStatsLayer::toString() const { - std::string result = ""; + std::string result = "\n"; StringAppendF(&result, "layerName = %s\n", layerName.c_str()); StringAppendF(&result, "packageName = %s\n", packageName.c_str()); - StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart)); - StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd)); - StringAppendF(&result, "totalFrames= %d\n", totalFrames); - auto iter = deltas.find("present2present"); + StringAppendF(&result, "totalFrames = %d\n", totalFrames); + StringAppendF(&result, "droppedFrames = %d\n", droppedFrames); + const auto iter = deltas.find("present2present"); if (iter != deltas.end()) { StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime()); } - for (auto& ele : deltas) { + for (const auto& ele : deltas) { StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str()); - StringAppendF(&result, "%s", ele.second.toString().c_str()); + result.append(ele.second.toString()); } return result; @@ -90,13 +89,15 @@ std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> m std::string result = "SurfaceFlinger TimeStats:\n"; StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart)); StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd)); - StringAppendF(&result, "totalFrames= %d\n", totalFrames); - StringAppendF(&result, "missedFrames= %d\n", missedFrames); - StringAppendF(&result, "clientCompositionFrames= %d\n", clientCompositionFrames); - StringAppendF(&result, "TimeStats for each layer is as below:\n"); + StringAppendF(&result, "totalFrames = %d\n", totalFrames); + StringAppendF(&result, "missedFrames = %d\n", missedFrames); + StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames); + StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime)); + StringAppendF(&result, "presentToPresent histogram is as below:\n"); + result.append(presentToPresent.toString()); const auto dumpStats = generateDumpStats(maxLayers); - for (auto& ele : dumpStats) { - StringAppendF(&result, "%s", ele->toString().c_str()); + for (const auto& ele : dumpStats) { + result.append(ele->toString()); } return result; @@ -106,15 +107,14 @@ SFTimeStatsLayerProto TimeStatsHelper::TimeStatsLayer::toProto() const { SFTimeStatsLayerProto layerProto; layerProto.set_layer_name(layerName); layerProto.set_package_name(packageName); - layerProto.set_stats_start(statsStart); - layerProto.set_stats_end(statsEnd); layerProto.set_total_frames(totalFrames); - for (auto& ele : deltas) { + layerProto.set_dropped_frames(droppedFrames); + for (const auto& ele : deltas) { SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas(); deltaProto->set_delta_name(ele.first); - for (auto& histEle : ele.second.hist) { + for (const auto& histEle : ele.second.hist) { SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms(); - histProto->set_render_millis(histEle.first); + histProto->set_time_millis(histEle.first); histProto->set_frame_count(histEle.second); } } @@ -129,8 +129,14 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( globalProto.set_total_frames(totalFrames); globalProto.set_missed_frames(missedFrames); globalProto.set_client_composition_frames(clientCompositionFrames); + globalProto.set_display_on_time(displayOnTime); + for (const auto& histEle : presentToPresent.hist) { + SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present(); + histProto->set_time_millis(histEle.first); + histProto->set_frame_count(histEle.second); + } const auto dumpStats = generateDumpStats(maxLayers); - for (auto& ele : dumpStats) { + for (const auto& ele : dumpStats) { SFTimeStatsLayerProto* layerProto = globalProto.add_stats(); layerProto->CopyFrom(ele->toProto()); } @@ -140,7 +146,7 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( std::vector<TimeStatsHelper::TimeStatsLayer const*> TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const { std::vector<TimeStatsLayer const*> dumpStats; - for (auto& ele : stats) { + for (const auto& ele : stats) { dumpStats.push_back(&ele.second); } diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 1798555be5..7d0fe79e06 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -42,9 +42,8 @@ public: public: std::string layerName; std::string packageName; - int64_t statsStart = 0; - int64_t statsEnd = 0; int32_t totalFrames = 0; + int32_t droppedFrames = 0; std::unordered_map<std::string, Histogram> deltas; std::string toString() const; @@ -58,6 +57,8 @@ public: int32_t totalFrames = 0; int32_t missedFrames = 0; int32_t clientCompositionFrames = 0; + int64_t displayOnTime = 0; + Histogram presentToPresent; std::unordered_map<std::string, TimeStatsLayer> stats; std::string toString(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto index f29fbd187a..377612a9ae 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto +++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto @@ -20,46 +20,63 @@ package android.surfaceflinger; option optimize_for = LITE_RUNTIME; +// //depot/google3/wireless/android/graphics/surfaceflingerstats/proto/ +// timestats.proto is based on this proto. Please only make valid protobuf +// changes to these messages, and keep google3 side proto messages in sync if +// the end to end pipeline needs to be updated. + +// Next tag: 9 message SFTimeStatsGlobalProto { - // The start & end timestamps in UTC as - // milliseconds since January 1, 1970 + // The stats start time in UTC as seconds since January 1, 1970 optional int64 stats_start = 1; + // The stats end time in UTC as seconds since January 1, 1970 optional int64 stats_end = 2; - // Total frames + // Total number of frames presented during tracing period. optional int32 total_frames = 3; // Total missed frames of SurfaceFlinger. optional int32 missed_frames = 4; // Total frames fallback to client composition. optional int32 client_composition_frames = 5; - + // Primary display on time in milliseconds. + optional int64 display_on_time = 7; + // Present to present histogram. + repeated SFTimeStatsHistogramBucketProto present_to_present = 8; + // Stats per layer. Apps could have multiple layers. repeated SFTimeStatsLayerProto stats = 6; } +// Next tag: 8 message SFTimeStatsLayerProto { - // The layer name + // The name of the visible view layer. optional string layer_name = 1; - // The package name + // The package name of the application owning this layer. optional string package_name = 2; - // The start & end timestamps in UTC as - // milliseconds since January 1, 1970 + // The stats start time in UTC as seconds since January 1, 1970 optional int64 stats_start = 3; + // The stats end time in UTC as seconds since January 1, 1970 optional int64 stats_end = 4; - // Distinct frame count. + // Total number of frames presented during tracing period. optional int32 total_frames = 5; - + // Total number of frames dropped by SurfaceFlinger. + optional int32 dropped_frames = 7; + // There are multiple timestamps tracked in SurfaceFlinger, and these are the + // histograms of deltas between different combinations of those timestamps. repeated SFTimeStatsDeltaProto deltas = 6; } +// Next tag: 3 message SFTimeStatsDeltaProto { // Name of the time interval optional string delta_name = 1; - // Histogram of the delta time + // Histogram of the delta time. There should be at most 85 buckets ranging + // from [0ms, 1ms) to [1000ms, infinity) repeated SFTimeStatsHistogramBucketProto histograms = 2; } +// Next tag: 3 message SFTimeStatsHistogramBucketProto { - // Lower bound of render time in milliseconds. - optional int32 render_millis = 1; + // Lower bound of time interval in milliseconds. + optional int32 time_millis = 1; // Number of frames in the bucket. optional int32 frame_count = 2; } diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h deleted file mode 100644 index b11d0576c4..0000000000 --- a/services/surfaceflinger/Transform.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2007 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_TRANSFORM_H -#define ANDROID_TRANSFORM_H - -#include <stdint.h> -#include <sys/types.h> - -#include <ui/Point.h> -#include <ui/Rect.h> -#include <math/vec2.h> -#include <math/vec3.h> - -#include <gui/ISurfaceComposer.h> - -#include <hardware/hardware.h> - -namespace android { - -class Region; - -// --------------------------------------------------------------------------- - -class Transform -{ -public: - Transform(); - Transform(const Transform& other); - explicit Transform(uint32_t orientation); - ~Transform(); - - enum orientation_flags { - ROT_0 = 0x00000000, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, - ROT_180 = FLIP_H|FLIP_V, - ROT_270 = ROT_180|ROT_90, - ROT_INVALID = 0x80 - }; - - static orientation_flags fromRotation(ISurfaceComposer::Rotation rotation); - - enum type_mask { - IDENTITY = 0, - TRANSLATE = 0x1, - ROTATE = 0x2, - SCALE = 0x4, - UNKNOWN = 0x8 - }; - - // query the transform - bool preserveRects() const; - uint32_t getType() const; - uint32_t getOrientation() const; - - const vec3& operator [] (size_t i) const; // returns column i - float tx() const; - float ty() const; - - // modify the transform - void reset(); - void set(float tx, float ty); - void set(float a, float b, float c, float d); - status_t set(uint32_t flags, float w, float h); - - // transform data - Rect makeBounds(int w, int h) const; - vec2 transform(int x, int y) const; - Region transform(const Region& reg) const; - Rect transform(const Rect& bounds, - bool roundOutwards = false) const; - FloatRect transform(const FloatRect& bounds) const; - Transform operator * (const Transform& rhs) const; - // assumes the last row is < 0 , 0 , 1 > - vec2 transform(const vec2& v) const; - vec3 transform(const vec3& v) const; - - Transform inverse() const; - - // for debugging - void dump(const char* name) const; - -private: - struct mat33 { - vec3 v[3]; - inline const vec3& operator [] (int i) const { return v[i]; } - inline vec3& operator [] (int i) { return v[i]; } - }; - - enum { UNKNOWN_TYPE = 0x80000000 }; - - uint32_t type() const; - static bool absIsOne(float f); - static bool isZero(float f); - - mat33 mMatrix; - mutable uint32_t mType; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif /* ANDROID_TRANSFORM_H */ diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h deleted file mode 100644 index a4c5262ddf..0000000000 --- a/services/surfaceflinger/clz.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2007 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_SURFACE_FLINGER_CLZ_H - -#include <stdint.h> - -namespace android { - -int inline clz(int32_t x) { - return __builtin_clz(x); -} - -template <typename T> -static inline T min(T a, T b) { - return a<b ? a : b; -} -template <typename T> -static inline T min(T a, T b, T c) { - return min(a, min(b, c)); -} -template <typename T> -static inline T min(T a, T b, T c, T d) { - return min(a, b, min(c, d)); -} - -template <typename T> -static inline T max(T a, T b) { - return a>b ? a : b; -} -template <typename T> -static inline T max(T a, T b, T c) { - return max(a, max(b, c)); -} -template <typename T> -static inline T max(T a, T b, T c, T d) { - return max(a, b, max(c, d)); -} - -template <typename T> -static inline -void swap(T& a, T& b) { - T t(a); - a = b; - b = t; -} - - -}; // namespace android - -#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */ diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index fcf42f00a9..1d7fb6791f 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -31,17 +31,12 @@ bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs int32_t lz = lhs->z; int32_t rz = rhs->z; if (lz != rz) { - return (lz > rz) ? 1 : -1; + return lz < rz; } return lhs->id < rhs->id; } -bool sortLayerUniquePtrs(const std::unique_ptr<LayerProtoParser::Layer>& lhs, - const std::unique_ptr<LayerProtoParser::Layer>& rhs) { - return sortLayers(lhs.get(), rhs.get()); -} - const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo( const LayersProto& layersProto) { LayerGlobal layerGlobal; @@ -52,77 +47,80 @@ const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo( return layerGlobal; } -std::vector<std::unique_ptr<LayerProtoParser::Layer>> LayerProtoParser::generateLayerTree( - const LayersProto& layersProto) { - std::unordered_map<int32_t, LayerProtoParser::Layer*> layerMap = generateMap(layersProto); - std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers; - - for (std::pair<int32_t, Layer*> kv : layerMap) { - if (kv.second->parent == nullptr) { - // Make unique_ptr for top level layers since they are not children. This ensures there - // will only be one unique_ptr made for each layer. - layers.push_back(std::unique_ptr<Layer>(kv.second)); +LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) { + LayerTree layerTree; + layerTree.allLayers = generateLayerList(layersProto); + + // find and sort the top-level layers + for (Layer& layer : layerTree.allLayers) { + if (layer.parent == nullptr) { + layerTree.topLevelLayers.push_back(&layer); } } + std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers); - std::sort(layers.begin(), layers.end(), sortLayerUniquePtrs); - return layers; + return layerTree; } -std::unordered_map<int32_t, LayerProtoParser::Layer*> LayerProtoParser::generateMap( +std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList( const LayersProto& layersProto) { + std::vector<Layer> layerList; std::unordered_map<int32_t, Layer*> layerMap; + // build the layer list and the layer map + layerList.reserve(layersProto.layers_size()); + layerMap.reserve(layersProto.layers_size()); for (int i = 0; i < layersProto.layers_size(); i++) { - const LayerProto& layerProto = layersProto.layers(i); - layerMap[layerProto.id()] = generateLayer(layerProto); + layerList.emplace_back(generateLayer(layersProto.layers(i))); + // this works because layerList never changes capacity + layerMap[layerList.back().id] = &layerList.back(); } + // fix up children and relatives for (int i = 0; i < layersProto.layers_size(); i++) { - const LayerProto& layerProto = layersProto.layers(i); - updateChildrenAndRelative(layerProto, layerMap); + updateChildrenAndRelative(layersProto.layers(i), layerMap); } - return layerMap; + return layerList; } -LayerProtoParser::Layer* LayerProtoParser::generateLayer(const LayerProto& layerProto) { - Layer* layer = new Layer(); - layer->id = layerProto.id(); - layer->name = layerProto.name(); - layer->type = layerProto.type(); - layer->transparentRegion = generateRegion(layerProto.transparent_region()); - layer->visibleRegion = generateRegion(layerProto.visible_region()); - layer->damageRegion = generateRegion(layerProto.damage_region()); - layer->layerStack = layerProto.layer_stack(); - layer->z = layerProto.z(); - layer->position = {layerProto.position().x(), layerProto.position().y()}; - layer->requestedPosition = {layerProto.requested_position().x(), +LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerProto) { + Layer layer; + layer.id = layerProto.id(); + layer.name = layerProto.name(); + layer.type = layerProto.type(); + layer.transparentRegion = generateRegion(layerProto.transparent_region()); + layer.visibleRegion = generateRegion(layerProto.visible_region()); + layer.damageRegion = generateRegion(layerProto.damage_region()); + layer.layerStack = layerProto.layer_stack(); + layer.z = layerProto.z(); + layer.position = {layerProto.position().x(), layerProto.position().y()}; + layer.requestedPosition = {layerProto.requested_position().x(), layerProto.requested_position().y()}; - layer->size = {layerProto.size().w(), layerProto.size().h()}; - layer->crop = generateRect(layerProto.crop()); - layer->finalCrop = generateRect(layerProto.final_crop()); - layer->isOpaque = layerProto.is_opaque(); - layer->invalidate = layerProto.invalidate(); - layer->dataspace = layerProto.dataspace(); - layer->pixelFormat = layerProto.pixel_format(); - layer->color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(), + layer.size = {layerProto.size().w(), layerProto.size().h()}; + layer.crop = generateRect(layerProto.crop()); + layer.isOpaque = layerProto.is_opaque(); + layer.invalidate = layerProto.invalidate(); + layer.dataspace = layerProto.dataspace(); + layer.pixelFormat = layerProto.pixel_format(); + layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(), layerProto.color().a()}; - layer->requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(), + layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(), layerProto.requested_color().b(), layerProto.requested_color().a()}; - layer->flags = layerProto.flags(); - layer->transform = generateTransform(layerProto.transform()); - layer->requestedTransform = generateTransform(layerProto.requested_transform()); - layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer()); - layer->queuedFrames = layerProto.queued_frames(); - layer->refreshPending = layerProto.refresh_pending(); - layer->hwcFrame = generateRect(layerProto.hwc_frame()); - layer->hwcCrop = generateFloatRect(layerProto.hwc_crop()); - layer->hwcTransform = layerProto.hwc_transform(); - layer->windowType = layerProto.window_type(); - layer->appId = layerProto.app_id(); - layer->hwcCompositionType = layerProto.hwc_composition_type(); - layer->isProtected = layerProto.is_protected(); + layer.flags = layerProto.flags(); + layer.transform = generateTransform(layerProto.transform()); + layer.requestedTransform = generateTransform(layerProto.requested_transform()); + layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer()); + layer.bufferTransform = generateTransform(layerProto.buffer_transform()); + layer.queuedFrames = layerProto.queued_frames(); + layer.refreshPending = layerProto.refresh_pending(); + layer.hwcFrame = generateRect(layerProto.hwc_frame()); + layer.hwcCrop = generateFloatRect(layerProto.hwc_crop()); + layer.hwcTransform = layerProto.hwc_transform(); + layer.windowType = layerProto.window_type(); + layer.appId = layerProto.app_id(); + layer.hwcCompositionType = layerProto.hwc_composition_type(); + layer.isProtected = layerProto.is_protected(); return layer; } @@ -186,9 +184,7 @@ void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto, for (int i = 0; i < layerProto.children_size(); i++) { if (layerMap.count(layerProto.children(i)) > 0) { - // Only make unique_ptrs for children since they are guaranteed to be unique, only one - // parent per child. This ensures there will only be one unique_ptr made for each layer. - currLayer->children.push_back(std::unique_ptr<Layer>(layerMap[layerProto.children(i)])); + currLayer->children.push_back(layerMap[layerProto.children(i)]); } } @@ -211,29 +207,28 @@ void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto, } } -std::string LayerProtoParser::layersToString( - std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers) { +std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) { std::string result; - for (std::unique_ptr<LayerProtoParser::Layer>& layer : layers) { + for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) { if (layer->zOrderRelativeOf != nullptr) { continue; } - result.append(layerToString(layer.get()).c_str()); + result.append(layerToString(layer)); } return result; } -std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) { +std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) { std::string result; std::vector<Layer*> traverse(layer->relatives); - for (std::unique_ptr<LayerProtoParser::Layer>& child : layer->children) { + for (LayerProtoParser::Layer* child : layer->children) { if (child->zOrderRelativeOf != nullptr) { continue; } - traverse.push_back(child.get()); + traverse.push_back(child); } std::sort(traverse.begin(), traverse.end(), sortLayers); @@ -244,13 +239,13 @@ std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) { if (relative->z >= 0) { break; } - result.append(layerToString(relative).c_str()); + result.append(layerToString(relative)); } - result.append(layer->to_string().c_str()); + result.append(layer->to_string()); result.append("\n"); for (; i < traverse.size(); i++) { auto& relative = traverse[i]; - result.append(layerToString(relative).c_str()); + result.append(layerToString(relative)); } return result; @@ -298,8 +293,7 @@ std::string LayerProtoParser::Layer::to_string() const { z, static_cast<double>(position.x), static_cast<double>(position.y), size.x, size.y); - StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(), - finalCrop.to_string().c_str()); + StringAppendF(&result, "crop=%s, ", crop.to_string().c_str()); StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate); StringAppendF(&result, "dataspace=%s, ", dataspace.c_str()); StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str()); @@ -312,6 +306,7 @@ std::string LayerProtoParser::Layer::to_string() const { StringAppendF(&result, " zOrderRelativeOf=%s\n", zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str()); StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str()); + StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str()); StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending); StringAppendF(&result, " windowType=%d, appId=%d", windowType, appId); diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index 74a6f28f2b..6b3b49763d 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -80,7 +80,7 @@ public: public: int32_t id; std::string name; - std::vector<std::unique_ptr<Layer>> children; + std::vector<Layer*> children; std::vector<Layer*> relatives; std::string type; LayerProtoParser::Region transparentRegion; @@ -92,7 +92,6 @@ public: float2 requestedPosition; int2 size; LayerProtoParser::Rect crop; - LayerProtoParser::Rect finalCrop; bool isOpaque; bool invalidate; std::string dataspace; @@ -105,6 +104,7 @@ public: Layer* parent = 0; Layer* zOrderRelativeOf = 0; LayerProtoParser::ActiveBuffer activeBuffer; + Transform bufferTransform; int32_t queuedFrames; bool refreshPending; LayerProtoParser::Rect hwcFrame; @@ -126,13 +126,22 @@ public: int32_t globalTransform; }; + class LayerTree { + public: + // all layers in LayersProto and in the original order + std::vector<Layer> allLayers; + + // pointers to top-level layers in allLayers + std::vector<Layer*> topLevelLayers; + }; + static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto); - static std::vector<std::unique_ptr<Layer>> generateLayerTree(const LayersProto& layersProto); - static std::string layersToString(std::vector<std::unique_ptr<LayerProtoParser::Layer>> layers); + static LayerTree generateLayerTree(const LayersProto& layersProto); + static std::string layerTreeToString(const LayerTree& layerTree); private: - static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto); - static LayerProtoParser::Layer* generateLayer(const LayerProto& layerProto); + static std::vector<Layer> generateLayerList(const LayersProto& layersProto); + static LayerProtoParser::Layer generateLayer(const LayerProto& layerProto); static LayerProtoParser::Region generateRegion(const RegionProto& regionProto); static LayerProtoParser::Rect generateRect(const RectProto& rectProto); static LayerProtoParser::FloatRect generateFloatRect(const FloatRectProto& rectProto); @@ -142,7 +151,7 @@ private: static void updateChildrenAndRelative(const LayerProto& layerProto, std::unordered_map<int32_t, Layer*>& layerMap); - static std::string layerToString(LayerProtoParser::Layer* layer); + static std::string layerToString(const LayerProtoParser::Layer* layer); }; } // namespace surfaceflinger diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index edf56abc4a..2a096345af 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -41,7 +41,7 @@ message LayerProto { // The layer's crop in it's own bounds. optional RectProto crop = 14; // The layer's crop in it's parent's bounds. - optional RectProto final_crop = 15; + optional RectProto final_crop = 15 [deprecated=true]; optional bool is_opaque = 16; optional bool invalidate = 17; optional string dataspace = 18; @@ -84,6 +84,8 @@ message LayerProto { optional uint64 curr_frame = 37; // A list of barriers that the layer is waiting to update state. repeated BarrierLayerProto barrier_layer = 38; + // If active_buffer is not null, record its transform. + optional TransformProto buffer_transform = 39; } message PositionProto { diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index b1ff522de6..9c2edca43d 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -21,16 +21,16 @@ #include <android/frameworks/displayservice/1.0/IDisplayService.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/graphics/allocator/2.0/IAllocator.h> -#include <cutils/sched_policy.h> -#include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <configstore/Utils.h> +#include <cutils/sched_policy.h> #include <displayservice/DisplayService.h> #include <hidl/LegacySupport.h> -#include <configstore/Utils.h> #include "GpuService.h" #include "SurfaceFlinger.h" +#include "SurfaceFlingerFactory.h" using namespace android; @@ -85,7 +85,7 @@ int main(int, char**) { ps->startThreadPool(); // instantiate surfaceflinger - sp<SurfaceFlinger> flinger = new SurfaceFlinger(); + sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger(); setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY); diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index c511c5e753..604aa7df56 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -17,6 +17,7 @@ cc_test { defaults: ["surfaceflinger_defaults"], test_suites: ["device-tests"], srcs: [ + "Credentials_test.cpp", "Stress_test.cpp", "SurfaceInterceptor_test.cpp", "Transaction_test.cpp", diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp new file mode 100644 index 0000000000..a73ec6c7ef --- /dev/null +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -0,0 +1,331 @@ +#include <algorithm> +#include <functional> +#include <limits> +#include <ostream> + +#include <gtest/gtest.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/LayerDebugInfo.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/android_filesystem_config.h> +#include <private/gui/ComposerService.h> + +#include <ui/DisplayInfo.h> +#include <utils/String8.h> + +namespace android { + +using Transaction = SurfaceComposerClient::Transaction; + +namespace { +const String8 DISPLAY_NAME("Credentials Display Test"); +const String8 SURFACE_NAME("Test Surface Name"); +const uint32_t ROTATION = 0; +const float FRAME_SCALE = 1.0f; +} // namespace + +/** + * This class tests the CheckCredentials method in SurfaceFlinger. + * Methods like EnableVsyncInjections and InjectVsync are not tested since they do not + * return anything meaningful. + */ +class CredentialsTest : public ::testing::Test { +protected: + void SetUp() override { + // Start the tests as root. + seteuid(AID_ROOT); + + ASSERT_NO_FATAL_FAILURE(initClient()); + } + + void TearDown() override { + mComposerClient->dispose(); + mBGSurfaceControl.clear(); + mComposerClient.clear(); + // Finish the tests as root. + seteuid(AID_ROOT); + } + + sp<IBinder> mDisplay; + sp<IBinder> mVirtualDisplay; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mVirtualSurfaceControl; + + void initClient() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + } + + void setupBackgroundSurface() { + mDisplay = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(mDisplay, &info); + const ssize_t displayWidth = info.w; + const ssize_t displayHeight = info.h; + + // Background surface + mBGSurfaceControl = + mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + + Transaction t; + t.setDisplayLayerStack(mDisplay, 0); + ASSERT_EQ(NO_ERROR, + t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply()); + } + + void setupVirtualDisplay() { + mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); + const ssize_t displayWidth = 100; + const ssize_t displayHeight = 100; + + // Background surface + mVirtualSurfaceControl = + mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mVirtualSurfaceControl != nullptr); + ASSERT_TRUE(mVirtualSurfaceControl->isValid()); + + Transaction t; + t.setDisplayLayerStack(mVirtualDisplay, 0); + ASSERT_EQ(NO_ERROR, + t.setLayer(mVirtualSurfaceControl, INT_MAX - 3) + .show(mVirtualSurfaceControl) + .apply()); + } + + /** + * Sets UID to imitate Graphic's process. + */ + void setGraphicsUID() { + seteuid(AID_ROOT); + seteuid(AID_GRAPHICS); + } + + /** + * Sets UID to imitate System's process. + */ + void setSystemUID() { + seteuid(AID_ROOT); + seteuid(AID_SYSTEM); + } + + /** + * Sets UID to imitate a process that doesn't have any special privileges in + * our code. + */ + void setBinUID() { + seteuid(AID_ROOT); + seteuid(AID_BIN); + } + + /** + * Template function the check a condition for different types of users: root + * graphics, system, and non-supported user. Root, graphics, and system should + * always equal privilegedValue, and non-supported user should equal unprivilegedValue. + */ + template <typename T> + void checkWithPrivileges(std::function<T()> condition, T privilegedValue, T unprivilegedValue) { + // Check with root. + seteuid(AID_ROOT); + ASSERT_EQ(privilegedValue, condition()); + + // Check as a Graphics user. + setGraphicsUID(); + ASSERT_EQ(privilegedValue, condition()); + + // Check as a system user. + setSystemUID(); + ASSERT_EQ(privilegedValue, condition()); + + // Check as a non-supported user. + setBinUID(); + ASSERT_EQ(unprivilegedValue, condition()); + } +}; + +TEST_F(CredentialsTest, ClientInitTest) { + // Root can init can init the client. + ASSERT_NO_FATAL_FAILURE(initClient()); + + // Graphics can init the client. + setGraphicsUID(); + ASSERT_NO_FATAL_FAILURE(initClient()); + + // System can init the client. + setSystemUID(); + ASSERT_NO_FATAL_FAILURE(initClient()); + + // No one else can init the client. + setBinUID(); + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_INIT, mComposerClient->initCheck()); +} + +TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { + std::function<bool()> condition = [=]() { + sp<IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + return (display != nullptr); + }; + // Anyone can access display information. + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true)); +} + +TEST_F(CredentialsTest, AllowedGetterMethodsTest) { + // The following methods are tested with a UID that is not root, graphics, + // or system, to show that anyone can access them. + setBinUID(); + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + ASSERT_TRUE(display != nullptr); + + DisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info)); + + Vector<DisplayInfo> configs; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs)); + + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display)); + + ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE), + SurfaceComposerClient::getActiveColorMode(display)); +} + +TEST_F(CredentialsTest, GetDisplayColorModesTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + std::function<status_t()> condition = [=]() { + Vector<ui::ColorMode> outColorModes; + return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR)); +} + +TEST_F(CredentialsTest, SetActiveConfigTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + std::function<status_t()> condition = [=]() { + return SurfaceComposerClient::setActiveConfig(display, 0); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); +} + +TEST_F(CredentialsTest, SetActiveColorModeTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + std::function<status_t()> condition = [=]() { + return SurfaceComposerClient::setActiveColorMode(display, ui::ColorMode::NATIVE); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); +} + +TEST_F(CredentialsTest, CreateSurfaceTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + const ssize_t displayWidth = info.w; + const ssize_t displayHeight = info.h; + + std::function<bool()> condition = [=]() { + mBGSurfaceControl = + mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + return mBGSurfaceControl != nullptr && mBGSurfaceControl->isValid(); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); +} + +TEST_F(CredentialsTest, CreateDisplayTest) { + std::function<bool()> condition = [=]() { + sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); + return testDisplay.get() != nullptr; + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); + + condition = [=]() { + sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); + return testDisplay.get() != nullptr; + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); +} + +TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) { + setupVirtualDisplay(); + + DisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info)); + SurfaceComposerClient::destroyDisplay(mVirtualDisplay); + // This test currently fails. TODO(b/112002626): Find a way to properly create + // a display in the test environment, so that destroy display can remove it. + ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info)); +} + +TEST_F(CredentialsTest, CaptureTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + std::function<status_t()> condition = [=]() { + sp<GraphicBuffer> outBuffer; + return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/, + 0 /*reqHeight*/, false, ROTATION, &outBuffer); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); +} + +TEST_F(CredentialsTest, CaptureLayersTest) { + setupBackgroundSurface(); + sp<GraphicBuffer> outBuffer; + std::function<status_t()> condition = [=]() { + sp<GraphicBuffer> outBuffer; + return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(), + ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, + Rect(), FRAME_SCALE, &outBuffer); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); +} + +/** + * The following tests are for methods accessible directly through SurfaceFlinger. + */ + +/** + * An app can pass a buffer queue to the media server and ask the media server to decode a DRM video + * to that buffer queue. The media server is the buffer producer in this case. Because the app may create + * its own buffer queue and act as the buffer consumer, the media server wants to be careful to avoid + * sending decoded video frames to the app. This is where authenticateSurfaceTexture call comes in, to check + * the consumer of a buffer queue is SurfaceFlinger. + */ +TEST_F(CredentialsTest, AuthenticateSurfaceTextureTest) { + setupBackgroundSurface(); + sp<IGraphicBufferProducer> producer = + mBGSurfaceControl->getSurface()->getIGraphicBufferProducer(); + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + + std::function<bool()> condition = [=]() { return sf->authenticateSurfaceTexture(producer); }; + // Anyone should be able to check if the consumer of the buffer queue is SF. + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true)); +} + +TEST_F(CredentialsTest, GetLayerDebugInfo) { + setupBackgroundSurface(); + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + + // Historically, only root and shell can access the getLayerDebugInfo which + // is called when we call dumpsys. I don't see a reason why we should change this. + std::vector<LayerDebugInfo> outLayers; + // Check with root. + seteuid(AID_ROOT); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + + // Check as a shell. + seteuid(AID_SHELL); + ASSERT_EQ(NO_ERROR, sf->getLayerDebugInfo(&outLayers)); + + // Check as anyone else. + seteuid(AID_ROOT); + seteuid(AID_BIN); + ASSERT_EQ(PERMISSION_DENIED, sf->getLayerDebugInfo(&outLayers)); +} +} // namespace android diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp index 4577153df2..3e1be8e288 100644 --- a/services/surfaceflinger/tests/Stress_test.cpp +++ b/services/surfaceflinger/tests/Stress_test.cpp @@ -101,10 +101,7 @@ TEST(LayerProtoStress, mem_info) { for (int i = 0; i < 100000; i++) { surfaceflinger::LayersProto layersProto = generateLayerProto(); auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto); - // Allow some layerTrees to just fall out of scope (instead of std::move) - if (i % 2) { - surfaceflinger::LayerProtoParser::layersToString(std::move(layerTree)); - } + surfaceflinger::LayerProtoParser::layerTreeToString(layerTree); } system(cmd.c_str()); } diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter index cca84e552f..8bd5fcbcc3 100644 --- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter +++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter @@ -1,5 +1,5 @@ { "presubmit": { - "filter": "LayerTransactionTest.*:LayerUpdateTest.*:ChildLayerTest.*:SurfaceFlingerStress.*:CropLatchingTest.*:GeometryLatchingTest.*:ScreenCaptureTest.*:DereferenceSurfaceControlTest.*:-CropLatchingTest.FinalCropLatchingBufferOldSize" + "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*" } } diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index de78c3f355..740d2fa302 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -42,13 +42,16 @@ constexpr uint32_t BUFFER_UPDATES = 18; constexpr uint32_t LAYER_UPDATE = INT_MAX - 2; constexpr uint32_t SIZE_UPDATE = 134; constexpr uint32_t STACK_UPDATE = 1; -constexpr uint64_t DEFERRED_UPDATE = 13; +constexpr uint64_t DEFERRED_UPDATE = 0; constexpr float ALPHA_UPDATE = 0.29f; constexpr float POSITION_UPDATE = 121; const Rect CROP_UPDATE(16, 16, 32, 32); const String8 DISPLAY_NAME("SurfaceInterceptor Display Test"); +constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface"; +constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG 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/SurfaceTrace.dat"; @@ -94,30 +97,21 @@ static void disableInterceptor() { system("service call SurfaceFlinger 1020 i32 0 > /dev/null"); } -int32_t getSurfaceId(const std::string& surfaceName) { - enableInterceptor(); - disableInterceptor(); - Trace capturedTrace; - readProtoFile(&capturedTrace); +int32_t getSurfaceId(const Trace& capturedTrace, const std::string& surfaceName) { int32_t layerId = 0; - for (const auto& increment : *capturedTrace.mutable_increment()) { + for (const auto& increment : capturedTrace.increment()) { if (increment.increment_case() == increment.kSurfaceCreation) { if (increment.surface_creation().name() == surfaceName) { layerId = increment.surface_creation().id(); - break; } } } return layerId; } -int32_t getDisplayId(const std::string& displayName) { - enableInterceptor(); - disableInterceptor(); - Trace capturedTrace; - readProtoFile(&capturedTrace); +int32_t getDisplayId(const Trace& capturedTrace, const std::string& displayName) { int32_t displayId = 0; - for (const auto& increment : *capturedTrace.mutable_increment()) { + for (const auto& increment : capturedTrace.increment()) { if (increment.increment_case() == increment.kDisplayCreation) { if (increment.display_creation().name() == displayName) { displayId = increment.display_creation().id(); @@ -130,36 +124,15 @@ int32_t getDisplayId(const std::string& displayName) { class SurfaceInterceptorTest : public ::testing::Test { protected: - virtual void SetUp() { + void SetUp() override { // Allow SurfaceInterceptor write to /data system("setenforce 0"); mComposerClient = new SurfaceComposerClient; ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); - DisplayInfo info; - SurfaceComposerClient::getDisplayInfo(display, &info); - ssize_t displayWidth = info.w; - ssize_t displayHeight = info.h; - - // Background surface - mBGSurfaceControl = mComposerClient->createSurface( - String8("BG Interceptor Test Surface"), displayWidth, displayHeight, - PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mBGSurfaceControl != nullptr); - ASSERT_TRUE(mBGSurfaceControl->isValid()); - mBGLayerId = getSurfaceId("BG Interceptor Test Surface"); - - Transaction t; - t.setDisplayLayerStack(display, 0); - ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3) - .show(mBGSurfaceControl) - .apply()); } - virtual void TearDown() { + void TearDown() override { mComposerClient->dispose(); mBGSurfaceControl.clear(); mComposerClient.clear(); @@ -168,18 +141,25 @@ protected: sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mBGSurfaceControl; int32_t mBGLayerId; - // Used to verify creation and destruction of surfaces and displays - int32_t mTargetId; public: - void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - bool (SurfaceInterceptorTest::* verification)(Trace *)); - void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - SurfaceChange::SurfaceChangeCase changeCase); - void captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - Increment::IncrementCase incrementCase); - void runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&), - bool intercepted = false); + using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&); + using TestAction = void (SurfaceInterceptorTest::*)(); + using TestBooleanVerification = bool (SurfaceInterceptorTest::*)(const Trace&); + using TestVerification = void (SurfaceInterceptorTest::*)(const Trace&); + + void setupBackgroundSurface(); + void preProcessTrace(const Trace& trace); + + // captureTest will enable SurfaceInterceptor, setup background surface, + // disable SurfaceInterceptor, collect the trace and process the trace for + // id of background surface before further verification. + void captureTest(TestTransactionAction action, TestBooleanVerification verification); + void captureTest(TestTransactionAction action, SurfaceChange::SurfaceChangeCase changeCase); + void captureTest(TestTransactionAction action, Increment::IncrementCase incrementCase); + void captureTest(TestAction action, TestBooleanVerification verification); + void captureTest(TestAction action, TestVerification verification); + void runInTransaction(TestTransactionAction action); // Verification of changes to a surface bool positionUpdateFound(const SurfaceChange& change, bool foundPosition); @@ -187,7 +167,6 @@ public: bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha); bool layerUpdateFound(const SurfaceChange& change, bool foundLayer); bool cropUpdateFound(const SurfaceChange& change, bool foundCrop); - bool finalCropUpdateFound(const SurfaceChange& change, bool foundFinalCrop); bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix); bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode); bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion); @@ -196,18 +175,22 @@ public: bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag); bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag); bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred); - bool surfaceUpdateFound(Trace* trace, SurfaceChange::SurfaceChangeCase changeCase); - void assertAllUpdatesFound(Trace* trace); + bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase); + + // Find all of the updates in the single trace + void assertAllUpdatesFound(const Trace& trace); // Verification of creation and deletion of a surface bool surfaceCreationFound(const Increment& increment, bool foundSurface); - bool surfaceDeletionFound(const Increment& increment, bool foundSurface); + bool surfaceDeletionFound(const Increment& increment, const int32_t targetId, + bool foundSurface); bool displayCreationFound(const Increment& increment, bool foundDisplay); - bool displayDeletionFound(const Increment& increment, bool foundDisplay); - bool singleIncrementFound(Trace* trace, Increment::IncrementCase incrementCase); + bool displayDeletionFound(const Increment& increment, const int32_t targetId, + bool foundDisplay); + bool singleIncrementFound(const Trace& trace, Increment::IncrementCase incrementCase); // Verification of buffer updates - bool bufferUpdatesFound(Trace* trace); + bool bufferUpdatesFound(const Trace& trace); // Perform each of the possible changes to a surface void positionUpdate(Transaction&); @@ -215,7 +198,6 @@ public: void alphaUpdate(Transaction&); void layerUpdate(Transaction&); void cropUpdate(Transaction&); - void finalCropUpdate(Transaction&); void matrixUpdate(Transaction&); void overrideScalingModeUpdate(Transaction&); void transparentRegionHintUpdate(Transaction&); @@ -230,48 +212,93 @@ public: void nBufferUpdates(); void runAllUpdates(); + +private: + void captureInTransaction(TestTransactionAction action, Trace*); + void capture(TestAction action, Trace*); }; -void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - bool (SurfaceInterceptorTest::* verification)(Trace *)) -{ - runInTransaction(action, true); +void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) { + enableInterceptor(); + setupBackgroundSurface(); + runInTransaction(action); + disableInterceptor(); + ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); + preProcessTrace(*outTrace); +} + +void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) { + enableInterceptor(); + setupBackgroundSurface(); + (this->*action)(); + disableInterceptor(); + ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); + preProcessTrace(*outTrace); +} + +void SurfaceInterceptorTest::setupBackgroundSurface() { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ssize_t displayWidth = info.w; + ssize_t displayHeight = info.h; + + // Background surface + mBGSurfaceControl = mComposerClient->createSurface( + String8(TEST_SURFACE_NAME), displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + + Transaction t; + t.setDisplayLayerStack(display, 0); + ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3) + .show(mBGSurfaceControl) + .apply()); +} + +void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) { + mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME); +} + +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + TestBooleanVerification verification) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE((this->*verification)(&capturedTrace)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE((this->*verification)(capturedTrace)); } -void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - Increment::IncrementCase incrementCase) -{ - runInTransaction(action, true); +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + Increment::IncrementCase incrementCase) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase)); } -void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - SurfaceChange::SurfaceChangeCase changeCase) -{ - runInTransaction(action, true); +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + SurfaceChange::SurfaceChangeCase changeCase) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase)); } -void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&), - bool intercepted) -{ - if (intercepted) { - enableInterceptor(); - } +void SurfaceInterceptorTest::captureTest(TestAction action, TestBooleanVerification verification) { + Trace capturedTrace; + capture(action, &capturedTrace); + ASSERT_TRUE((this->*verification)(capturedTrace)); +} + +void SurfaceInterceptorTest::captureTest(TestAction action, TestVerification verification) { + Trace capturedTrace; + capture(action, &capturedTrace); + (this->*verification)(capturedTrace); +} + +void SurfaceInterceptorTest::runInTransaction(TestTransactionAction action) { Transaction t; (this->*action)(t); t.apply(true); - - if (intercepted) { - disableInterceptor(); - } } void SurfaceInterceptorTest::positionUpdate(Transaction& t) { @@ -291,11 +318,7 @@ void SurfaceInterceptorTest::layerUpdate(Transaction& t) { } void SurfaceInterceptorTest::cropUpdate(Transaction& t) { - t.setCrop(mBGSurfaceControl, CROP_UPDATE); -} - -void SurfaceInterceptorTest::finalCropUpdate(Transaction& t) { - t.setFinalCrop(mBGSurfaceControl, CROP_UPDATE); + t.setCrop_legacy(mBGSurfaceControl, CROP_UPDATE); } void SurfaceInterceptorTest::matrixUpdate(Transaction& t) { @@ -328,7 +351,8 @@ void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) { } void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) { - t.deferTransactionUntil(mBGSurfaceControl, mBGSurfaceControl->getHandle(), DEFERRED_UPDATE); + t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(), + DEFERRED_UPDATE); } void SurfaceInterceptorTest::displayCreation(Transaction&) { @@ -338,7 +362,6 @@ void SurfaceInterceptorTest::displayCreation(Transaction&) { void SurfaceInterceptorTest::displayDeletion(Transaction&) { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); - mTargetId = getDisplayId(DISPLAY_NAME.string()); SurfaceComposerClient::destroyDisplay(testDisplay); } @@ -348,7 +371,6 @@ void SurfaceInterceptorTest::runAllUpdates() { runInTransaction(&SurfaceInterceptorTest::alphaUpdate); runInTransaction(&SurfaceInterceptorTest::layerUpdate); runInTransaction(&SurfaceInterceptorTest::cropUpdate); - runInTransaction(&SurfaceInterceptorTest::finalCropUpdate); runInTransaction(&SurfaceInterceptorTest::matrixUpdate); runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate); runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate); @@ -380,9 +402,8 @@ bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bo bool hasY(change.position().y() == POSITION_UPDATE); if (hasX && hasY && !foundPosition) { foundPosition = true; - } - // Failed because the position update was found a second time - else if (hasX && hasY && foundPosition) { + } else if (hasX && hasY && foundPosition) { + // Failed because the position update was found a second time [] () { FAIL(); }(); } return foundPosition; @@ -393,8 +414,7 @@ bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool f bool hasHeight(change.size().w() == SIZE_UPDATE); if (hasWidth && hasHeight && !foundSize) { foundSize = true; - } - else if (hasWidth && hasHeight && foundSize) { + } else if (hasWidth && hasHeight && foundSize) { [] () { FAIL(); }(); } return foundSize; @@ -404,8 +424,7 @@ bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool bool hasAlpha(change.alpha().alpha() == ALPHA_UPDATE); if (hasAlpha && !foundAlpha) { foundAlpha = true; - } - else if (hasAlpha && foundAlpha) { + } else if (hasAlpha && foundAlpha) { [] () { FAIL(); }(); } return foundAlpha; @@ -415,8 +434,7 @@ bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool bool hasLayer(change.layer().layer() == LAYER_UPDATE); if (hasLayer && !foundLayer) { foundLayer = true; - } - else if (hasLayer && foundLayer) { + } else if (hasLayer && foundLayer) { [] () { FAIL(); }(); } return foundLayer; @@ -429,59 +447,38 @@ bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool f bool hasBottom(change.crop().rectangle().bottom() == CROP_UPDATE.bottom); if (hasLeft && hasRight && hasTop && hasBottom && !foundCrop) { foundCrop = true; - } - else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) { + } else if (hasLeft && hasRight && hasTop && hasBottom && foundCrop) { [] () { FAIL(); }(); } return foundCrop; } -bool SurfaceInterceptorTest::finalCropUpdateFound(const SurfaceChange& change, - bool foundFinalCrop) -{ - bool hasLeft(change.final_crop().rectangle().left() == CROP_UPDATE.left); - bool hasTop(change.final_crop().rectangle().top() == CROP_UPDATE.top); - bool hasRight(change.final_crop().rectangle().right() == CROP_UPDATE.right); - bool hasBottom(change.final_crop().rectangle().bottom() == CROP_UPDATE.bottom); - if (hasLeft && hasRight && hasTop && hasBottom && !foundFinalCrop) { - foundFinalCrop = true; - } - else if (hasLeft && hasRight && hasTop && hasBottom && foundFinalCrop) { - [] () { FAIL(); }(); - } - return foundFinalCrop; -} - bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool foundMatrix) { bool hasSx((float)change.matrix().dsdx() == (float)M_SQRT1_2); bool hasTx((float)change.matrix().dtdx() == (float)M_SQRT1_2); - bool hasSy((float)change.matrix().dsdy() == (float)-M_SQRT1_2); - bool hasTy((float)change.matrix().dtdy() == (float)M_SQRT1_2); + bool hasSy((float)change.matrix().dsdy() == (float)M_SQRT1_2); + bool hasTy((float)change.matrix().dtdy() == (float)-M_SQRT1_2); if (hasSx && hasTx && hasSy && hasTy && !foundMatrix) { foundMatrix = true; - } - else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) { + } else if (hasSx && hasTx && hasSy && hasTy && foundMatrix) { [] () { FAIL(); }(); } return foundMatrix; } bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change, - bool foundScalingMode) -{ + bool foundScalingMode) { bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE); if (hasScalingUpdate && !foundScalingMode) { foundScalingMode = true; - } - else if (hasScalingUpdate && foundScalingMode) { + } else if (hasScalingUpdate && foundScalingMode) { [] () { FAIL(); }(); } return foundScalingMode; } bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change, - bool foundTransparentRegion) -{ + bool foundTransparentRegion) { auto traceRegion = change.transparent_region_hint().region(0); bool hasLeft(traceRegion.left() == CROP_UPDATE.left); bool hasTop(traceRegion.top() == CROP_UPDATE.top); @@ -489,84 +486,72 @@ bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChang bool hasBottom(traceRegion.bottom() == CROP_UPDATE.bottom); if (hasLeft && hasRight && hasTop && hasBottom && !foundTransparentRegion) { foundTransparentRegion = true; - } - else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) { + } else if (hasLeft && hasRight && hasTop && hasBottom && foundTransparentRegion) { [] () { FAIL(); }(); } return foundTransparentRegion; } bool SurfaceInterceptorTest::layerStackUpdateFound(const SurfaceChange& change, - bool foundLayerStack) -{ + bool foundLayerStack) { bool hasLayerStackUpdate(change.layer_stack().layer_stack() == STACK_UPDATE); if (hasLayerStackUpdate && !foundLayerStack) { foundLayerStack = true; - } - else if (hasLayerStackUpdate && foundLayerStack) { + } else if (hasLayerStackUpdate && foundLayerStack) { [] () { FAIL(); }(); } return foundLayerStack; } bool SurfaceInterceptorTest::hiddenFlagUpdateFound(const SurfaceChange& change, - bool foundHiddenFlag) -{ + bool foundHiddenFlag) { bool hasHiddenFlag(change.hidden_flag().hidden_flag()); if (hasHiddenFlag && !foundHiddenFlag) { foundHiddenFlag = true; - } - else if (hasHiddenFlag && foundHiddenFlag) { + } else if (hasHiddenFlag && foundHiddenFlag) { [] () { FAIL(); }(); } return foundHiddenFlag; } bool SurfaceInterceptorTest::opaqueFlagUpdateFound(const SurfaceChange& change, - bool foundOpaqueFlag) -{ + bool foundOpaqueFlag) { bool hasOpaqueFlag(change.opaque_flag().opaque_flag()); if (hasOpaqueFlag && !foundOpaqueFlag) { foundOpaqueFlag = true; - } - else if (hasOpaqueFlag && foundOpaqueFlag) { + } else if (hasOpaqueFlag && foundOpaqueFlag) { [] () { FAIL(); }(); } return foundOpaqueFlag; } bool SurfaceInterceptorTest::secureFlagUpdateFound(const SurfaceChange& change, - bool foundSecureFlag) -{ + bool foundSecureFlag) { bool hasSecureFlag(change.secure_flag().secure_flag()); if (hasSecureFlag && !foundSecureFlag) { foundSecureFlag = true; - } - else if (hasSecureFlag && foundSecureFlag) { + } else if (hasSecureFlag && foundSecureFlag) { [] () { FAIL(); }(); } return foundSecureFlag; } bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& change, - bool foundDeferred) -{ + bool foundDeferred) { bool hasId(change.deferred_transaction().layer_id() == mBGLayerId); bool hasFrameNumber(change.deferred_transaction().frame_number() == DEFERRED_UPDATE); if (hasId && hasFrameNumber && !foundDeferred) { foundDeferred = true; - } - else if (hasId && hasFrameNumber && foundDeferred) { + } else if (hasId && hasFrameNumber && foundDeferred) { [] () { FAIL(); }(); } return foundDeferred; } -bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, - SurfaceChange::SurfaceChangeCase changeCase) -{ +bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, + SurfaceChange::SurfaceChangeCase changeCase) { bool foundUpdate = false; - for (const auto& increment : *trace->mutable_increment()) { + for (const auto& increment : trace.increment()) { if (increment.increment_case() == increment.kTransaction) { for (const auto& change : increment.transaction().surface_change()) { if (change.id() == mBGLayerId && change.SurfaceChange_case() == changeCase) { @@ -587,9 +572,6 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, case SurfaceChange::SurfaceChangeCase::kCrop: foundUpdate = cropUpdateFound(change, foundUpdate); break; - case SurfaceChange::SurfaceChangeCase::kFinalCrop: - foundUpdate = finalCropUpdateFound(change, foundUpdate); - break; case SurfaceChange::SurfaceChangeCase::kMatrix: foundUpdate = matrixUpdateFound(change, foundUpdate); break; @@ -624,13 +606,12 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, return foundUpdate; } -void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) { +void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) { ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kPosition)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSize)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kAlpha)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kFinalCrop)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint)); @@ -642,24 +623,23 @@ void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) { } bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) { - bool isMatch(increment.surface_creation().name() == LAYER_NAME && + bool isMatch(increment.surface_creation().name() == UNIQUE_LAYER_NAME && increment.surface_creation().w() == SIZE_UPDATE && increment.surface_creation().h() == SIZE_UPDATE); if (isMatch && !foundSurface) { foundSurface = true; - } - else if (isMatch && foundSurface) { + } else if (isMatch && foundSurface) { [] () { FAIL(); }(); } return foundSurface; } -bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool foundSurface) { - bool isMatch(increment.surface_deletion().id() == mTargetId); +bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, + const int32_t targetId, bool foundSurface) { + bool isMatch(increment.surface_deletion().id() == targetId); if (isMatch && !foundSurface) { foundSurface = true; - } - else if (isMatch && foundSurface) { + } else if (isMatch && foundSurface) { [] () { FAIL(); }(); } return foundSurface; @@ -670,42 +650,45 @@ bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bo increment.display_creation().is_secure()); if (isMatch && !foundDisplay) { foundDisplay = true; - } - else if (isMatch && foundDisplay) { + } else if (isMatch && foundDisplay) { [] () { FAIL(); }(); } return foundDisplay; } -bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, bool foundDisplay) { - bool isMatch(increment.display_deletion().id() == mTargetId); +bool SurfaceInterceptorTest::displayDeletionFound(const Increment& increment, + const int32_t targetId, bool foundDisplay) { + bool isMatch(increment.display_deletion().id() == targetId); if (isMatch && !foundDisplay) { foundDisplay = true; - } - else if (isMatch && foundDisplay) { + } else if (isMatch && foundDisplay) { [] () { FAIL(); }(); } return foundDisplay; } -bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace, - Increment::IncrementCase incrementCase) -{ +bool SurfaceInterceptorTest::singleIncrementFound(const Trace& trace, + Increment::IncrementCase incrementCase) { bool foundIncrement = false; - for (const auto& increment : *trace->mutable_increment()) { + for (const auto& increment : trace.increment()) { if (increment.increment_case() == incrementCase) { + int32_t targetId = 0; switch (incrementCase) { case Increment::IncrementCase::kSurfaceCreation: foundIncrement = surfaceCreationFound(increment, foundIncrement); break; case Increment::IncrementCase::kSurfaceDeletion: - foundIncrement = surfaceDeletionFound(increment, foundIncrement); + // Find the id of created surface. + targetId = getSurfaceId(trace, UNIQUE_LAYER_NAME); + foundIncrement = surfaceDeletionFound(increment, targetId, foundIncrement); break; case Increment::IncrementCase::kDisplayCreation: foundIncrement = displayCreationFound(increment, foundIncrement); break; case Increment::IncrementCase::kDisplayDeletion: - foundIncrement = displayDeletionFound(increment, foundIncrement); + // Find the id of created display. + targetId = getDisplayId(trace, DISPLAY_NAME.string()); + foundIncrement = displayDeletionFound(increment, targetId, foundIncrement); break; default: /* code */ @@ -716,9 +699,9 @@ bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace, return foundIncrement; } -bool SurfaceInterceptorTest::bufferUpdatesFound(Trace* trace) { +bool SurfaceInterceptorTest::bufferUpdatesFound(const Trace& trace) { uint32_t updates = 0; - for (const auto& inc : *trace->mutable_increment()) { + for (const auto& inc : trace.increment()) { if (inc.increment_case() == inc.kBufferUpdate && inc.buffer_update().id() == mBGLayerId) { updates++; } @@ -747,11 +730,6 @@ TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) { captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop); } -TEST_F(SurfaceInterceptorTest, InterceptFinalCropUpdateWorks) { - captureTest(&SurfaceInterceptorTest::finalCropUpdate, - SurfaceChange::SurfaceChangeCase::kFinalCrop); -} - TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); } @@ -792,14 +770,8 @@ TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) { } TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) { - enableInterceptor(); - runAllUpdates(); - disableInterceptor(); - - // Find all of the updates in the single trace - Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - assertAllUpdatesFound(&capturedTrace); + captureTest(&SurfaceInterceptorTest::runAllUpdates, + &SurfaceInterceptorTest::assertAllUpdatesFound); } TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) { @@ -808,16 +780,15 @@ TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) { } TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) { + enableInterceptor(); sp<SurfaceControl> layerToDelete = mComposerClient->createSurface(String8(LAYER_NAME), SIZE_UPDATE, SIZE_UPDATE, PIXEL_FORMAT_RGBA_8888, 0); - this->mTargetId = getSurfaceId(LAYER_NAME); - enableInterceptor(); mComposerClient->destroySurface(layerToDelete->getHandle()); disableInterceptor(); Trace capturedTrace; ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceDeletion)); + ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceDeletion)); } TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) { @@ -826,21 +797,24 @@ TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) { } TEST_F(SurfaceInterceptorTest, InterceptDisplayDeletionWorks) { - captureTest(&SurfaceInterceptorTest::displayDeletion, - Increment::IncrementCase::kDisplayDeletion); + enableInterceptor(); + runInTransaction(&SurfaceInterceptorTest::displayDeletion); + disableInterceptor(); + Trace capturedTrace; + ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); + ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kDisplayDeletion)); } TEST_F(SurfaceInterceptorTest, InterceptBufferUpdateWorks) { - nBufferUpdates(); - Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(bufferUpdatesFound(&capturedTrace)); + captureTest(&SurfaceInterceptorTest::nBufferUpdates, + &SurfaceInterceptorTest::bufferUpdatesFound); } // If the interceptor is enabled while buffer updates are being pushed, the interceptor should // first create a snapshot of the existing displays and surfaces and then start capturing // the buffer updates TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) { + setupBackgroundSurface(); std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this); enableInterceptor(); disableInterceptor(); @@ -854,6 +828,7 @@ TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) { TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) { enableInterceptor(); + setupBackgroundSurface(); std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this); std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this); runInTransaction(&SurfaceInterceptorTest::surfaceCreation); @@ -863,10 +838,11 @@ TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) { Trace capturedTrace; ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); + preProcessTrace(capturedTrace); - assertAllUpdatesFound(&capturedTrace); - ASSERT_TRUE(bufferUpdatesFound(&capturedTrace)); - ASSERT_TRUE(singleIncrementFound(&capturedTrace, Increment::IncrementCase::kSurfaceCreation)); + assertAllUpdatesFound(capturedTrace); + ASSERT_TRUE(bufferUpdatesFound(capturedTrace)); + ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation)); } } diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index 5108279043..c814142f49 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -30,6 +30,7 @@ #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> +#include <ui/ColorSpace.h> #include <ui/DisplayInfo.h> #include <ui/Rect.h> #include <utils/String8.h> @@ -62,38 +63,54 @@ const Color Color::WHITE{255, 255, 255, 255}; const Color Color::BLACK{0, 0, 0, 255}; const Color Color::TRANSPARENT{0, 0, 0, 0}; +using android::hardware::graphics::common::V1_1::BufferUsage; + std::ostream& operator<<(std::ostream& os, const Color& color) { os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a); return os; } // Fill a region with the specified color. -void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const Color& color) { - int32_t x = rect.left; - int32_t y = rect.top; - int32_t width = rect.right - rect.left; - int32_t height = rect.bottom - rect.top; - - if (x < 0) { - width += x; - x = 0; +void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, + const Color& color) { + Rect r(0, 0, buffer.width, buffer.height); + if (!r.intersect(rect, &r)) { + return; } - if (y < 0) { - height += y; - y = 0; - } - if (x + width > buffer.width) { - x = std::min(x, buffer.width); - width = buffer.width - x; + + int32_t width = r.right - r.left; + int32_t height = r.bottom - r.top; + + for (int32_t row = 0; row < height; row++) { + uint8_t* dst = + static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4; + for (int32_t column = 0; column < width; column++) { + dst[0] = color.r; + dst[1] = color.g; + dst[2] = color.b; + dst[3] = color.a; + dst += 4; + } } - if (y + height > buffer.height) { - y = std::min(y, buffer.height); - height = buffer.height - y; +} + +// Fill a region with the specified color. +void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) { + Rect r(0, 0, buffer->width, buffer->height); + if (!r.intersect(rect, &r)) { + return; } - for (int32_t j = 0; j < height; j++) { - uint8_t* dst = static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (y + j) + x) * 4; - for (int32_t i = 0; i < width; i++) { + int32_t width = r.right - r.left; + int32_t height = r.bottom - r.top; + + uint8_t* pixels; + buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + for (int32_t row = 0; row < height; row++) { + uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4; + for (int32_t column = 0; column < width; column++) { dst[0] = color.r; dst[1] = color.g; dst[2] = color.b; @@ -101,6 +118,7 @@ void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const dst += 4; } } + buffer->unlock(); } // Check if a region has the specified color. @@ -169,17 +187,15 @@ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, // individual pixel values for testing purposes. class ScreenCapture : public RefBase { public: - static void captureScreen(sp<ScreenCapture>* sc, int32_t minLayerZ = 0, - int32_t maxLayerZ = std::numeric_limits<int32_t>::max()) { + static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); SurfaceComposerClient::Transaction().apply(true); sp<GraphicBuffer> outBuffer; ASSERT_EQ(NO_ERROR, - sf->captureScreen(display, &outBuffer, Rect(), 0, 0, minLayerZ, maxLayerZ, - false)); - *sc = new ScreenCapture(outBuffer); + sf->captureScreen(display, &outBuffer, Rect(), 0, 0, false)); + *sc = std::make_unique<ScreenCapture>(outBuffer); } static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, @@ -299,10 +315,20 @@ protected: ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sp<IBinder> binder = sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + mColorManagementUsed = sf->isColorManagementUsed(); } - sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0) { + virtual void TearDown() { + mBlackBgSurface = 0; + mClient->dispose(); + mClient = 0; + } + + virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0) { auto layer = mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags); EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; @@ -319,7 +345,7 @@ protected: return layer; } - ANativeWindow_Buffer getLayerBuffer(const sp<SurfaceControl>& layer) { + ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { // wait for previous transactions (such as setSize) to complete Transaction().apply(true); @@ -329,40 +355,108 @@ protected: return buffer; } - void postLayerBuffer(const sp<SurfaceControl>& layer) { + void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); // wait for the newly posted buffer to be latched waitForLayerBuffers(); } - void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color) { + virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer)); - fillBufferColor(buffer, Rect(0, 0, buffer.width, buffer.height), color); - postLayerBuffer(layer); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); + postBufferQueueLayerBuffer(layer); + } + + virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); + Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply(); + } + + void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + switch (mLayerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight); + break; + default: + ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; + } } - void fillLayerQuadrant(const sp<SurfaceControl>& layer, const Color& topLeft, + void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer, + int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft, const Color& topRight, const Color& bottomLeft, const Color& bottomRight) { + switch (mLayerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, + bottomLeft, bottomRight); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, + bottomLeft, bottomRight); + break; + default: + ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; + } + } + + virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, + const Color& topRight, const Color& bottomLeft, + const Color& bottomRight) { ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer)); - ASSERT_TRUE(buffer.width % 2 == 0 && buffer.height % 2 == 0); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); + + const int32_t halfW = bufferWidth / 2; + const int32_t halfH = bufferHeight / 2; + fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); + fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight); + fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft); + fillANativeWindowBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), + bottomRight); + + postBufferQueueLayerBuffer(layer); + } + + virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, + const Color& topRight, const Color& bottomLeft, + const Color& bottomRight) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); - const int32_t halfW = buffer.width / 2; - const int32_t halfH = buffer.height / 2; - fillBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); - fillBufferColor(buffer, Rect(halfW, 0, buffer.width, halfH), topRight); - fillBufferColor(buffer, Rect(0, halfH, halfW, buffer.height), bottomLeft); - fillBufferColor(buffer, Rect(halfW, halfH, buffer.width, buffer.height), bottomRight); + ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); - postLayerBuffer(layer); + const int32_t halfW = bufferWidth / 2; + const int32_t halfH = bufferHeight / 2; + fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); + fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight); + fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft); + fillGraphicBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight); + + Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply(); } - sp<ScreenCapture> screenshot() { - sp<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot, mLayerZBase); + std::unique_ptr<ScreenCapture> screenshot() { + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); return screenshot; } @@ -376,6 +470,13 @@ protected: // leave room for ~256 layers const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256; + void setPositionWithResizeHelper(uint32_t layerType); + void setSizeBasicHelper(uint32_t layerType); + void setMatrixWithResizeHelper(uint32_t layerType); + + sp<SurfaceControl> mBlackBgSurface; + bool mColorManagementUsed; + private: void SetUpDisplay() { mDisplay = mClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); @@ -393,21 +494,72 @@ private: mBufferPostDelay = int32_t(1e6 / info.fps) * 3; mDisplayLayerStack = 0; + + mBlackBgSurface = mClient->createSurface(String8("BaseSurface"), mDisplayWidth, + mDisplayHeight, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + // set layer stack (b/68888219) Transaction t; t.setDisplayLayerStack(mDisplay, mDisplayLayerStack); + t.setLayerStack(mBlackBgSurface, mDisplayLayerStack); + t.setColor(mBlackBgSurface, half3{0, 0, 0}); + t.setLayer(mBlackBgSurface, mLayerZBase); t.apply(); } - void waitForLayerBuffers() { usleep(mBufferPostDelay); } + void waitForLayerBuffers() { + // Request an empty transaction to get applied synchronously to ensure the buffer is + // latched. + Transaction().apply(true); + usleep(mBufferPostDelay); + } int32_t mBufferPostDelay; }; -TEST_F(LayerTransactionTest, SetPositionBasic) { +class LayerTypeTransactionTest : public LayerTransactionTest, + public ::testing::WithParamInterface<uint32_t> { +public: + LayerTypeTransactionTest() { mLayerType = GetParam(); } + + sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0) override { + // if the flags already have a layer type specified, return an error + if (flags & ISurfaceComposerClient::eFXSurfaceMask) { + return nullptr; + } + return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType); + } + + void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth, + int32_t bufferHeight) { + ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color, + bufferWidth, bufferHeight)); + } + + void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, const Color& topRight, + const Color& bottomLeft, const Color& bottomRight) { + ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer, + bufferWidth, bufferHeight, + topLeft, topRight, + bottomLeft, bottomRight)); + } + +protected: + uint32_t mLayerType; +}; + +INSTANTIATE_TEST_CASE_P( + LayerTypeTransactionTests, LayerTypeTransactionTest, + ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), + static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState))); + +TEST_P(LayerTypeTransactionTest, SetPositionBasic) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); { SCOPED_TRACE("default position"); @@ -425,10 +577,10 @@ TEST_F(LayerTransactionTest, SetPositionBasic) { } } -TEST_F(LayerTransactionTest, SetPositionRounding) { +TEST_P(LayerTypeTransactionTest, SetPositionRounding) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); // GLES requires only 4 bits of subpixel precision during rasterization // XXX GLES composition does not match HWC composition due to precision @@ -447,10 +599,10 @@ TEST_F(LayerTransactionTest, SetPositionRounding) { } } -TEST_F(LayerTransactionTest, SetPositionOutOfBounds) { +TEST_P(LayerTypeTransactionTest, SetPositionOutOfBounds) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); Transaction().setPosition(layer, -32, -32).apply(); { @@ -465,10 +617,10 @@ TEST_F(LayerTransactionTest, SetPositionOutOfBounds) { } } -TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) { +TEST_P(LayerTypeTransactionTest, SetPositionPartiallyOutOfBounds) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); // partially out of bounds Transaction().setPosition(layer, -30, -30).apply(); @@ -486,10 +638,10 @@ TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) { } } -TEST_F(LayerTransactionTest, SetPositionWithResize) { +void LayerTransactionTest::setPositionWithResizeHelper(uint32_t layerType) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 32, 32)); // setPosition is applied immediately by default, with or without resize // pending @@ -497,21 +649,43 @@ TEST_F(LayerTransactionTest, SetPositionWithResize) { { SCOPED_TRACE("resize pending"); auto shot = screenshot(); - shot->expectColor(Rect(5, 10, 37, 42), Color::RED); - shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK); + Rect rect; + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + rect = {5, 10, 37, 42}; + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + rect = {5, 10, 69, 74}; + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 64, 64)); { SCOPED_TRACE("resize applied"); screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED); } } -TEST_F(LayerTransactionTest, SetPositionWithNextResize) { +TEST_F(LayerTransactionTest, SetPositionWithResize_BufferQueue) { + ASSERT_NO_FATAL_FAILURE( + setPositionWithResizeHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_F(LayerTransactionTest, SetPositionWithResize_BufferState) { + ASSERT_NO_FATAL_FAILURE( + setPositionWithResizeHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_F(LayerTransactionTest, SetPositionWithNextResize_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); // request setPosition to be applied with the next resize Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply(); @@ -533,17 +707,17 @@ TEST_F(LayerTransactionTest, SetPositionWithNextResize) { } // finally resize and latch the buffer - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); { SCOPED_TRACE("new position applied"); screenshot()->expectColor(Rect(15, 20, 79, 84), Color::RED); } } -TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) { +TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); // setPosition is not immediate even with SCALE_TO_WINDOW override Transaction() @@ -557,27 +731,38 @@ TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) { screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); { SCOPED_TRACE("new position applied"); screenshot()->expectColor(Rect(5, 10, 69, 74), Color::RED); } } -TEST_F(LayerTransactionTest, SetSizeBasic) { +void LayerTransactionTest::setSizeBasicHelper(uint32_t layerType) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 32, 32)); Transaction().setSize(layer, 64, 64).apply(); { SCOPED_TRACE("resize pending"); auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + Rect rect; + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + rect = {0, 0, 32, 32}; + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + rect = {0, 0, 64, 64}; + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 64, 64)); { SCOPED_TRACE("resize applied"); auto shot = screenshot(); @@ -586,14 +771,22 @@ TEST_F(LayerTransactionTest, SetSizeBasic) { } } -TEST_F(LayerTransactionTest, SetSizeInvalid) { +TEST_F(LayerTransactionTest, SetSizeBasic_BufferQueue) { + setSizeBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue); +} + +TEST_F(LayerTransactionTest, SetSizeBasic_BufferState) { + setSizeBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState); +} + +TEST_P(LayerTypeTransactionTest, SetSizeInvalid) { // cannot test robustness against invalid sizes (zero or really huge) } -TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) { +TEST_P(LayerTypeTransactionTest, SetSizeWithScaleToWindow) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition Transaction() @@ -603,13 +796,13 @@ TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) { screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED); } -TEST_F(LayerTransactionTest, SetZBasic) { +TEST_P(LayerTypeTransactionTest, SetZBasic) { sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); Transaction().setLayer(layerR, mLayerZBase + 1).apply(); { @@ -624,38 +817,43 @@ TEST_F(LayerTransactionTest, SetZBasic) { } } -TEST_F(LayerTransactionTest, SetZNegative) { +TEST_P(LayerTypeTransactionTest, SetZNegative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight, + ISurfaceComposerClient::eFXSurfaceContainer); sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + Transaction() + .reparent(layerR, parent->getHandle()) + .reparent(layerG, parent->getHandle()) + .apply(); Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply(); { SCOPED_TRACE("layerR"); - sp<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot, -2, -1); - screenshot->expectColor(Rect(0, 0, 32, 32), Color::RED); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); } Transaction().setLayer(layerR, -3).apply(); { SCOPED_TRACE("layerG"); - sp<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot, -3, -1); - screenshot->expectColor(Rect(0, 0, 32, 32), Color::GREEN); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN); } } -TEST_F(LayerTransactionTest, SetRelativeZBasic) { +TEST_P(LayerTypeTransactionTest, SetRelativeZBasic) { sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); Transaction() .setPosition(layerG, 16, 16) @@ -677,36 +875,44 @@ TEST_F(LayerTransactionTest, SetRelativeZBasic) { } } -TEST_F(LayerTransactionTest, SetRelativeZNegative) { +TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight, + ISurfaceComposerClient::eFXSurfaceContainer); sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; sp<SurfaceControl> layerB; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); + + Transaction() + .reparent(layerB, parent->getHandle()) + .apply(); // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2 Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply(); - sp<ScreenCapture> screenshot; + std::unique_ptr<ScreenCapture> screenshot; // only layerB is in this range - ScreenCapture::captureScreen(&screenshot, -2, -1); + sp<IBinder> parentHandle = parent->getHandle(); + ScreenCapture::captureLayers(&screenshot, parentHandle); screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); } -TEST_F(LayerTransactionTest, SetRelativeZGroup) { +TEST_P(LayerTypeTransactionTest, SetRelativeZGroup) { sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; sp<SurfaceControl> layerB; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); // layerR = 0, layerG = layerR + 3, layerB = 2 Transaction() @@ -764,14 +970,14 @@ TEST_F(LayerTransactionTest, SetRelativeZGroup) { } } -TEST_F(LayerTransactionTest, SetRelativeZBug64572777) { +TEST_P(LayerTypeTransactionTest, SetRelativeZBug64572777) { sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); Transaction() .setPosition(layerG, 16, 16) @@ -783,10 +989,10 @@ TEST_F(LayerTransactionTest, SetRelativeZBug64572777) { screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); } -TEST_F(LayerTransactionTest, SetFlagsHidden) { +TEST_P(LayerTypeTransactionTest, SetFlagsHidden) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply(); { @@ -801,14 +1007,14 @@ TEST_F(LayerTransactionTest, SetFlagsHidden) { } } -TEST_F(LayerTransactionTest, SetFlagsOpaque) { +TEST_P(LayerTypeTransactionTest, SetFlagsOpaque) { const Color translucentRed = {100, 0, 0, 100}; sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); Transaction() .setLayer(layerR, mLayerZBase + 1) @@ -827,10 +1033,10 @@ TEST_F(LayerTransactionTest, SetFlagsOpaque) { } } -TEST_F(LayerTransactionTest, SetFlagsSecure) { +TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); sp<ISurfaceComposer> composer = ComposerService::getComposerService(); sp<GraphicBuffer> outBuffer; @@ -838,28 +1044,26 @@ TEST_F(LayerTransactionTest, SetFlagsSecure) { .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) .apply(true); ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase, - false)); + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase, - false)); + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); } -TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { +TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferQueue) { const Rect top(0, 0, 32, 16); const Rect bottom(0, 16, 32, 32); sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer)); - ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::RED)); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::RED)); // setTransparentRegionHint always applies to the following buffer Transaction().setTransparentRegionHint(layer, Region(top)).apply(); - ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); { SCOPED_TRACE("top transparent"); auto shot = screenshot(); @@ -875,10 +1079,61 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { shot->expectColor(bottom, Color::RED); } - ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer)); - ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, top, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillBufferColor(buffer, bottom, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(postLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); + { + SCOPED_TRACE("bottom transparent"); + auto shot = screenshot(); + shot->expectColor(top, Color::RED); + shot->expectColor(bottom, Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic_BufferState) { + const Rect top(0, 0, 32, 16); + const Rect bottom(0, 16, 32, 32); + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + + ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::RED)); + Transaction() + .setTransparentRegionHint(layer, Region(top)) + .setBuffer(layer, buffer) + .setSize(layer, 32, 32) + .apply(); + { + SCOPED_TRACE("top transparent"); + auto shot = screenshot(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::RED); + } + + Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); + { + SCOPED_TRACE("transparent region hint intermediate"); + auto shot = screenshot(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::BLACK); + } + + buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + + ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT)); + Transaction().setBuffer(layer, buffer).setSize(layer, 32, 32).apply(); { SCOPED_TRACE("bottom transparent"); auto shot = screenshot(); @@ -887,7 +1142,7 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { } } -TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) { +TEST_P(LayerTypeTransactionTest, SetTransparentRegionHintOutOfBounds) { sp<SurfaceControl> layerTransparent; sp<SurfaceControl> layerR; ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); @@ -900,18 +1155,18 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) { .setPosition(layerR, 16, 16) .setLayer(layerR, mLayerZBase + 1) .apply(); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerTransparent, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED); } -TEST_F(LayerTransactionTest, SetAlphaBasic) { +TEST_P(LayerTypeTransactionTest, SetAlphaBasic) { sp<SurfaceControl> layer1; sp<SurfaceControl> layer2; ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32)); ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer1, {64, 0, 0, 255})); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer2, {0, 64, 0, 255})); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer1, {64, 0, 0, 255}, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer2, {0, 64, 0, 255}, 32, 32)); Transaction() .setAlpha(layer1, 0.25f) @@ -931,11 +1186,11 @@ TEST_F(LayerTransactionTest, SetAlphaBasic) { } } -TEST_F(LayerTransactionTest, SetAlphaClamped) { +TEST_P(LayerTypeTransactionTest, SetAlphaClamped) { const Color color = {64, 0, 0, 255}; sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32)); Transaction().setAlpha(layer, 2.0f).apply(); { @@ -954,7 +1209,7 @@ TEST_F(LayerTransactionTest, SetColorBasic) { sp<SurfaceControl> bufferLayer; sp<SurfaceControl> colorLayer; ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE( colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor)); @@ -989,7 +1244,7 @@ TEST_F(LayerTransactionTest, SetColorWithAlpha) { sp<SurfaceControl> bufferLayer; sp<SurfaceControl> colorLayer; ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE( colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor)); @@ -1014,7 +1269,7 @@ TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) { sp<SurfaceControl> colorLayer; ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer( "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor)); @@ -1034,20 +1289,20 @@ TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) { tolerance); } -TEST_F(LayerTransactionTest, SetColorWithBuffer) { +TEST_P(LayerTypeTransactionTest, SetColorWithBuffer) { sp<SurfaceControl> bufferLayer; ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32)); // color is ignored Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply(); screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); } -TEST_F(LayerTransactionTest, SetLayerStackBasic) { +TEST_P(LayerTypeTransactionTest, SetLayerStackBasic) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply(); { @@ -1062,11 +1317,11 @@ TEST_F(LayerTransactionTest, SetLayerStackBasic) { } } -TEST_F(LayerTransactionTest, SetMatrixBasic) { +TEST_P(LayerTypeTransactionTest, SetMatrixBasic) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE( - fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); + fillLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply(); { @@ -1104,11 +1359,11 @@ TEST_F(LayerTransactionTest, SetMatrixBasic) { } } -TEST_F(LayerTransactionTest, SetMatrixRot45) { +TEST_P(LayerTypeTransactionTest, SetMatrixRot45) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE( - fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); + fillLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); const float rot = M_SQRT1_2; // 45 degrees const float trans = M_SQRT2 * 16.0f; @@ -1127,31 +1382,52 @@ TEST_F(LayerTransactionTest, SetMatrixRot45) { shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE); } -TEST_F(LayerTransactionTest, SetMatrixWithResize) { +void LayerTransactionTest::setMatrixWithResizeHelper(uint32_t layerType) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 32, 32)); // setMatrix is applied after any pending resize, unlike setPosition Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply(); { SCOPED_TRACE("resize pending"); auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + Rect rect; + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + rect = {0, 0, 32, 32}; + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + rect = {0, 0, 128, 128}; + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer, Color::RED, 64, 64)); { SCOPED_TRACE("resize applied"); screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED); } } -TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) { +TEST_F(LayerTransactionTest, SetMatrixWithResize_BufferQueue) { + ASSERT_NO_FATAL_FAILURE( + setMatrixWithResizeHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_F(LayerTransactionTest, SetMatrixWithResize_BufferState) { + ASSERT_NO_FATAL_FAILURE( + setMatrixWithResizeHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerTypeTransactionTest, SetMatrixWithScaleToWindow) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition Transaction() @@ -1162,11 +1438,11 @@ TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) { screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED); } -TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) { +TEST_P(LayerTypeTransactionTest, SetOverrideScalingModeBasic) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE( - fillLayerQuadrant(layer, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); + fillLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, Color::BLUE, Color::WHITE)); // XXX SCALE_CROP is not respected; calling setSize and // setOverrideScalingMode in separate transactions does not work @@ -1182,10 +1458,36 @@ TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) { } } -TEST_F(LayerTransactionTest, SetCropBasic) { +TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + + sp<IBinder> handle = layer->getHandle(); + ASSERT_TRUE(handle != nullptr); + + FrameStats frameStats; + mClient->getLayerFrameStats(handle, &frameStats); + + ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0)); +} + +TEST_F(LayerTransactionTest, SetCropBasic_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + const Rect crop(8, 8, 24, 24); + + Transaction().setCrop_legacy(layer, crop).apply(); + auto shot = screenshot(); + shot->expectColor(crop, Color::RED); + shot->expectBorder(crop, Color::BLACK); +} + +TEST_F(LayerTransactionTest, SetCropBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); const Rect crop(8, 8, 24, 24); Transaction().setCrop(layer, crop).apply(); @@ -1194,10 +1496,29 @@ TEST_F(LayerTransactionTest, SetCropBasic) { shot->expectBorder(crop, Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropEmpty) { +TEST_F(LayerTransactionTest, SetCropEmpty_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("empty rect"); + Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply(); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + { + SCOPED_TRACE("negative rect"); + Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply(); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } +} + +TEST_F(LayerTransactionTest, SetCropEmpty_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); { SCOPED_TRACE("empty rect"); @@ -1212,10 +1533,22 @@ TEST_F(LayerTransactionTest, SetCropEmpty) { } } -TEST_F(LayerTransactionTest, SetCropOutOfBounds) { +TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply(); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); Transaction().setCrop(layer, Rect(-128, -64, 128, 64)).apply(); auto shot = screenshot(); @@ -1223,10 +1556,24 @@ TEST_F(LayerTransactionTest, SetCropOutOfBounds) { shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithTranslation) { +TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + const Point position(32, 32); + const Rect crop(8, 8, 24, 24); + Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply(); + auto shot = screenshot(); + shot->expectColor(crop + position, Color::RED); + shot->expectBorder(crop + position, Color::BLACK); +} + +TEST_F(LayerTransactionTest, SetCropWithTranslation_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); const Point position(32, 32); const Rect crop(8, 8, 24, 24); @@ -1236,10 +1583,26 @@ TEST_F(LayerTransactionTest, SetCropWithTranslation) { shot->expectBorder(crop + position, Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithScale) { +TEST_F(LayerTransactionTest, SetCropWithScale_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // crop is affected by matrix + Transaction() + .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) + .setCrop_legacy(layer, Rect(8, 8, 24, 24)) + .apply(); + auto shot = screenshot(); + shot->expectColor(Rect(16, 16, 48, 48), Color::RED); + shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); +} + +TEST_F(LayerTransactionTest, SetCropWithScale_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); // crop is affected by matrix Transaction() @@ -1251,13 +1614,13 @@ TEST_F(LayerTransactionTest, SetCropWithScale) { shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithResize) { +TEST_F(LayerTransactionTest, SetCropWithResize_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - // setCrop is applied immediately by default, with or without resize pending - Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); + // setCrop_legacy is applied immediately by default, with or without resize pending + Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); { SCOPED_TRACE("resize pending"); auto shot = screenshot(); @@ -1265,7 +1628,7 @@ TEST_F(LayerTransactionTest, SetCropWithResize) { shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); { SCOPED_TRACE("resize applied"); auto shot = screenshot(); @@ -1274,19 +1637,46 @@ TEST_F(LayerTransactionTest, SetCropWithResize) { } } -TEST_F(LayerTransactionTest, SetCropWithNextResize) { +TEST_F(LayerTransactionTest, SetCropWithResize_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + // setCrop_legacy is applied immediately by default, with or without resize pending + Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); + { + SCOPED_TRACE("new buffer pending"); + auto shot = screenshot(); + shot->expectColor(Rect(8, 8, 16, 16), Color::RED); + shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 16, 16)); + { + SCOPED_TRACE("new buffer"); + auto shot = screenshot(); + shot->expectColor(Rect(8, 8, 16, 16), Color::RED); + shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, SetCropWithNextResize_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - // request setCrop to be applied with the next resize - Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setGeometryAppliesWithResize(layer).apply(); + // request setCrop_legacy to be applied with the next resize + Transaction() + .setCrop_legacy(layer, Rect(8, 8, 24, 24)) + .setGeometryAppliesWithResize(layer) + .apply(); { SCOPED_TRACE("waiting for next resize"); screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); } - Transaction().setCrop(layer, Rect(4, 4, 12, 12)).apply(); + Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply(); { SCOPED_TRACE("pending crop modified"); screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); @@ -1299,7 +1689,7 @@ TEST_F(LayerTransactionTest, SetCropWithNextResize) { } // finally resize - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); { SCOPED_TRACE("new crop applied"); auto shot = screenshot(); @@ -1308,14 +1698,49 @@ TEST_F(LayerTransactionTest, SetCropWithNextResize) { } } -TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { +TEST_F(LayerTransactionTest, SetCropWithNextResize_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + // request setCrop_legacy to be applied with the next resize + Transaction().setCrop(layer, Rect(8, 8, 24, 24)).setGeometryAppliesWithResize(layer).apply(); + { + SCOPED_TRACE("set crop 1"); + screenshot()->expectColor(Rect(8, 8, 24, 24), Color::RED); + } + + Transaction().setCrop(layer, Rect(4, 4, 12, 12)).apply(); + { + SCOPED_TRACE("set crop 2"); + screenshot()->expectColor(Rect(4, 4, 12, 12), Color::RED); + } + + Transaction().setSize(layer, 16, 16).apply(); + { + SCOPED_TRACE("resize"); + screenshot()->expectColor(Rect(4, 4, 12, 12), Color::RED); + } + + // finally resize + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 16, 16)); + { + SCOPED_TRACE("new buffer"); + auto shot = screenshot(); + shot->expectColor(Rect(4, 4, 12, 12), Color::RED); + shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - // setCrop is not immediate even with SCALE_TO_WINDOW override + // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override Transaction() - .setCrop(layer, Rect(4, 4, 12, 12)) + .setCrop_legacy(layer, Rect(4, 4, 12, 12)) .setSize(layer, 16, 16) .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) .setGeometryAppliesWithResize(layer) @@ -1329,7 +1754,7 @@ TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { // XXX crop is never latched without other geometry change (b/69315677) Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply(); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); Transaction().setPosition(layer, 0, 0).apply(); { SCOPED_TRACE("new crop applied"); @@ -1339,173 +1764,403 @@ TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { } } -TEST_F(LayerTransactionTest, SetFinalCropBasic) { +TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); - const Rect crop(8, 8, 24, 24); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + // all properties are applied immediate so setGeometryAppliesWithResize has no effect + Transaction() + .setCrop(layer, Rect(4, 4, 12, 12)) + .setSize(layer, 16, 16) + .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) + .setGeometryAppliesWithResize(layer) + .apply(); + { + SCOPED_TRACE("new crop pending"); + auto shot = screenshot(); + shot->expectColor(Rect(4, 4, 12, 12), Color::RED); + shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); + } + + Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply(); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 16, 16)); + Transaction().setPosition(layer, 0, 0).apply(); + { + SCOPED_TRACE("new crop applied"); + auto shot = screenshot(); + shot->expectColor(Rect(4, 4, 12, 12), Color::RED); + shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, SetBufferBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - // same as in SetCropBasic - Transaction().setFinalCrop(layer, crop).apply(); auto shot = screenshot(); - shot->expectColor(crop, Color::RED); - shot->expectBorder(crop, Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropEmpty) { +TEST_F(LayerTransactionTest, SetBufferMultipleBuffers_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - // same as in SetCropEmpty { - SCOPED_TRACE("empty rect"); - Transaction().setFinalCrop(layer, Rect(8, 8, 8, 8)).apply(); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + SCOPED_TRACE("set buffer 1"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + { - SCOPED_TRACE("negative rect"); - Transaction().setFinalCrop(layer, Rect(8, 8, 0, 0)).apply(); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + SCOPED_TRACE("set buffer 2"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("set buffer 3"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, SetBufferMultipleLayers_BufferState) { + sp<SurfaceControl> layer1; + ASSERT_NO_FATAL_FAILURE( + layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<SurfaceControl> layer2; + ASSERT_NO_FATAL_FAILURE( + layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64)); + + { + SCOPED_TRACE("set layer 1 buffer red"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 64, 64), Color::RED); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32)); + + { + SCOPED_TRACE("set layer 2 buffer blue"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectColor(Rect(0, 32, 64, 64), Color::RED); + shot->expectColor(Rect(0, 32, 32, 64), Color::RED); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64)); + { + SCOPED_TRACE("set layer 1 buffer green"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); + shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32)); + + { + SCOPED_TRACE("set layer 2 buffer white"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE); + shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); + shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); } } -TEST_F(LayerTransactionTest, SetFinalCropOutOfBounds) { +TEST_F(LayerTransactionTest, SetTransformRotate90_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction().setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90).apply(); + + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE, + Color::GREEN, true /* filtered */); +} + +TEST_F(LayerTransactionTest, SetTransformFlipH_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction().setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H).apply(); + + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE, + Color::BLUE, true /* filtered */); +} + +TEST_F(LayerTransactionTest, SetTransformFlipV_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction().setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V).apply(); + + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED, + Color::GREEN, true /* filtered */); +} + +TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction().setTransformToDisplayInverse(layer, false).apply(); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32)); + + Transaction().setTransformToDisplayInverse(layer, true).apply(); +} + +TEST_F(LayerTransactionTest, SetFenceBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + sp<Fence> fence = new Fence(-1); + + Transaction() + .setBuffer(layer, buffer) + .setAcquireFence(layer, fence) + .setSize(layer, 32, 32) + .apply(); - // same as in SetCropOutOfBounds - Transaction().setFinalCrop(layer, Rect(-128, -64, 128, 64)).apply(); auto shot = screenshot(); shot->expectColor(Rect(0, 0, 32, 32), Color::RED); shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithTranslation) { +TEST_F(LayerTransactionTest, SetDataspaceBasic_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + Transaction() + .setBuffer(layer, buffer) + .setDataspace(layer, ui::Dataspace::UNKNOWN) + .setSize(layer, 32, 32) + .apply(); - // final crop is applied post-translation - Transaction().setPosition(layer, 16, 16).setFinalCrop(layer, Rect(8, 8, 24, 24)).apply(); auto shot = screenshot(); - shot->expectColor(Rect(16, 16, 24, 24), Color::RED); - shot->expectBorder(Rect(16, 16, 24, 24), Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithScale) { +TEST_F(LayerTransactionTest, SetHdrMetadataBasic_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - // final crop is not affected by matrix + HdrMetadata hdrMetadata; + hdrMetadata.validTypes = 0; Transaction() - .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) - .setFinalCrop(layer, Rect(8, 8, 24, 24)) + .setBuffer(layer, buffer) + .setHdrMetadata(layer, hdrMetadata) + .setSize(layer, 32, 32) .apply(); + auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 24, 24), Color::RED); - shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithResize) { +TEST_F(LayerTransactionTest, SetSurfaceDamageRegionBasic_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - // same as in SetCropWithResize - Transaction().setFinalCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); - { - SCOPED_TRACE("resize pending"); - auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 24, 24), Color::RED); - shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); - } + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); - { - SCOPED_TRACE("resize applied"); - auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 16, 16), Color::RED); - shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); - } + Region region; + region.set(32, 32); + Transaction() + .setBuffer(layer, buffer) + .setSurfaceDamageRegion(layer, region) + .setSize(layer, 32, 32) + .apply(); + + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithNextResize) { +TEST_F(LayerTransactionTest, SetApiBasic_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - // same as in SetCropWithNextResize Transaction() - .setFinalCrop(layer, Rect(8, 8, 24, 24)) - .setGeometryAppliesWithResize(layer) + .setBuffer(layer, buffer) + .setApi(layer, NATIVE_WINDOW_API_CPU) + .setSize(layer, 32, 32) .apply(); - { - SCOPED_TRACE("waiting for next resize"); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + // verify this doesn't cause a crash + Transaction().setSidebandStream(layer, nullptr).apply(); +} + +class ColorTransformHelper { +public: + static void DegammaColorSingle(half& s) { + if (s <= 0.03928f) + s = s / 12.92f; + else + s = pow((s + 0.055f) / 1.055f, 2.4f); } - Transaction().setFinalCrop(layer, Rect(4, 4, 12, 12)).apply(); - { - SCOPED_TRACE("pending final crop modified"); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + static void DegammaColor(half3& color) { + DegammaColorSingle(color.r); + DegammaColorSingle(color.g); + DegammaColorSingle(color.b); } - Transaction().setSize(layer, 16, 16).apply(); - { - SCOPED_TRACE("resize pending"); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + static void GammaColorSingle(half& s) { + if (s <= 0.0031308f) { + s = s * 12.92f; + } else { + s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f; + } } - // finally resize - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); - { - SCOPED_TRACE("new final crop applied"); - auto shot = screenshot(); - shot->expectColor(Rect(4, 4, 12, 12), Color::RED); - shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); + static void GammaColor(half3& color) { + GammaColorSingle(color.r); + GammaColorSingle(color.g); + GammaColorSingle(color.b); } -} -TEST_F(LayerTransactionTest, SetFinalCropWithNextResizeScaleToWindow) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + static void applyMatrix(half3& color, const mat3& mat) { + half3 ret = half3(0); - // same as in SetCropWithNextResizeScaleToWindow - Transaction() - .setFinalCrop(layer, Rect(4, 4, 12, 12)) - .setSize(layer, 16, 16) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .setGeometryAppliesWithResize(layer) - .apply(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + ret[i] = ret[i] + color[j] * mat[j][i]; + } + } + color = ret; + } +}; + +TEST_F(LayerTransactionTest, SetColorTransformBasic) { + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE( + colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor)); + + Transaction().setLayer(colorLayer, mLayerZBase + 1).apply(); { - SCOPED_TRACE("new final crop pending"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 16, 16), Color::RED); - shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK); + SCOPED_TRACE("default color"); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); } - // XXX final crop is never latched without other geometry change (b/69315677) - Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply(); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); - Transaction().setPosition(layer, 0, 0).apply(); + const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); + half3 expected = color; + mat3 matrix; + matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11; + matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11; + matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11; + + // degamma before applying the matrix + if (mColorManagementUsed) { + ColorTransformHelper::DegammaColor(expected); + } + + ColorTransformHelper::applyMatrix(expected, matrix); + + if (mColorManagementUsed) { + ColorTransformHelper::GammaColor(expected); + } + + const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), + uint8_t(expected.b * 255), 255}; + + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + + Transaction().setColor(colorLayer, color) + .setColorTransform(colorLayer, matrix, vec3()).apply(); { - SCOPED_TRACE("new final crop applied"); - auto shot = screenshot(); - shot->expectColor(Rect(4, 4, 12, 12), Color::RED); - shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); + SCOPED_TRACE("new color"); + screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); } } class LayerUpdateTest : public LayerTransactionTest { protected: virtual void SetUp() { - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); sp<IBinder> display( SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); @@ -1516,24 +2171,22 @@ protected: ssize_t displayHeight = info.h; // Background surface - mBGSurfaceControl = - mComposerClient->createSurface(String8("BG Test Surface"), displayWidth, - displayHeight, PIXEL_FORMAT_RGBA_8888, 0); + mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, + displayHeight, 0); ASSERT_TRUE(mBGSurfaceControl != nullptr); ASSERT_TRUE(mBGSurfaceControl->isValid()); fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); // Foreground surface - mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, - PIXEL_FORMAT_RGBA_8888, 0); + mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); ASSERT_TRUE(mFGSurfaceControl->isValid()); fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); // Synchronization surface - mSyncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1, - PIXEL_FORMAT_RGBA_8888, 0); + mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0); ASSERT_TRUE(mSyncSurfaceControl != nullptr); ASSERT_TRUE(mSyncSurfaceControl->isValid()); @@ -1555,11 +2208,10 @@ protected: } virtual void TearDown() { - mComposerClient->dispose(); + LayerTransactionTest::TearDown(); mBGSurfaceControl = 0; mFGSurfaceControl = 0; mSyncSurfaceControl = 0; - mComposerClient = 0; } void waitForPostedBuffers() { @@ -1578,7 +2230,6 @@ protected: t.apply(true); } - sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mBGSurfaceControl; sp<SurfaceControl> mFGSurfaceControl; @@ -1588,10 +2239,10 @@ protected: }; TEST_F(LayerUpdateTest, RelativesAreNotDetached) { - sp<ScreenCapture> sc; - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("relativeTestSurface"), 10, - 10, PIXEL_FORMAT_RGBA_8888, 0); + std::unique_ptr<ScreenCapture> sc; + + sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0); fillSurfaceRGBA8(relative, 10, 10, 10); waitForPostedBuffers(); @@ -1649,13 +2300,12 @@ protected: asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 64, 64); t.setPosition(mFGSurfaceControl, 64, 64); - t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64)); - t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1)); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); }); EXPECT_INITIAL_STATE("After restoring initial state"); } - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; }; class CropLatchingTest : public GeometryLatchingTest { @@ -1679,45 +2329,8 @@ protected: } }; -// In this test we ensure that setGeometryAppliesWithResize actually demands -// a buffer of the new size, and not just any size. -TEST_F(CropLatchingTest, FinalCropLatchingBufferOldSize) { - EXPECT_INITIAL_STATE("before anything"); - // Normally the crop applies immediately even while a resize is pending. - asTransaction([&](Transaction& t) { - t.setSize(mFGSurfaceControl, 128, 128); - t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - }); - - EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)"); - - restoreInitialState(); - - // In order to prepare to submit a buffer at the wrong size, we acquire it prior to - // initiating the resize. - lockAndFillFGBuffer(); - - asTransaction([&](Transaction& t) { - t.setSize(mFGSurfaceControl, 128, 128); - t.setGeometryAppliesWithResize(mFGSurfaceControl); - t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - }); - - EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)"); - - // We now submit our old buffer, at the old size, and ensure it doesn't - // trigger geometry latching. - unlockFGBuffer(); - - EXPECT_INITIAL_STATE("after unlocking FG buffer (with geometryAppliesWithResize)"); - - completeFGResize(); - - EXPECT_CROPPED_STATE("after the resize finishes"); -} - TEST_F(LayerUpdateTest, DeferredTransactionTest) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; { SCOPED_TRACE("before anything"); ScreenCapture::captureScreen(&sc); @@ -1729,14 +2342,14 @@ TEST_F(LayerUpdateTest, DeferredTransactionTest) { // set up two deferred transactions on different frames asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.75); - t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber()); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber()); }); asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 128, 128); - t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); }); { @@ -1772,13 +2385,13 @@ TEST_F(LayerUpdateTest, DeferredTransactionTest) { } TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; sp<SurfaceControl> childNoBuffer = - mComposerClient->createSurface(String8("Bufferless child"), 10, 10, + mClient->createSurface(String8("Bufferless child"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); sp<SurfaceControl> childBuffer = - mComposerClient->createSurface(String8("Buffered child"), 20, 20, + mClient->createSurface(String8("Buffered child"), 20, 20, PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get()); fillSurfaceRGBA8(childBuffer, 200, 200, 200); @@ -1800,7 +2413,7 @@ TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { } TEST_F(LayerUpdateTest, MergingTransactions) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; { SCOPED_TRACE("before move"); ScreenCapture::captureScreen(&sc); @@ -1827,13 +2440,13 @@ class ChildLayerTest : public LayerUpdateTest { protected: void SetUp() override { LayerUpdateTest::SetUp(); - mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + mChild = mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(mChild, 200, 200, 200); { SCOPED_TRACE("before anything"); - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(64, 64); } } @@ -1843,7 +2456,7 @@ protected: } sp<SurfaceControl> mChild; - sp<ScreenCapture> mCapture; + std::unique_ptr<ScreenCapture> mCapture; }; TEST_F(ChildLayerTest, ChildLayerPositioning) { @@ -1854,7 +2467,7 @@ TEST_F(ChildLayerTest, ChildLayerPositioning) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -1866,7 +2479,7 @@ TEST_F(ChildLayerTest, ChildLayerPositioning) { asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground should now be at 0, 0 mCapture->expectFGColor(0, 0); // But 10 pixels in we should see the child surface @@ -1881,27 +2494,11 @@ TEST_F(ChildLayerTest, ChildLayerCropping) { t.show(mChild); t.setPosition(mChild, 0, 0); t.setPosition(mFGSurfaceControl, 0, 0); - t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); - }); - - { - ScreenCapture::captureScreen(&mCapture); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(4, 4); - mCapture->expectBGColor(5, 5); - } -} - -TEST_F(ChildLayerTest, ChildLayerFinalCropping) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(0, 0); mCapture->expectChildColor(4, 4); mCapture->expectBGColor(5, 5); @@ -1916,7 +2513,7 @@ TEST_F(ChildLayerTest, ChildLayerConstraints) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(0, 0); // Last pixel in foreground should now be the child. mCapture->expectChildColor(63, 63); @@ -1931,7 +2528,7 @@ TEST_F(ChildLayerTest, ChildLayerScaling) { // Find the boundary between the parent and child { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(9, 9); mCapture->expectFGColor(10, 10); } @@ -1941,7 +2538,7 @@ TEST_F(ChildLayerTest, ChildLayerScaling) { // The boundary should be twice as far from the origin now. // The pixels from the last test should all be child now { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(9, 9); mCapture->expectChildColor(10, 10); mCapture->expectChildColor(19, 19); @@ -1962,7 +2559,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Unblended child color mCapture->checkPixel(0, 0, 0, 254, 0); } @@ -1970,7 +2567,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Child and BG blended. mCapture->checkPixel(0, 0, 127, 127, 0); } @@ -1978,7 +2575,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Child and BG blended. mCapture->checkPixel(0, 0, 95, 64, 95); } @@ -1992,7 +2589,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2006,7 +2603,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); // In reparenting we should have exposed the entire foreground surface. mCapture->expectFGColor(74, 74); @@ -2025,7 +2622,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2034,6 +2631,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { mCapture->expectFGColor(84, 84); } + asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); asTransaction([&](Transaction& t) { t.hide(mChild); }); @@ -2041,7 +2639,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { // Since the child has the same client as the parent, it will not get // detached and will be hidden. { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); mCapture->expectFGColor(74, 74); mCapture->expectFGColor(84, 84); @@ -2067,7 +2665,7 @@ TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2082,7 +2680,7 @@ TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { // Nothing should have changed. { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); mCapture->expectChildColor(74, 74); mCapture->expectFGColor(84, 84); @@ -2097,7 +2695,7 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); // But it's only 10x10. @@ -2111,7 +2709,7 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); mCapture->expectChildColor(10, 10); @@ -2130,7 +2728,7 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); // But it's only 10x10. @@ -2149,7 +2747,7 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { { // The child should still be in the same place and not have any strange scaling as in // b/37673612. - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(0, 0); mCapture->expectFGColor(10, 10); } @@ -2160,14 +2758,14 @@ TEST_F(ChildLayerTest, Bug36858924) { mChild.clear(); // Now recreate it as hidden - mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + mChild = mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, mFGSurfaceControl.get()); // Show the child layer in a deferred transaction asTransaction([&](Transaction& t) { - t.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()); + t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); t.show(mChild); }); @@ -2194,7 +2792,7 @@ TEST_F(ChildLayerTest, Reparent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2206,7 +2804,7 @@ TEST_F(ChildLayerTest, Reparent) { asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); // In reparenting we should have exposed the entire foreground surface. mCapture->expectFGColor(74, 74); @@ -2225,7 +2823,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2235,7 +2833,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { } asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Nothing should have changed. mCapture->expectFGColor(64, 64); mCapture->expectChildColor(74, 74); @@ -2244,8 +2842,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { } TEST_F(ChildLayerTest, ReparentFromNoParent) { - sp<SurfaceControl> newSurface = mComposerClient->createSurface(String8("New Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0); ASSERT_TRUE(newSurface != nullptr); ASSERT_TRUE(newSurface->isValid()); @@ -2259,7 +2856,7 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // At 10, 10 we should see the new surface @@ -2269,7 +2866,7 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from // mFGSurface, putting it at 74, 74. mCapture->expectFGColor(64, 64); @@ -2280,12 +2877,12 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { TEST_F(ChildLayerTest, NestedChildren) { sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 10, 10, + mClient->createSurface(String8("Grandchild surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer // which begins at 64, 64 mCapture->checkPixel(64, 64, 50, 50, 50); @@ -2293,8 +2890,7 @@ TEST_F(ChildLayerTest, NestedChildren) { } TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 128, - 128, PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0); fillSurfaceRGBA8(relative, 255, 255, 255); Transaction t; @@ -2306,7 +2902,7 @@ TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { // We expect that the child should have been elevated above our // INT_MAX layer even though it's not a child of it. { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(0, 0); mCapture->expectChildColor(9, 9); mCapture->checkPixel(10, 10, 255, 255, 255); @@ -2330,7 +2926,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { auto fgHandle = mFGSurfaceControl->getHandle(); sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); @@ -2346,7 +2942,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { auto fgHandle = mFGSurfaceControl->getHandle(); sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); @@ -2360,7 +2956,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { TEST_F(ScreenCaptureTest, CaptureTransparent) { sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); @@ -2380,10 +2976,9 @@ TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { auto fgHandle = mFGSurfaceControl->getHandle(); sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 10, - 10, PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); fillSurfaceRGBA8(child, 200, 200, 200); fillSurfaceRGBA8(relative, 100, 100, 100); @@ -2404,10 +2999,10 @@ TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { auto fgHandle = mFGSurfaceControl->getHandle(); sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); sp<SurfaceControl> relative = - mComposerClient->createSurface(String8("Relative surface"), 10, 10, + mClient->createSurface(String8("Relative surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); fillSurfaceRGBA8(relative, 100, 100, 100); @@ -2438,7 +3033,7 @@ public: LayerUpdateTest::SetUp(); mChild = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(mChild, 200, 200, 200); @@ -2465,8 +3060,9 @@ TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { } TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { - - SurfaceComposerClient::Transaction().setCrop(mFGSurfaceControl, Rect(0, 0, 1, 1)).apply(true); + SurfaceComposerClient::Transaction() + .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1)) + .apply(true); // Even though the parent is cropped out we should still capture the child. verify(); @@ -2495,12 +3091,12 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { auto fgHandle = mFGSurfaceControl->getHandle(); sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 5, 5, + mClient->createSurface(String8("Grandchild surface"), 5, 5, PIXEL_FORMAT_RGBA_8888, 0, child.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); @@ -2519,7 +3115,7 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { TEST_F(ScreenCaptureTest, CaptureChildOnly) { sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); auto childHandle = child->getHandle(); @@ -2534,13 +3130,13 @@ TEST_F(ScreenCaptureTest, CaptureChildOnly) { TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); auto childHandle = child->getHandle(); sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 5, 5, + mClient->createSurface(String8("Grandchild surface"), 5, 5, PIXEL_FORMAT_RGBA_8888, 0, child.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); @@ -2559,14 +3155,13 @@ TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { } TEST_F(ScreenCaptureTest, CaptureCrop) { - sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60, - PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); sp<SurfaceControl> blueLayer = - mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -2593,14 +3188,13 @@ TEST_F(ScreenCaptureTest, CaptureCrop) { } TEST_F(ScreenCaptureTest, CaptureSize) { - sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60, - PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); sp<SurfaceControl> blueLayer = - mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, + mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -2629,13 +3223,12 @@ TEST_F(ScreenCaptureTest, CaptureSize) { } TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { - sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60, - PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); auto redLayerHandle = redLayer->getHandle(); - mComposerClient->destroySurface(redLayerHandle); + mClient->destroySurface(redLayerHandle); SurfaceComposerClient::Transaction().apply(true); sp<GraphicBuffer> outBuffer; @@ -2651,9 +3244,9 @@ protected: void SetUp() override { LayerTransactionTest::SetUp(); bgLayer = createLayer("BG layer", 20, 20); - fillLayerColor(bgLayer, Color::RED); + fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20); fgLayer = createLayer("FG layer", 20, 20); - fillLayerColor(fgLayer, Color::BLUE); + fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20); Transaction().setLayer(fgLayer, mLayerZBase + 1).apply(); { SCOPED_TRACE("before anything"); diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 6ad1b87464..a5b522e827 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -30,8 +30,9 @@ cc_test { "libutils", ], static_libs: [ + "libgmock", + "librenderengine", "libtrace_proto", - "libgmock" ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index 9b319854e6..356a880b6e 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -624,7 +624,7 @@ TEST_F(TransactionTest, LayerCrop) { { TransactionScope ts(*sFakeComposer); Rect cropRect(16, 16, 32, 32); - ts.setCrop(mFGSurfaceControl, cropRect); + ts.setCrop_legacy(mFGSurfaceControl, cropRect); } ASSERT_EQ(2, sFakeComposer->getFrameCount()); @@ -634,40 +634,6 @@ TEST_F(TransactionTest, LayerCrop) { EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); } -TEST_F(TransactionTest, LayerFinalCrop) { - // TODO: Add scaling to confirm that crop happens in display space? - { - TransactionScope ts(*sFakeComposer); - Rect cropRect(32, 32, 32 + 64, 32 + 64); - ts.setFinalCrop(mFGSurfaceControl, cropRect); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // In display space we are cropping with [32, 32, 96, 96] against display rect - // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96] - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; - referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32}; - - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - -TEST_F(TransactionTest, LayerFinalCropEmpty) { - // TODO: Add scaling to confirm that crop happens in display space? - { - TransactionScope ts(*sFakeComposer); - Rect cropRect(16, 16, 32, 32); - ts.setFinalCrop(mFGSurfaceControl, cropRect); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // In display space we are cropping with [16, 16, 32, 32] against display rect - // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited. - std::vector<RenderState> referenceFrame(1); - referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - TEST_F(TransactionTest, LayerSetLayer) { { TransactionScope ts(*sFakeComposer); @@ -847,18 +813,16 @@ TEST_F(TransactionTest, DeferredTransaction) { { TransactionScope ts(*sFakeComposer); ts.setAlpha(mFGSurfaceControl, 0.75); - ts.deferTransactionUntil(mFGSurfaceControl, - syncSurfaceControl->getHandle(), - syncSurfaceControl->getSurface()->getNextFrameNumber()); + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber()); } EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); { TransactionScope ts(*sFakeComposer); ts.setPosition(mFGSurfaceControl, 128, 128); - ts.deferTransactionUntil(mFGSurfaceControl, - syncSurfaceControl->getHandle(), - syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); } EXPECT_EQ(4, sFakeComposer->getFrameCount()); EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); @@ -981,7 +945,7 @@ TEST_F(ChildLayerTest, Cropping) { ts.show(mChild); ts.setPosition(mChild, 0, 0); ts.setPosition(mFGSurfaceControl, 0, 0); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); } // NOTE: The foreground surface would be occluded by the child // now, but is included in the stack because the child is @@ -994,22 +958,6 @@ TEST_F(ChildLayerTest, Cropping) { EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); } -TEST_F(ChildLayerTest, FinalCropping) { - { - TransactionScope ts(*sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 0, 0); - ts.setPosition(mFGSurfaceControl, 0, 0); - ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); - } - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - TEST_F(ChildLayerTest, Constraints) { { TransactionScope ts(*sFakeComposer); @@ -1095,7 +1043,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); } -TEST_F(ChildLayerTest, DetachChildren) { +TEST_F(ChildLayerTest, DetachChildrenSameClient) { { TransactionScope ts(*sFakeComposer); ts.show(mChild); @@ -1111,14 +1059,59 @@ TEST_F(ChildLayerTest, DetachChildren) { { TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 0, 0); ts.detachChildren(mFGSurfaceControl); } { TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 64, 64); ts.hide(mChild); } + std::vector<RenderState> refFrame(2); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + refFrame[FG_LAYER] = mBaseFrame[FG_LAYER]; + + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + ASSERT_TRUE(childNewClient != nullptr); + ASSERT_TRUE(childNewClient->isValid()); + fillSurfaceRGBA8(childNewClient, LIGHT_GRAY); + + { + TransactionScope ts(*sFakeComposer); + ts.hide(mChild); + ts.show(childNewClient); + ts.setPosition(childNewClient, 10, 10); + ts.setPosition(mFGSurfaceControl, 64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.detachChildren(mFGSurfaceControl); + ts.setPosition(mFGSurfaceControl, 0, 0); + } + + { + TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 64, 64); + ts.setPosition(childNewClient, 0, 0); + ts.hide(childNewClient); + } + // Nothing should have changed. The child control becomes a no-op // zombie on detach. See comments for detachChildren in the // SurfaceControl.h file. @@ -1193,8 +1186,8 @@ TEST_F(ChildLayerTest, Bug36858924) { // Show the child layer in a deferred transaction { TransactionScope ts(*sFakeComposer); - ts.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()); + ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); ts.show(mChild); } @@ -1217,6 +1210,81 @@ TEST_F(ChildLayerTest, Bug36858924) { sFakeComposer->runVSyncAndWait(); } +class ChildColorLayerTest : public ChildLayerTest { +protected: + void SetUp() override { + TransactionTest::SetUp(); + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, + mFGSurfaceControl.get()); + { + TransactionScope ts(*sFakeComposer); + ts.setColor(mChild, + {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f}); + } + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); + mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f}; + mBaseFrame[CHILD_LAYER].mSwapCount = 0; + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + } +}; + +TEST_F(ChildColorLayerTest, LayerAlpha) { + { + TransactionScope ts(*sFakeComposer); + ts.show(mChild); + ts.setPosition(mChild, 0, 0); + ts.setPosition(mFGSurfaceControl, 0, 0); + ts.setAlpha(mChild, 0.5); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.setAlpha(mFGSurfaceControl, 0.5); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f; + referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildColorLayerTest, LayerZeroAlpha) { + { + TransactionScope ts(*sFakeComposer); + ts.show(mChild); + ts.setPosition(mChild, 0, 0); + ts.setPosition(mFGSurfaceControl, 0, 0); + ts.setAlpha(mChild, 0.5); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.setAlpha(mFGSurfaceControl, 0.0f); + } + + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + class LatchingTest : public TransactionTest { protected: void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); } @@ -1235,8 +1303,7 @@ protected: TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 64, 64); ts.setPosition(mFGSurfaceControl, 64, 64); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64)); - ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); } }; @@ -1280,7 +1347,7 @@ TEST_F(LatchingTest, CropLatching) { { TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 128, 128); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63)); } auto referenceFrame1 = mBaseFrame; @@ -1294,7 +1361,7 @@ TEST_F(LatchingTest, CropLatching) { TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 128, 128); ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63)); } EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); @@ -1307,111 +1374,6 @@ TEST_F(LatchingTest, CropLatching) { EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); } -TEST_F(LatchingTest, FinalCropLatching) { - // Normally the crop applies immediately even while a resize is pending. - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - } - - auto referenceFrame1 = mBaseFrame; - referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; - referenceFrame1[FG_LAYER].mSourceCrop = - hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; - EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); - - restoreInitialState(); - - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - } - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - - completeFGResize(); - - auto referenceFrame2 = mBaseFrame; - referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; - referenceFrame2[FG_LAYER].mSourceCrop = - hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; - referenceFrame2[FG_LAYER].mSwapCount++; - EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); -} - -// In this test we ensure that setGeometryAppliesWithResize actually demands -// a buffer of the new size, and not just any size. -TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) { - // Normally the crop applies immediately even while a resize is pending. - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - } - - auto referenceFrame1 = mBaseFrame; - referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; - referenceFrame1[FG_LAYER].mSourceCrop = - hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; - EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); - - restoreInitialState(); - - // In order to prepare to submit a buffer at the wrong size, we acquire it prior to - // initiating the resize. - lockAndFillFGBuffer(); - - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - } - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - - // We now submit our old buffer, at the old size, and ensure it doesn't - // trigger geometry latching. - unlockFGBuffer(); - - auto referenceFrame2 = mBaseFrame; - referenceFrame2[FG_LAYER].mSwapCount++; - EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); - - completeFGResize(); - auto referenceFrame3 = referenceFrame2; - referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; - referenceFrame3[FG_LAYER].mSourceCrop = - hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; - referenceFrame3[FG_LAYER].mSwapCount++; - EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame())); -} - -TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) { - // In this scenario, we attempt to set the final crop a second time while the resize - // is still pending, and ensure we are successful. Success meaning the second crop - // is the one which eventually latches and not the first. - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - } - - { - TransactionScope ts(*sFakeComposer); - ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1)); - } - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - - completeFGResize(); - - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mSwapCount++; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - } // namespace int main(int argc, char** argv) { diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 9949bfa8a2..6fe52d3589 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -1,4 +1,4 @@ -// Copyright (C) 2018 The Android Open Source Project +// Copyright 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,16 +16,26 @@ cc_test { name: "libsurfaceflinger_unittest", defaults: ["libsurfaceflinger_defaults"], test_suites: ["device-tests"], + sanitize: { + // Using the address sanitizer not only helps uncover issues in the code + // covered by the tests, but also covers some of the tricky injection of + // fakes the unit tests currently do. + address: true, + }, srcs: [ ":libsurfaceflinger_sources", + "CompositionTest.cpp", + "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", "EventControlThreadTest.cpp", "EventThreadTest.cpp", + "SchedulerTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplaySurface.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", "mock/gui/MockGraphicBufferConsumer.cpp", "mock/gui/MockGraphicBufferProducer.cpp", + "mock/MockDispSync.cpp", "mock/MockEventControlThread.cpp", "mock/MockEventThread.cpp", "mock/MockMessageQueue.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp new file mode 100644 index 0000000000..1352df598d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -0,0 +1,1277 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CompositionTest" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <gui/IProducerListener.h> +#include <log/log.h> +#include <system/window.h> +#include <utils/String8.h> + +#include "BufferQueueLayer.h" +#include "ColorLayer.h" +#include "Layer.h" + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockDisplaySurface.h" +#include "mock/MockDispSync.h" +#include "mock/MockEventControlThread.h" +#include "mock/MockEventThread.h" +#include "mock/MockMessageQueue.h" +#include "mock/RenderEngine/MockRenderEngine.h" + +namespace android { +namespace { + +using testing::_; +using testing::AtLeast; +using testing::ByMove; +using testing::DoAll; +using testing::IsNull; +using testing::Mock; +using testing::NotNull; +using testing::Ref; +using testing::Return; +using testing::ReturnRef; +using testing::SetArgPointee; + +using android::Hwc2::Error; +using android::Hwc2::IComposer; +using android::Hwc2::IComposerClient; +using android::Hwc2::Transform; + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + +constexpr hwc2_display_t HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; +constexpr hwc2_layer_t HWC_LAYER = 5000; +constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0); + +constexpr int DEFAULT_DISPLAY_WIDTH = 1920; +constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; + +constexpr int DEFAULT_CONFIG_ID = 0; +constexpr int DEFAULT_TEXTURE_ID = 6000; +constexpr int DEFAULT_LAYER_STACK = 7000; + +constexpr int DEFAULT_DISPLAY_MAX_LUMINANCE = 500; + +constexpr int DEFAULT_SIDEBAND_STREAM = 51; + +class CompositionTest : public testing::Test { +public: + CompositionTest() { + 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()); + + mFlinger.mutableEventControlThread().reset(mEventControlThread); + mFlinger.mutableEventThread().reset(mEventThread); + mFlinger.mutableEventQueue().reset(mMessageQueue); + + mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); + EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); + EXPECT_CALL(*mPrimaryDispSync, getPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); + + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); + setupComposer(0); + } + + ~CompositionTest() { + 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()); + } + + void setupComposer(int virtualDisplayCount) { + mComposer = new Hwc2::mock::Composer(); + EXPECT_CALL(*mComposer, getCapabilities()) + .WillOnce(Return(std::vector<IComposer::Capability>())); + EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount)); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + + Mock::VerifyAndClear(mComposer); + } + + void setupForceGeometryDirty() { + // TODO: This requires the visible region and other related + // state to be set, and is problematic for BufferLayers since they are + // not visible without a buffer (and setting up a buffer looks like a + // pain) + // mFlinger.mutableVisibleRegionsDirty() = true; + + mFlinger.mutableGeometryInvalid() = true; + } + + template <typename Case> + void displayRefreshCompositionDirtyGeometry(); + + template <typename Case> + void displayRefreshCompositionDirtyFrame(); + + template <typename Case> + void captureScreenComposition(); + + std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream}; + + TestableSurfaceFlinger mFlinger; + sp<DisplayDevice> mDisplay; + sp<DisplayDevice> mExternalDisplay; + sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface(); + renderengine::mock::Surface* mRenderSurface = new renderengine::mock::Surface(); + + mock::EventThread* mEventThread = new mock::EventThread(); + mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); + + Hwc2::mock::Composer* mComposer = nullptr; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); + mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + renderengine::mock::Image* mReImage = new renderengine::mock::Image(); + renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer(); + + sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; + + sp<GraphicBuffer> mCaptureScreenBuffer; +}; + +template <typename LayerCase> +void CompositionTest::displayRefreshCompositionDirtyGeometry() { + setupForceGeometryDirty(); + LayerCase::setupForDirtyGeometry(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.onMessageReceived(MessageQueue::INVALIDATE); + mFlinger.onMessageReceived(MessageQueue::REFRESH); + + LayerCase::cleanup(this); +} + +template <typename LayerCase> +void CompositionTest::displayRefreshCompositionDirtyFrame() { + LayerCase::setupForDirtyFrame(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.onMessageReceived(MessageQueue::INVALIDATE); + mFlinger.onMessageReceived(MessageQueue::REFRESH); + + LayerCase::cleanup(this); +} + +template <typename LayerCase> +void CompositionTest::captureScreenComposition() { + LayerCase::setupForScreenCapture(this); + + const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); + constexpr bool useIdentityTransform = true; + constexpr bool forSystem = true; + + DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB, + ui::Transform::ROT_0); + + auto traverseLayers = [this](const LayerVector::Visitor& visitor) { + return mFlinger.traverseLayersInDisplay(mDisplay, visitor); + }; + + // TODO: Eliminate expensive/real allocation if possible. + const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), + HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot"); + + int fd = -1; + status_t result = + mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(), + useIdentityTransform, forSystem, &fd); + if (fd >= 0) { + close(fd); + } + + EXPECT_EQ(NO_ERROR, result); + + LayerCase::cleanup(this); +} + +/* ------------------------------------------------------------------------ + * Variants for each display configuration which can be tested + */ + +template <typename Derived> +struct BaseDisplayVariant { + static constexpr bool IS_SECURE = true; + static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL; + + static void setupPreconditions(CompositionTest* test) { + FakeHwcDisplayInjector(DisplayDevice::DISPLAY_PRIMARY, HWC2::DisplayType::Physical) + .setCapabilities(&test->mDefaultCapabilities) + .inject(&test->mFlinger, test->mComposer); + + test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DisplayDevice::DISPLAY_PRIMARY, + DisplayDevice::DISPLAY_PRIMARY) + .setDisplaySize(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT) + .setDisplaySurface(test->mDisplaySurface) + .setRenderSurface(std::unique_ptr<renderengine::Surface>( + test->mRenderSurface)) + .setSecure(Derived::IS_SECURE) + .setPowerMode(Derived::INIT_POWER_MODE) + .inject(); + test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK); + } + + template <typename Case> + static void setupCommonCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY)) + .Times(1); + EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); + EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1); + EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1); + EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1); + EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1); + + EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); + EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return()); + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, setCurrentSurface(Ref(*test->mRenderSurface))) + .WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, + setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Transform::ROT_0)) + .Times(1); + + EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1); + EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1); + + Case::CompositionType::setupHwcSetCallExpectations(test); + Case::CompositionType::setupHwcGetCallExpectations(test); + } + + template <typename Case> + static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) { + // Called once with a non-null value to set a framebuffer, and then + // again with nullptr to clear it. + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull())).WillOnce(Return(true)); + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull())).WillOnce(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return()); + EXPECT_CALL(*test->mRenderEngine, createFramebuffer()) + .WillOnce(Return( + ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer)))); + EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mRenderEngine, clearWithColor(0, 0, 0, 1)).Times(1); + EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE)) + .Times(1); + // This expectation retires on saturation as setViewportAndProjection is + // called an extra time for the code path this setup is for. + // TODO: Investigate this extra call + EXPECT_CALL(*test->mRenderEngine, + setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Transform::ROT_0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1); + } + + static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mDisplaySurface, beginFrame(true)).Times(1); + } + + static void setupEmptyFrameCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mDisplaySurface, beginFrame(false)).Times(1); + } + + static void setupHwcCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)).Times(1); + + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES)) + .Times(1); + EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence()) + .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence)); + + EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE)) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, setColorTransform(_)).Times(2); + // These expectations retire on saturation as the code path these + // expectations are for appears to make an extra call to them. + // TODO: Investigate this extra call + EXPECT_CALL(*test->mRenderEngine, + setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Transform::ROT_0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*test->mRenderEngine, setCurrentSurface(Ref(*test->mRenderSurface))) + .WillOnce(Return(true)) + .RetiresOnSaturation(); + EXPECT_CALL(*test->mRenderSurface, swapBuffers()).Times(1); + } + + template <typename Case> + static void setupRELayerCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupRECompositionCallExpectations(test); + } + + template <typename Case> + static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupREScreenshotCompositionCallExpectations(test); + + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + } +}; + +struct DefaultDisplaySetupVariant : public BaseDisplayVariant<DefaultDisplaySetupVariant> {}; + +struct InsecureDisplaySetupVariant : public BaseDisplayVariant<InsecureDisplaySetupVariant> { + static constexpr bool IS_SECURE = false; + + template <typename Case> + static void setupRELayerCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupInsecureRECompositionCallExpectations(test); + + // TODO: Investigate this extra call + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + template <typename Case> + static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test); + + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + } +}; + +struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDisplaySetupVariant> { + static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_OFF; + + template <typename Case> + static void setupCommonCompositionCallExpectations(CompositionTest* test) { + // TODO: This seems like an unnecessary call if display is powered off. + EXPECT_CALL(*test->mComposer, + setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY)) + .Times(1); + + // TODO: This seems like an unnecessary call if display is powered off. + Case::CompositionType::setupHwcSetCallExpectations(test); + } + + static void setupHwcCompositionCallExpectations(CompositionTest*) {} + + static void setupRECompositionCallExpectations(CompositionTest* test) { + // TODO: This seems like an unnecessary call if display is powered off. + EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence()) + .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence)); + } + + template <typename Case> + static void setupRELayerCompositionCallExpectations(CompositionTest*) {} +}; + +/* ------------------------------------------------------------------------ + * Variants for each layer configuration which can be tested + */ + +template <typename LayerProperties> +struct BaseLayerProperties { + static constexpr uint32_t WIDTH = 100; + static constexpr uint32_t HEIGHT = 100; + static constexpr PixelFormat FORMAT = PIXEL_FORMAT_RGBA_8888; + static constexpr uint64_t USAGE = + GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_SW_WRITE_NEVER; + static constexpr android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN; + static constexpr uint32_t SCALING_MODE = 0; + static constexpr uint32_t TRANSFORM = 0; + static constexpr uint32_t LAYER_FLAGS = 0; + static constexpr float COLOR[] = {1.f, 1.f, 1.f, 1.f}; + static constexpr IComposerClient::BlendMode BLENDMODE = + IComposerClient::BlendMode::PREMULTIPLIED; + + static void enqueueBuffer(CompositionTest*, sp<BufferQueueLayer> layer) { + auto producer = layer->getProducer(); + + IGraphicBufferProducer::QueueBufferOutput qbo; + status_t result = producer->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &qbo); + if (result != NO_ERROR) { + ALOGE("Failed to connect() (%d)", result); + return; + } + + int slot; + sp<Fence> fence; + result = producer->dequeueBuffer(&slot, &fence, LayerProperties::WIDTH, + LayerProperties::HEIGHT, LayerProperties::FORMAT, + LayerProperties::USAGE, nullptr, nullptr); + if (result != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + ALOGE("Failed to dequeueBuffer() (%d)", result); + return; + } + + sp<GraphicBuffer> buffer; + result = producer->requestBuffer(slot, &buffer); + if (result != NO_ERROR) { + ALOGE("Failed to requestBuffer() (%d)", result); + return; + } + + IGraphicBufferProducer::QueueBufferInput qbi(systemTime(), false /* isAutoTimestamp */, + LayerProperties::DATASPACE, + Rect(LayerProperties::WIDTH, + LayerProperties::HEIGHT), + LayerProperties::SCALING_MODE, + LayerProperties::TRANSFORM, Fence::NO_FENCE); + result = producer->queueBuffer(slot, qbi, &qbo); + if (result != NO_ERROR) { + ALOGE("Failed to queueBuffer (%d)", result); + return; + } + } + + static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) { + // TODO: Eliminate the complexity of actually creating a buffer + EXPECT_CALL(*test->mRenderEngine, getMaxTextureSize()).WillOnce(Return(16384)); + EXPECT_CALL(*test->mRenderEngine, getMaxViewportDims()).WillOnce(Return(16384)); + 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); + enqueueBuffer(test, layer); + Mock::VerifyAndClear(test->mMessageQueue); + + EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); + bool ignoredRecomputeVisibleRegions; + layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE); + Mock::VerifyAndClear(test->mRenderEngine); + Mock::VerifyAndClear(test->mReImage); + } + + static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + setupLatchedBuffer(test, layer); + } + + static void setupBufferLayerPostFrameCallExpectations(CompositionTest* test) { + // BufferLayer::onPostComposition(), when there is no present fence + EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY, _)) + .WillOnce(DoAll(SetArgPointee<1>(DEFAULT_CONFIG_ID), Return(Error::NONE))); + } + + static void setupHwcSetGeometryCallExpectations(CompositionTest* test) { + // TODO: Coverage of other values + EXPECT_CALL(*test->mComposer, + setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE)) + .Times(1); + // TODO: Coverage of other values for origin + EXPECT_CALL(*test->mComposer, + setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER, + IComposerClient::Rect({0, 0, LayerProperties::WIDTH, + LayerProperties::HEIGHT}))) + .Times(1); + EXPECT_CALL(*test->mComposer, + setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3])) + .Times(1); + // TODO: Coverage of other values + EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1); + // TODO: Coverage of other values + EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1); + + // These expectations retire on saturation as the code path these + // expectations are for appears to make an extra call to them. + // TODO: Investigate this extra call + EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM)) + .Times(AtLeast(1)) + .RetiresOnSaturation(); + } + + static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER, + IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH, + LayerProperties::HEIGHT}))) + .Times(1); + } + + static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER, + IComposerClient::FRect({0.f, 0.f, 0.f, 0.f}))) + .Times(1); + } + + static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER, + std::vector<IComposerClient::Rect>({IComposerClient::Rect( + {0, 0, LayerProperties::WIDTH, + LayerProperties::HEIGHT})}))) + .Times(1); + } + + static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1); + + // TODO: use COLOR + EXPECT_CALL(*test->mComposer, + setLayerColor(HWC_DISPLAY, HWC_LAYER, + IComposerClient::Color({0xff, 0xff, 0xff, 0xff}))) + .Times(1); + + // TODO: ColorLayer::onPreComposition() always returns true, triggering an + // extra layer update in SurfaceFlinger::preComposition(). This seems + // wrong on the surface. + EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1); + } + + static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1); + EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1); + + setupBufferLayerPostFrameCallExpectations(test); + } + + static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, + setupLayerBlending(true, false, false, + half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]))) + .Times(1); + + EXPECT_CALL(*test->mRenderEngine, createImage()) + .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage)))); + EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1); + // This call retires on saturation as the code that renders a texture disables the state, + // along with a top-level disable to ensure it is disabled for non-buffer layers. + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation(); + } + + static void setupREBufferCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCommonCallExpectations(test); + + // TODO - Investigate and eliminate these differences between display + // composition and screenshot composition. + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) { + setupREBufferCompositionCallExpectations(test); + } + + static void setupREBufferScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCommonCallExpectations(test); + } + + static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCommonCallExpectations(test); + } + + static void setupREColorCompositionCommonCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + static void setupREColorCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, + setupLayerBlending(true, false, true, + half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]))) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + } + + static void setupREColorScreenshotCompositionCallExpectations(CompositionTest* test) { + setupREColorCompositionCallExpectations(test); + } +}; + +struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {}; + +struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {}; + +struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> { + using Base = BaseLayerProperties<SidebandLayerProperties>; + static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE; + + static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + sp<NativeHandle> stream = + NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), + false); + test->mFlinger.setLayerSidebandStream(layer, stream); + } + + static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER, + IComposerClient::FRect({0.f, 0.f, -1.f, -1.f}))) + .Times(1); + } + + static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerSidebandStream(HWC_DISPLAY, HWC_LAYER, + reinterpret_cast<native_handle_t*>( + DEFAULT_SIDEBAND_STREAM))) + .WillOnce(Return(Error::NONE)); + + EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1); + } + + static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, setupFillWithColor(0, 0, 0, 1)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + } +}; + +struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> { + using Base = BaseLayerProperties<SecureLayerProperties>; + + static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure; + + static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, createImage()) + .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage)))); + EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1); + + EXPECT_CALL(*test->mRenderEngine, + setupLayerBlending(true, false, false, + half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2], + Base::COLOR[3]))) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1); + // This call retires on saturation as the code that renders a texture disables the state, + // along with a top-level disable to ensure it is disabled for non-buffer layers. + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation(); + } + + static void setupInsecureREBufferCompositionCallExpectations(CompositionTest* test) { + setupInsecureREBufferCompositionCommonCallExpectations(test); + Base::setupBufferLayerPostFrameCallExpectations(test); + } + + static void setupInsecureREBufferScreenshotCompositionCallExpectations(CompositionTest* test) { + setupInsecureREBufferCompositionCommonCallExpectations(test); + } +}; + +struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> { + using Base = BaseLayerProperties<CursorLayerProperties>; + + static void setupLayerState(CompositionTest* test, sp<BufferQueueLayer> layer) { + Base::setupLayerState(test, layer); + test->mFlinger.setLayerPotentialCursor(layer, true); + } +}; + +struct NoLayerVariant { + using FlingerLayerType = sp<BufferQueueLayer>; + + static FlingerLayerType createLayer(CompositionTest*) { return FlingerLayerType(); } + static void injectLayer(CompositionTest*, FlingerLayerType) {} + static void cleanupInjectedLayers(CompositionTest*) {} + + static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {} + static void setupCallExpectationsForDirtyFrame(CompositionTest*) {} +}; + +template <typename LayerProperties> +struct BaseLayerVariant { + template <typename L, typename F> + static sp<L> createLayerWithFactory(CompositionTest* test, F factory) { + EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(0); + + sp<L> layer = factory(); + + Mock::VerifyAndClear(test->mComposer); + Mock::VerifyAndClear(test->mRenderEngine); + Mock::VerifyAndClear(test->mMessageQueue); + + auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); + layerDrawingState.layerStack = DEFAULT_LAYER_STACK; + layerDrawingState.active.w = 100; + layerDrawingState.active.h = 100; + layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]); + + layer->setVisibleRegion(Region(Rect(0, 0, 100, 100))); + + return layer; + } + + static void injectLayer(CompositionTest* test, sp<Layer> layer) { + EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _)) + .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE))); + + layer->createHwcLayer(test->mFlinger.mFlinger->getBE().mHwc.get(), test->mDisplay->getId()); + + Mock::VerifyAndClear(test->mComposer); + + Vector<sp<Layer>> layers; + layers.add(layer); + test->mDisplay->setVisibleLayersSortedByZ(layers); + test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer); + } + + static void cleanupInjectedLayers(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER)) + .WillOnce(Return(Error::NONE)); + for (auto layer : test->mFlinger.mutableDrawingState().layersSortedByZ) { + layer->destroyHwcLayer(test->mDisplay->getId()); + } + test->mFlinger.mutableDrawingState().layersSortedByZ.clear(); + } +}; + +template <typename LayerProperties> +struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> { + using Base = BaseLayerVariant<LayerProperties>; + using FlingerLayerType = sp<ColorLayer>; + + static FlingerLayerType createLayer(CompositionTest* test) { + FlingerLayerType layer = Base::template createLayerWithFactory<ColorLayer>(test, [test]() { + return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), + String8("test-layer"), LayerProperties::WIDTH, + LayerProperties::HEIGHT, + LayerProperties::LAYER_FLAGS)); + }); + return layer; + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREColorCompositionCommonCallExpectations(test); + LayerProperties::setupREColorCompositionCallExpectations(test); + } + + static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREColorScreenshotCompositionCallExpectations(test); + } + + static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { + LayerProperties::setupHwcSetGeometryCallExpectations(test); + LayerProperties::setupHwcSetSourceCropColorCallExpectations(test); + } + + static void setupCallExpectationsForDirtyFrame(CompositionTest* test) { + LayerProperties::setupHwcSetPerFrameCallExpectations(test); + LayerProperties::setupHwcSetPerFrameColorCallExpectations(test); + } +}; + +template <typename LayerProperties> +struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { + using Base = BaseLayerVariant<LayerProperties>; + using FlingerLayerType = sp<BufferQueueLayer>; + + static FlingerLayerType createLayer(CompositionTest* test) { + test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID); + + FlingerLayerType layer = + Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() { + return new BufferQueueLayer( + LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), + String8("test-layer"), LayerProperties::WIDTH, + LayerProperties::HEIGHT, + LayerProperties::LAYER_FLAGS)); + }); + + LayerProperties::setupLayerState(test, layer); + + return layer; + } + + static void cleanupInjectedLayers(CompositionTest* test) { + EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(2); + Base::cleanupInjectedLayers(test); + } + + static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { + LayerProperties::setupHwcSetGeometryCallExpectations(test); + LayerProperties::setupHwcSetSourceCropBufferCallExpectations(test); + } + + static void setupCallExpectationsForDirtyFrame(CompositionTest* test) { + LayerProperties::setupHwcSetPerFrameCallExpectations(test); + LayerProperties::setupHwcSetPerFrameBufferCallExpectations(test); + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCallExpectations(test); + } + + static void setupInsecureRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupInsecureREBufferCompositionCallExpectations(test); + } + + static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferScreenshotCompositionCallExpectations(test); + } + + static void setupInsecureREScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupInsecureREBufferScreenshotCompositionCallExpectations(test); + } +}; + +/* ------------------------------------------------------------------------ + * Variants to control how the composition type is changed + */ + +struct NoCompositionTypeVariant { + static void setupHwcSetCallExpectations(CompositionTest*) {} + + static void setupHwcGetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1); + } +}; + +template <IComposerClient::Composition CompositionType> +struct KeepCompositionTypeVariant { + static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType); + + static void setupHwcSetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType)) + .Times(1); + } + + static void setupHwcGetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1); + } +}; + +template <IComposerClient::Composition InitialCompositionType, + IComposerClient::Composition FinalCompositionType> +struct ChangeCompositionTypeVariant { + static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType); + + static void setupHwcSetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType)) + .Times(1); + } + + static void setupHwcGetCallExpectations(CompositionTest* test) { + 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}), + Return(Error::NONE))); + } +}; + +/* ------------------------------------------------------------------------ + * Variants to select how the composition is expected to be handled + */ + +struct CompositionResultBaseVariant { + static void setupLayerState(CompositionTest*, sp<Layer>) {} + + template <typename Case> + static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { + Case::Layer::setupCallExpectationsForDirtyGeometry(test); + } + + template <typename Case> + static void setupCallExpectationsForDirtyFrame(CompositionTest* test) { + Case::Layer::setupCallExpectationsForDirtyFrame(test); + } +}; + +struct NoCompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcCompositionCallExpectations(test); + } +}; + +struct HwcCompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcCompositionCallExpectations(test); + } +}; + +struct RECompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupRECompositionCallExpectations(test); + Case::Display::template setupRELayerCompositionCallExpectations<Case>(test); + } +}; + +struct ForcedClientCompositionResultVariant : public RECompositionResultVariant { + static void setupLayerState(CompositionTest*, sp<Layer> layer) { + layer->forceClientComposition(DisplayDevice::DISPLAY_PRIMARY); + } + + template <typename Case> + static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {} + + template <typename Case> + static void setupCallExpectationsForDirtyFrame(CompositionTest*) {} +}; + +struct EmptyScreenshotResultVariant { + static void setupLayerState(CompositionTest*, sp<Layer>) {} + + template <typename Case> + static void setupCallExpectations(CompositionTest*) {} +}; + +struct REScreenshotResultVariant : public EmptyScreenshotResultVariant { + using Base = EmptyScreenshotResultVariant; + + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Base::template setupCallExpectations<Case>(test); + Case::Display::template setupRELayerScreenshotCompositionCallExpectations<Case>(test); + } +}; + +/* ------------------------------------------------------------------------ + * Composition test case, containing all the variants being tested + */ + +template <typename DisplayCase, typename LayerCase, typename CompositionTypeCase, + typename CompositionResultCase> +struct CompositionCase { + using ThisCase = + CompositionCase<DisplayCase, LayerCase, CompositionTypeCase, CompositionResultCase>; + using Display = DisplayCase; + using Layer = LayerCase; + using CompositionType = CompositionTypeCase; + using CompositionResult = CompositionResultCase; + + static void setupCommon(CompositionTest* test) { + Display::setupPreconditions(test); + + auto layer = Layer::createLayer(test); + Layer::injectLayer(test, layer); + CompositionResult::setupLayerState(test, layer); + } + + static void setupForDirtyGeometry(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonCompositionCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyGeometry<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void setupForDirtyFrame(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonCompositionCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void setupForScreenCapture(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonScreensCaptureCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void cleanup(CompositionTest* test) { + Layer::cleanupInjectedLayers(test); + + for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) { + hwcDisplay->mutableLayers().clear(); + } + + test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>()); + } +}; + +/* ------------------------------------------------------------------------ + * Composition cases to test + */ + +TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + NoCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + NoCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, noLayersDoesMinimalWorkToCaptureScreen) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + EmptyScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Simple buffer layers + */ + +TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedNormalBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenNormalBufferLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Single-color layers + */ + +TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedColorLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenColorLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with sideband buffers + */ + +TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedSidebandBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSidebandBufferLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with ISurfaceComposerClient::eSecure, on a secure display + */ + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedSecureBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with ISurfaceComposerClient::eSecure, on a non-secure display + */ + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) { + captureScreenComposition< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Cursor layers + */ + +TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedCursorLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenCursorLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Simple buffer layer on a display which is powered off. + */ + +TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) { + displayRefreshCompositionDirtyFrame<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenNormalBufferLayerOnPoweredOffDisplay) { + captureScreenComposition<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp new file mode 100644 index 0000000000..4f1c99e417 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp @@ -0,0 +1,147 @@ +/* + * 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "DisplayHardware/DisplayIdentification.h" + +namespace android { +namespace { + +const unsigned char kInternalEdid[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" + "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" + "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" + "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" + "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" + "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; + +const unsigned char kExternalEdid[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01" + "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25" + "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20" + "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30" + "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48" + "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff" + "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71"; + +// Extended EDID with timing extension. +const unsigned char kExternalEedid[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00" + "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26" + "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00" + "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c" + "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30" + "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18" + "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc" + "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d" + "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07" + "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01" + "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00" + "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00" + "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0" + "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6"; + +template <size_t N> +DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) { + return DisplayIdentificationData(bytes, bytes + N - 1); +} + +} // namespace + +TEST(DisplayIdentificationTest, isEdid) { + EXPECT_FALSE(isEdid({})); + + EXPECT_TRUE(isEdid(asDisplayIdentificationData(kInternalEdid))); + EXPECT_TRUE(isEdid(asDisplayIdentificationData(kExternalEdid))); + EXPECT_TRUE(isEdid(asDisplayIdentificationData(kExternalEedid))); +} + +TEST(DisplayIdentificationTest, parseEdid) { + auto data = asDisplayIdentificationData(kInternalEdid); + auto edid = parseEdid(data); + ASSERT_TRUE(edid); + EXPECT_EQ(0x4ca3u, edid->manufacturerId); + EXPECT_STREQ("SEC", edid->pnpId.data()); + // ASCII text should be used as fallback if display name and serial number are missing. + EXPECT_EQ("121AT11-801", edid->displayName); + + data = asDisplayIdentificationData(kExternalEdid); + edid = parseEdid(data); + ASSERT_TRUE(edid); + EXPECT_EQ(0x22f0u, edid->manufacturerId); + EXPECT_STREQ("HWP", edid->pnpId.data()); + EXPECT_EQ("HP ZR30w", edid->displayName); + + data = asDisplayIdentificationData(kExternalEedid); + edid = parseEdid(data); + ASSERT_TRUE(edid); + EXPECT_EQ(0x4c2du, edid->manufacturerId); + EXPECT_STREQ("SAM", edid->pnpId.data()); + EXPECT_EQ("SAMSUNG", edid->displayName); +} + +TEST(DisplayIdentificationTest, parseInvalidEdid) { + EXPECT_FALSE(isEdid({})); + EXPECT_FALSE(parseEdid({})); + + // Display name must be printable. + auto data = asDisplayIdentificationData(kExternalEdid); + data[97] = '\x1b'; + auto edid = parseEdid(data); + ASSERT_TRUE(edid); + // Serial number should be used as fallback if display name is invalid. + EXPECT_EQ("CN4202137Q", edid->displayName); + + // Parsing should succeed even if EDID is truncated. + data.pop_back(); + edid = parseEdid(data); + ASSERT_TRUE(edid); + EXPECT_EQ("CN4202137Q", edid->displayName); +} + +TEST(DisplayIdentificationTest, getPnpId) { + EXPECT_FALSE(getPnpId(0)); + EXPECT_FALSE(getPnpId(static_cast<uint16_t>(-1))); + + EXPECT_STREQ("SEC", getPnpId(0x4ca3u).value_or(PnpId{}).data()); + EXPECT_STREQ("HWP", getPnpId(0x22f0u).value_or(PnpId{}).data()); + EXPECT_STREQ("SAM", getPnpId(0x4c2du).value_or(PnpId{}).data()); +} + +TEST(DisplayIdentificationTest, generateDisplayId) { + const auto primaryId = generateDisplayId(0, asDisplayIdentificationData(kInternalEdid)); + ASSERT_TRUE(primaryId); + + const auto secondaryId = generateDisplayId(1, asDisplayIdentificationData(kExternalEdid)); + ASSERT_TRUE(secondaryId); + + const auto tertiaryId = generateDisplayId(2, asDisplayIdentificationData(kExternalEedid)); + ASSERT_TRUE(tertiaryId); + + // Display IDs should be unique. + EXPECT_NE(primaryId, secondaryId); + EXPECT_NE(primaryId, tertiaryId); + EXPECT_NE(secondaryId, tertiaryId); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 9ac5f3b73e..d32627a7d3 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -22,9 +22,11 @@ #include <log/log.h> +#include <ui/DebugUtils.h> #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplaySurface.h" +#include "mock/MockDispSync.h" #include "mock/MockEventControlThread.h" #include "mock/MockEventThread.h" #include "mock/MockMessageQueue.h" @@ -115,17 +117,18 @@ public: // These mocks are created by the test, but are destroyed by SurfaceFlinger // by virtue of being stored into a std::unique_ptr. However we still need // to keep a reference to them for use in setting up call expectations. - RE::mock::RenderEngine* mRenderEngine = new RE::mock::RenderEngine(); + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = nullptr; mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor(); + mock::DispSync* mPrimaryDispSync = new mock::DispSync(); // These mocks are created only when expected to be created via a factory. sp<mock::GraphicBufferConsumer> mConsumer; sp<mock::GraphicBufferProducer> mProducer; - mock::NativeWindowSurface* mNativeWindowSurface = nullptr; + surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr; sp<mock::NativeWindow> mNativeWindow; - RE::mock::Surface* mRenderSurface = nullptr; + renderengine::mock::Surface* mRenderSurface = nullptr; }; DisplayTransactionTest::DisplayTransactionTest() { @@ -135,6 +138,7 @@ DisplayTransactionTest::DisplayTransactionTest() { // Default to no wide color display support configured mFlinger.mutableHasWideColorDisplay() = false; + mFlinger.mutableUseColorManagement() = false; mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; // Default to using HWC virtual displays @@ -152,8 +156,9 @@ DisplayTransactionTest::DisplayTransactionTest() { mFlinger.mutableEventControlThread().reset(mEventControlThread); mFlinger.mutableEventThread().reset(mEventThread); mFlinger.mutableEventQueue().reset(mMessageQueue); - mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine)); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.mutableInterceptor().reset(mSurfaceInterceptor); + mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); injectMockComposer(0); } @@ -191,11 +196,12 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { // This setup is only expected once per test. ASSERT_TRUE(mNativeWindowSurface == nullptr); - mNativeWindowSurface = new mock::NativeWindowSurface(); + mNativeWindowSurface = new surfaceflinger::mock::NativeWindowSurface(); mNativeWindow = new mock::NativeWindow(); - mFlinger.setCreateNativeWindowSurface( - [this](auto) { return std::unique_ptr<NativeWindowSurface>(mNativeWindowSurface); }); + mFlinger.setCreateNativeWindowSurface([this](auto) { + return std::unique_ptr<surfaceflinger::NativeWindowSurface>(mNativeWindowSurface); + }); } bool DisplayTransactionTest::hasHwcDisplay(hwc2_display_t displayId) { @@ -207,11 +213,11 @@ bool DisplayTransactionTest::hasTransactionFlagSet(int flag) { } bool DisplayTransactionTest::hasDisplayDevice(sp<IBinder> displayToken) { - return mFlinger.mutableDisplays().indexOfKey(displayToken) >= 0; + return mFlinger.mutableDisplays().count(displayToken) == 1; } sp<DisplayDevice> DisplayTransactionTest::getDisplayDevice(sp<IBinder> displayToken) { - return mFlinger.mutableDisplays().valueFor(displayToken); + return mFlinger.mutableDisplays()[displayToken]; } bool DisplayTransactionTest::hasCurrentDisplayState(sp<IBinder> displayToken) { @@ -234,8 +240,8 @@ const DisplayDeviceState& DisplayTransactionTest::getDrawingDisplayState(sp<IBin * */ -template <DisplayDevice::DisplayType type, DisplayDevice::DisplayType hwcId, int width, int height, - Critical critical, Async async, Secure secure, int grallocUsage> +template <DisplayDevice::DisplayType type, DisplayDevice::DisplayType displayId, int width, + int height, Critical critical, Async async, Secure secure, int grallocUsage> struct DisplayVariant { // The display width and height static constexpr int WIDTH = width; @@ -245,7 +251,9 @@ struct DisplayVariant { // The type for this display static constexpr DisplayDevice::DisplayType TYPE = type; - static constexpr DisplayDevice::DisplayType HWCOMPOSER_ID = hwcId; + static_assert(TYPE != DisplayDevice::DISPLAY_ID_INVALID); + + static constexpr DisplayDevice::DisplayType DISPLAY_ID = displayId; // When creating native window surfaces for the framebuffer, whether those should be critical static constexpr Critical CRITICAL = critical; @@ -257,7 +265,7 @@ struct DisplayVariant { static constexpr Secure SECURE = secure; static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) { - auto injector = FakeDisplayDeviceInjector(test->mFlinger, TYPE, HWCOMPOSER_ID); + auto injector = FakeDisplayDeviceInjector(test->mFlinger, TYPE, DISPLAY_ID); injector.setSecure(static_cast<bool>(SECURE)); return injector; } @@ -271,14 +279,15 @@ struct DisplayVariant { // For simplicity, we only expect to create a single render surface for // each test. ASSERT_TRUE(test->mRenderSurface == nullptr); - test->mRenderSurface = new RE::mock::Surface(); + test->mRenderSurface = new renderengine::mock::Surface(); EXPECT_CALL(*test->mRenderEngine, createSurface()) - .WillOnce(Return(ByMove(std::unique_ptr<RE::Surface>(test->mRenderSurface)))); + .WillOnce(Return(ByMove( + std::unique_ptr<renderengine::Surface>(test->mRenderSurface)))); EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1); EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1); EXPECT_CALL(*test->mRenderSurface, setNativeWindow(test->mNativeWindow.get())).Times(1); - EXPECT_CALL(*test->mRenderSurface, queryWidth()).WillOnce(Return(WIDTH)); - EXPECT_CALL(*test->mRenderSurface, queryHeight()).WillOnce(Return(HEIGHT)); + EXPECT_CALL(*test->mRenderSurface, getWidth()).WillOnce(Return(WIDTH)); + EXPECT_CALL(*test->mRenderSurface, getHeight()).WillOnce(Return(HEIGHT)); } static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) { @@ -353,6 +362,8 @@ struct HwcDisplayVariant { getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, IComposerClient::Attribute::DPI_Y, _)) .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _)) + .WillRepeatedly(Return(Error::UNSUPPORTED)); } // Called by tests to set up HWC call expectations @@ -383,11 +394,6 @@ struct PhysicalDisplayVariant DisplayVariant<type, type, width, height, critical, Async::FALSE, Secure::TRUE, GRALLOC_USAGE_PHYSICAL_DISPLAY>> {}; -// An invalid display -using InvalidDisplayVariant = - DisplayVariant<DisplayDevice::DISPLAY_ID_INVALID, DisplayDevice::DISPLAY_ID_INVALID, 0, 0, - Critical::FALSE, Async::FALSE, Secure::FALSE, 0>; - // A primary display is a physical display that is critical using PrimaryDisplayVariant = PhysicalDisplayVariant<1001, DisplayDevice::DISPLAY_PRIMARY, 3840, 2160, Critical::TRUE>; @@ -453,6 +459,7 @@ struct WideColorSupportNotConfiguredVariant { static void injectConfigChange(DisplayTransactionTest* test) { test->mFlinger.mutableHasWideColorDisplay() = false; + test->mFlinger.mutableUseColorManagement() = false; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; } @@ -471,6 +478,7 @@ struct WideColorP3ColorimetricSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = true; static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; } @@ -499,6 +507,7 @@ struct WideColorNotSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = false; static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; } @@ -578,7 +587,7 @@ struct HdrNotSupportedVariant { struct NonHwcPerFrameMetadataSupportVariant { static constexpr int PER_FRAME_METADATA_KEYS = 0; static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_, _)).Times(0); + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0); } }; @@ -586,9 +595,8 @@ template <typename Display> struct NoPerFrameMetadataSupportVariant { static constexpr int PER_FRAME_METADATA_KEYS = 0; static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>()), - Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>())); } }; @@ -596,8 +604,8 @@ template <typename Display> struct Smpte2086PerFrameMetadataSupportVariant { static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086; static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({ + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>({ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, @@ -608,8 +616,7 @@ struct Smpte2086PerFrameMetadataSupportVariant { PerFrameMetadataKey::WHITE_POINT_Y, PerFrameMetadataKey::MAX_LUMINANCE, PerFrameMetadataKey::MIN_LUMINANCE, - })), - Return(Error::NONE))); + }))); } }; @@ -617,12 +624,11 @@ template <typename Display> struct Cta861_3_PerFrameMetadataSupportVariant { static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3; static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<PerFrameMetadataKey>({ + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>({ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, - })), - Return(Error::NONE))); + }))); } }; @@ -658,7 +664,8 @@ using NonHwcVirtualDisplayCase = using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>; using HwcVirtualDisplayCase = Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant, - NonHwcDisplayHdrSupportVariant, NonHwcPerFrameMetadataSupportVariant>; + HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>, + NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>; using WideColorP3ColorimetricDisplayCase = Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>, HdrNotSupportedVariant<PrimaryDisplayVariant>, @@ -683,9 +690,7 @@ using HdrCta861_3_DisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, HdrNotSupportedVariant<PrimaryDisplayVariant>, Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant, - NonHwcDisplayHdrSupportVariant, - NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>; + /* ------------------------------------------------------------------------ * * SurfaceFlinger::onHotplugReceived @@ -693,8 +698,8 @@ using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfig TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { constexpr int currentSequenceId = 123; - constexpr hwc2_display_t displayId1 = 456; - constexpr hwc2_display_t displayId2 = 654; + constexpr hwc2_display_t hwcDisplayId1 = 456; + constexpr hwc2_display_t hwcDisplayId2 = 654; // -------------------------------------------------------------------- // Preconditions @@ -717,8 +722,8 @@ TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { // Invocation // Simulate two hotplug events (a connect and a disconnect) - mFlinger.onHotplugReceived(currentSequenceId, displayId1, HWC2::Connection::Connected); - mFlinger.onHotplugReceived(currentSequenceId, displayId2, HWC2::Connection::Disconnected); + mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, HWC2::Connection::Connected); + mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, HWC2::Connection::Disconnected); // -------------------------------------------------------------------- // Postconditions @@ -729,9 +734,9 @@ TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { // All events should be in the pending event queue. const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); ASSERT_EQ(2u, pendingEvents.size()); - EXPECT_EQ(displayId1, pendingEvents[0].display); + EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId); EXPECT_EQ(HWC2::Connection::Connected, pendingEvents[0].connection); - EXPECT_EQ(displayId2, pendingEvents[1].display); + EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId); EXPECT_EQ(HWC2::Connection::Disconnected, pendingEvents[1].connection); } @@ -967,6 +972,9 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // The call clears the current render engine surface EXPECT_CALL(*mRenderEngine, resetCurrentSurface()); + // The call ends any display resyncs + EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1); + // -------------------------------------------------------------------- // Invocation @@ -990,6 +998,93 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { } /* ------------------------------------------------------------------------ + * DisplayDevice::GetBestColorMode + */ +class GetBestColorModeTest : public DisplayTransactionTest { +public: + GetBestColorModeTest() + : DisplayTransactionTest(), + mInjector(FakeDisplayDeviceInjector(mFlinger, DisplayDevice::DISPLAY_PRIMARY, 0)) {} + + void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; } + + void addHwcColorModesMapping(ui::ColorMode colorMode, + std::vector<ui::RenderIntent> renderIntents) { + mHwcColorModes[colorMode] = renderIntents; + } + + void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; } + + void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; } + + void getBestColorMode() { + mInjector.setHwcColorModes(mHwcColorModes); + mInjector.setHasWideColorGamut(mHasWideColorGamut); + auto displayDevice = mInjector.inject(); + + displayDevice->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace, + &mOutColorMode, &mOutRenderIntent); + } + + ui::Dataspace mOutDataspace; + ui::ColorMode mOutColorMode; + ui::RenderIntent mOutRenderIntent; + +private: + ui::Dataspace mInputDataspace; + ui::RenderIntent mInputRenderIntent; + bool mHasWideColorGamut = false; + std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes; + FakeDisplayDeviceInjector mInjector; +}; + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) { + addHwcColorModesMapping(ui::ColorMode::SRGB, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::SRGB, mOutDataspace); + ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) { + addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + addHwcColorModesMapping(ui::ColorMode::SRGB, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace); + ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) { + addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace); + ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +/* ------------------------------------------------------------------------ * SurfaceFlinger::setupNewDisplayDeviceInternal */ @@ -1031,7 +1126,10 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // -------------------------------------------------------------------- // Invocation - auto state = DisplayDeviceState(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE)); + DisplayDeviceState state; + state.type = Case::Display::TYPE; + state.isSecure = static_cast<bool>(Case::Display::SECURE); + auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::TYPE, state, displaySurface, producer); @@ -1159,13 +1257,23 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + true)) + .Times(1); } template <typename Case> void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, false)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(Case::Display::TYPE == DisplayDevice::DISPLAY_PRIMARY + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + false)) + .Times(1); } template <typename Case> @@ -1196,7 +1304,7 @@ void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() { static_assert(0 <= Case::Display::TYPE && Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES, "Must use a valid physical display type index for the fixed-size array"); - auto& displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]; + auto& displayToken = mFlinger.mutableDisplayTokens()[Case::Display::TYPE]; ASSERT_TRUE(displayToken != nullptr); verifyDisplayIsConnected<Case>(displayToken); @@ -1285,6 +1393,8 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { // Call Expectations EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _)) + .Times(0); setupCommonCallExpectationsForDisconnectProcessing<Case>(); @@ -1302,7 +1412,7 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { // The display should not be set up as a built-in display. ASSERT_TRUE(0 <= Case::Display::TYPE && Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES); - auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]; + auto displayToken = mFlinger.mutableDisplayTokens()[Case::Display::TYPE]; EXPECT_TRUE(displayToken == nullptr); // The existing token should have been removed @@ -1395,7 +1505,7 @@ TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary // The display should not be set up as a primary built-in display. ASSERT_TRUE(0 <= Case::Display::TYPE && Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES); - auto displayToken = mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]; + auto displayToken = mFlinger.mutableDisplayTokens()[Case::Display::TYPE]; EXPECT_TRUE(displayToken == nullptr); } @@ -1438,7 +1548,7 @@ TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary static_assert(0 <= Case::Display::TYPE && Case::Display::TYPE < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES, "Display type must be a built-in display"); - EXPECT_NE(existing.token(), mFlinger.mutableBuiltinDisplays()[Case::Display::TYPE]); + EXPECT_NE(existing.token(), mFlinger.mutableDisplayTokens()[Case::Display::TYPE]); // A new display should be connected in its place @@ -1467,7 +1577,11 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { // A virtual display was added to the current state, and it has a // surface(producer) sp<BBinder> displayToken = new BBinder(); - DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE)); + + DisplayDeviceState info; + info.type = Case::Display::TYPE; + info.isSecure = static_cast<bool>(Case::Display::SECURE); + sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()}; info.surface = surface; mFlinger.mutableCurrentState().displays.add(displayToken, info); @@ -1531,7 +1645,11 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { // A virtual display was added to the current state, but it does not have a // surface. sp<BBinder> displayToken = new BBinder(); - DisplayDeviceState info(Case::Display::TYPE, static_cast<bool>(Case::Display::SECURE)); + + DisplayDeviceState info; + info.type = Case::Display::TYPE; + info.isSecure = static_cast<bool>(Case::Display::SECURE); + mFlinger.mutableCurrentState().displays.add(displayToken, info); // -------------------------------------------------------------------- @@ -1709,11 +1827,11 @@ TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { // A display is set up auto nativeWindow = new mock::NativeWindow(); auto displaySurface = new mock::DisplaySurface(); - auto renderSurface = new RE::mock::Surface(); + auto renderSurface = new renderengine::mock::Surface(); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.setNativeWindow(nativeWindow); display.setDisplaySurface(displaySurface); - display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface)); + display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface)); display.inject(); // There is a change to the viewport state @@ -1728,8 +1846,8 @@ TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1); EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1); EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1); - EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(newWidth)); - EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(oldHeight)); + EXPECT_CALL(*renderSurface, getWidth()).WillOnce(Return(newWidth)); + EXPECT_CALL(*renderSurface, getHeight()).WillOnce(Return(oldHeight)); // -------------------------------------------------------------------- // Invocation @@ -1750,11 +1868,11 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { // A display is set up auto nativeWindow = new mock::NativeWindow(); auto displaySurface = new mock::DisplaySurface(); - auto renderSurface = new RE::mock::Surface(); + auto renderSurface = new renderengine::mock::Surface(); auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.setNativeWindow(nativeWindow); display.setDisplaySurface(displaySurface); - display.setRenderSurface(std::unique_ptr<RE::Surface>(renderSurface)); + display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface)); display.inject(); // There is a change to the viewport state @@ -1769,8 +1887,8 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { EXPECT_CALL(*renderSurface, setNativeWindow(nullptr)).Times(1); EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1); EXPECT_CALL(*renderSurface, setNativeWindow(nativeWindow)).Times(1); - EXPECT_CALL(*renderSurface, queryWidth()).WillOnce(Return(oldWidth)); - EXPECT_CALL(*renderSurface, queryHeight()).WillOnce(Return(newHeight)); + EXPECT_CALL(*renderSurface, getWidth()).WillOnce(Return(oldWidth)); + EXPECT_CALL(*renderSurface, getHeight()).WillOnce(Return(newHeight)); // -------------------------------------------------------------------- // Invocation @@ -1810,40 +1928,6 @@ TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDispla EXPECT_FALSE(hasCurrentDisplayState(displayToken)); } -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithInvalidDisplay) { - using Case = InvalidDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // An invalid display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The invalid display has some state - display.mutableCurrentDisplayState().layerStack = 654u; - - // The requested display state tries to change the display state. - DisplayState state; - state.what = DisplayState::eLayerStackChanged; - state.token = display.token(); - state.layerStack = 456; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The current display layer stack value is unchanged. - EXPECT_EQ(654u, getCurrentDisplayState(display.token()).layerStack); -} - TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) { using Case = SimplePrimaryDisplayCase; @@ -2419,6 +2503,24 @@ struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { } }; +struct DispSyncIsSupportedVariant { + static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mPrimaryDispSync, reset()).Times(1); + EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1); + EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1); + } + + static void setupEndResyncCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1); + } +}; + +struct DispSyncNotSupportedVariant { + static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {} + + static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {} +}; + // -------------------------------------------------------------------- // Note: // @@ -2440,6 +2542,7 @@ struct TransitionOffToOnVariant static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupBeginResyncCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -2469,6 +2572,7 @@ struct TransitionOnToOffVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::DispSync::setupEndResyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -2504,6 +2608,7 @@ struct TransitionDozeSuspendToDozeVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupBeginResyncCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; @@ -2522,6 +2627,7 @@ struct TransitionDozeSuspendToOnVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupBeginResyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; @@ -2531,6 +2637,7 @@ struct TransitionOnToDozeSuspendVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::DispSync::setupEndResyncCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); } }; @@ -2553,11 +2660,12 @@ struct TransitionOnToUnknownVariant // -------------------------------------------------------------------- template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant, - typename TransitionVariant> + typename DispSyncVariant, typename TransitionVariant> struct DisplayPowerCase { using Display = DisplayVariant; using Doze = DozeVariant; using EventThread = EventThreadVariant; + using DispSync = DispSyncVariant; using Transition = TransitionVariant; static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) { @@ -2605,15 +2713,16 @@ struct DisplayPowerCase { // In addition to having event thread support, we emulate doze support. template <typename TransitionVariant> using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant, - EventThreadIsSupportedVariant, TransitionVariant>; + EventThreadIsSupportedVariant, + DispSyncIsSupportedVariant, TransitionVariant>; // A sample configuration for the external display. // In addition to not having event thread support, we emulate not having doze // support. template <typename TransitionVariant> -using ExternalDisplayPowerCase = - DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant, - EventThreadNotSupportedVariant, TransitionVariant>; +using ExternalDisplayPowerCase = DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant, + EventThreadNotSupportedVariant, + DispSyncNotSupportedVariant, TransitionVariant>; class SetPowerModeInternalTest : public DisplayTransactionTest { public: @@ -2670,7 +2779,7 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.inject(); - // The diplay is already set to HWC_POWER_MODE_NORMAL + // The display is already set to HWC_POWER_MODE_NORMAL display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL); // -------------------------------------------------------------------- @@ -2684,7 +2793,7 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode()); } -TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirtualDisplay) { +TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) { using Case = HwcVirtualDisplayCase; // -------------------------------------------------------------------- @@ -2699,13 +2808,13 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalJustSetsInternalStateIfVirt auto display = Case::Display::makeFakeExistingDisplayInjector(this); display.inject(); - // The display is set to HWC_POWER_MODE_OFF - getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_OFF); + // The display is set to HWC_POWER_MODE_NORMAL + getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_NORMAL); // -------------------------------------------------------------------- // Invocation - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL); + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_OFF); // -------------------------------------------------------------------- // Postconditions diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp index b34645463f..9dc4193ecc 100644 --- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp @@ -23,7 +23,7 @@ #include <log/log.h> #include "AsyncCallRecorder.h" -#include "EventControlThread.h" +#include "Scheduler/EventControlThread.h" namespace android { namespace { diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 80fdb80264..fb3b7a2951 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -25,7 +25,7 @@ #include <utils/Errors.h> #include "AsyncCallRecorder.h" -#include "EventThread.h" +#include "Scheduler/EventThread.h" using namespace std::chrono_literals; using namespace std::placeholders; @@ -71,7 +71,8 @@ protected: ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount); void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); - void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected); + void expectHotplugEventReceivedByConnection(EventThread::DisplayType expectedDisplayType, + bool expectedConnected); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; @@ -169,13 +170,16 @@ void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimes expectedCount); } -void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType, - bool expectedConnected) { +void EventThreadTest::expectHotplugEventReceivedByConnection( + EventThread::DisplayType expectedDisplayType, bool expectedConnected) { + const uint32_t expectedDisplayId = + expectedDisplayType == EventThread::DisplayType::Primary ? 0 : 1; + auto args = mConnectionEventCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); const auto& event = std::get<0>(args.value()); EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, event.header.type); - EXPECT_EQ(static_cast<unsigned>(expectedDisplayType), event.header.id); + EXPECT_EQ(expectedDisplayId, event.header.id); EXPECT_EQ(expectedConnected, event.hotplug.connected); } @@ -394,28 +398,23 @@ TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { } TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false); } TEST_F(EventThreadTest, postHotplugPrimaryConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true); } TEST_F(EventThreadTest, postHotplugExternalDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false); + mThread->onHotplugReceived(EventThread::DisplayType::External, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, false); } TEST_F(EventThreadTest, postHotplugExternalConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true); -} - -TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false); - EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + mThread->onHotplugReceived(EventThread::DisplayType::External, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, true); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp new file mode 100644 index 0000000000..be91a9c8de --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -0,0 +1,189 @@ +#undef LOG_TAG +#define LOG_TAG "SchedulerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include <mutex> + +#include "AsyncCallRecorder.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/Scheduler.h" +#include "mock/MockDispSync.h" +#include "mock/MockEventThread.h" + +using testing::_; +using testing::Return; + +namespace android { + +class SchedulerTest : public testing::Test { +protected: + class MockEventThreadConnection : public BnDisplayEventConnection { + public: + MockEventThreadConnection() = default; + ~MockEventThreadConnection() = default; + + MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel)); + MOCK_METHOD1(setVsyncRate, status_t(uint32_t count)); + MOCK_METHOD0(requestNextVsync, void()); + }; + + /** + * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else + * the same. + */ + class MockScheduler : public android::Scheduler { + public: + MockScheduler(std::unique_ptr<EventThread> eventThread) + : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {} + + std::unique_ptr<EventThread> makeEventThread( + const std::string& /* connectionName */, DispSync* /* dispSync */, + nsecs_t /* phaseOffsetNs */, + impl::EventThread::ResyncWithRateLimitCallback /* resyncCallback */, + impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override { + return std::move(mEventThread); + } + + MockScheduler() = default; + ~MockScheduler() override = default; + + std::unique_ptr<EventThread> mEventThread; + }; + + SchedulerTest(); + ~SchedulerTest() override; + + sp<Scheduler::ConnectionHandle> mConnectionHandle; + mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + mock::EventThread* mEventThread; + std::unique_ptr<MockScheduler> mScheduler; + sp<MockEventThreadConnection> mEventThreadConnection; + + AsyncCallRecorder<void (*)()> mResyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; +}; + +SchedulerTest::SchedulerTest() { + 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()); + + std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); + mEventThread = eventThread.get(); + mScheduler = std::make_unique<MockScheduler>(std::move(eventThread)); + mEventThreadConnection = new MockEventThreadConnection(); + + // createConnection call to scheduler makes a createEventConnection call to EventThread. Make + // sure that call gets executed and returns an EventThread::Connection object. + EXPECT_CALL(*mEventThread, createEventConnection()) + .WillRepeatedly(Return(mEventThreadConnection)); + + mConnectionHandle = + mScheduler->createConnection("appConnection", 16, mResyncCallRecorder.getInvocable(), + mInterceptVSyncCallRecorder.getInvocable()); +} + +SchedulerTest::~SchedulerTest() { + 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()); +} + +namespace { +/* ------------------------------------------------------------------------ + * Test cases + */ +TEST_F(SchedulerTest, canCreateAndDestroyTest) { + EXPECT_FALSE(mResyncCallRecorder.waitForCall().has_value()); + EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall().has_value()); + EXPECT_EQ(0, mConnectionHandle->id); +} + +TEST_F(SchedulerTest, testNullPtr) { + // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any + // exceptions, just gracefully continues. + sp<IDisplayEventConnection> returnedValue; + ASSERT_NO_FATAL_FAILURE(returnedValue = mScheduler->createDisplayEventConnection(nullptr)); + EXPECT_TRUE(returnedValue == nullptr); + EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr); + EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr); + ASSERT_NO_FATAL_FAILURE( + mScheduler->hotplugReceived(nullptr, EventThread::DisplayType::Primary, false)); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr)); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr)); + String8 testString; + ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString)); + EXPECT_TRUE(testString == ""); + ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10)); +} + +TEST_F(SchedulerTest, invalidConnectionHandle) { + // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any + // exceptions, just gracefully continues. + sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20); + + sp<IDisplayEventConnection> returnedValue; + ASSERT_NO_FATAL_FAILURE(returnedValue = + mScheduler->createDisplayEventConnection(connectionHandle)); + EXPECT_TRUE(returnedValue == nullptr); + EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr); + EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr); + + // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. + EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); + ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(connectionHandle, + EventThread::DisplayType::Primary, false)); + + EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle)); + + EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle)); + + String8 testString; + EXPECT_CALL(*mEventThread, dump(_)).Times(0); + ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString)); + EXPECT_TRUE(testString == ""); + + EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0); + ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10)); +} + +TEST_F(SchedulerTest, validConnectionHandle) { + sp<IDisplayEventConnection> returnedValue; + ASSERT_NO_FATAL_FAILURE(returnedValue = + mScheduler->createDisplayEventConnection(mConnectionHandle)); + EXPECT_TRUE(returnedValue != nullptr); + ASSERT_EQ(returnedValue, mEventThreadConnection); + + EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr); + EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr); + + EXPECT_CALL(*mEventThread, onHotplugReceived(EventThread::DisplayType::Primary, false)) + .Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(mConnectionHandle, + EventThread::DisplayType::Primary, false)); + + EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle)); + + EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle)); + + String8 testString("dump"); + EXPECT_CALL(*mEventThread, dump(testString)).Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString)); + EXPECT_TRUE(testString != ""); + + EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10)); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 558845f09b..c3534e83a8 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -16,27 +16,141 @@ #pragma once +#include "BufferQueueLayer.h" +#include "BufferStateLayer.h" +#include "ColorLayer.h" +#include "ContainerLayer.h" #include "DisplayDevice.h" +#include "Layer.h" +#include "NativeWindowSurface.h" +#include "StartPropertySetThread.h" #include "SurfaceFlinger.h" +#include "SurfaceFlingerFactory.h" +#include "SurfaceInterceptor.h" namespace android { class EventThread; -namespace RE { +namespace renderengine { + class RenderEngine; -} + +} // namespace renderengine namespace Hwc2 { + class Composer; -} + +} // namespace Hwc2 + +namespace surfaceflinger::test { + +class Factory final : public surfaceflinger::Factory { +public: + ~Factory() = default; + + std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + std::unique_ptr<EventControlThread> createEventControlThread( + std::function<void(bool)>) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + std::unique_ptr<MessageQueue> createMessageQueue() override { + // TODO: Use test-fixture controlled factory + return std::make_unique<android::impl::MessageQueue>(); + } + + std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override { + // TODO: Use test-fixture controlled factory + return std::make_unique<android::impl::SurfaceInterceptor>(flinger); + } + + sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { + // TODO: Use test-fixture controlled factory + return new StartPropertySetThread(timestampPropertyValue); + } + + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override { + // TODO: Use test-fixture controlled factory + return new DisplayDevice(std::move(creationArgs)); + } + + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override { + // TODO: Use test-fixture controlled factory + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + } + + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) override { + if (!mCreateBufferQueue) return; + mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + } + + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) override { + if (!mCreateNativeWindowSurface) return nullptr; + return mCreateNativeWindowSurface(producer); + } + + sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + sp<ColorLayer> createColorLayer(const LayerCreationArgs&) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + using CreateBufferQueueFunction = + std::function<void(sp<IGraphicBufferProducer>* /* outProducer */, + sp<IGraphicBufferConsumer>* /* outConsumer */, + bool /* consumerIsSurfaceFlinger */)>; + CreateBufferQueueFunction mCreateBufferQueue; + + using CreateNativeWindowSurfaceFunction = + std::function<std::unique_ptr<surfaceflinger::NativeWindowSurface>( + const sp<IGraphicBufferProducer>&)>; + CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface; +}; + +} // namespace surfaceflinger::test class TestableSurfaceFlinger { public: // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. - void setupRenderEngine(std::unique_ptr<RE::RenderEngine> renderEngine) { + void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { mFlinger->getBE().mRenderEngine = std::move(renderEngine); } @@ -44,18 +158,34 @@ public: mFlinger->getBE().mHwc.reset(new HWComposer(std::move(composer))); } - using CreateBufferQueueFunction = SurfaceFlinger::CreateBufferQueueFunction; + using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { - mFlinger->mCreateBufferQueue = f; + mFactory.mCreateBufferQueue = f; } - using CreateNativeWindowSurfaceFunction = SurfaceFlinger::CreateNativeWindowSurfaceFunction; + using CreateNativeWindowSurfaceFunction = + surfaceflinger::test::Factory::CreateNativeWindowSurfaceFunction; void setCreateNativeWindowSurface(CreateNativeWindowSurfaceFunction f) { - mFlinger->mCreateNativeWindowSurface = f; + mFactory.mCreateNativeWindowSurface = f; } using HotplugEvent = SurfaceFlinger::HotplugEvent; + auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; } + auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; } + + void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) { + layer->getBE().compositionInfo.hwc.sidebandStream = sidebandStream; + } + + void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) { + layer->getBE().mHwcLayers[DisplayDevice::DISPLAY_PRIMARY].compositionType = type; + }; + + void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) { + layer->mPotentialCursor = potentialCursor; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -64,15 +194,17 @@ public: return mFlinger->createDisplay(displayName, secure); } - auto destroyDisplay(const sp<IBinder>& display) { return mFlinger->destroyDisplay(display); } + auto destroyDisplay(const sp<IBinder>& displayToken) { + return mFlinger->destroyDisplay(displayToken); + } auto resetDisplayState() { return mFlinger->resetDisplayState(); } - auto setupNewDisplayDeviceInternal(const wp<IBinder>& display, int hwcId, + auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken, int32_t displayId, const DisplayDeviceState& state, const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) { - return mFlinger->setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface, + return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface, producer); } @@ -89,8 +221,23 @@ public: auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); } - auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) { - return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld); + auto setPowerModeInternal(const sp<DisplayDevice>& display, int mode, + bool stateLockHeld = false) { + return mFlinger->setPowerModeInternal(display, mode, stateLockHeld); + } + + auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); } + + auto captureScreenImplLocked(const RenderArea& renderArea, + TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer, + bool useIdentityTransform, bool forSystem, int* outSyncFd) { + return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer, + useIdentityTransform, forSystem, outSyncFd); + } + + auto traverseLayersInDisplay(const sp<const DisplayDevice>& display, + const LayerVector::Visitor& visitor) { + return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor); } /* ------------------------------------------------------------------------ @@ -110,20 +257,25 @@ public: */ auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; } + auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; } + auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; } - auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } - auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } + auto& mutableDisplays() { return mFlinger->mDisplays; } + auto& mutableDisplayTokens() { return mFlinger->mDisplayTokens; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableEventControlThread() { return mFlinger->mEventControlThread; } auto& mutableEventQueue() { return mFlinger->mEventQueue; } auto& mutableEventThread() { return mFlinger->mEventThread; } + auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; } auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; } auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } + auto& mutablePrimaryDispSync() { return mFlinger->mPrimaryDispSync; } auto& mutablePrimaryHWVsyncEnabled() { return mFlinger->mPrimaryHWVsyncEnabled; } + auto& mutableTexturePool() { return mFlinger->mTexturePool; } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; } @@ -141,6 +293,7 @@ public: mutableEventQueue().reset(); mutableEventThread().reset(); mutableInterceptor().reset(); + mutablePrimaryDispSync().reset(); mFlinger->getBE().mHwc.reset(); mFlinger->getBE().mRenderEngine.reset(); } @@ -153,7 +306,7 @@ public: public: FakePowerAdvisor() = default; ~FakePowerAdvisor() override = default; - void setExpensiveRenderingExpected(hwc2_display_t, bool) override { } + void setExpensiveRenderingExpected(hwc2_display_t, bool) override {} }; struct HWC2Display : public HWC2::Display { @@ -168,6 +321,7 @@ public: auto& mutableIsConnected() { return this->mIsConnected; } auto& mutableConfigs() { return this->mConfigs; } + auto& mutableLayers() { return this->mLayers; } }; class FakeHwcDisplayInjector { @@ -250,7 +404,7 @@ public: display->mutableIsConnected() = true; ASSERT_TRUE(flinger->mutableHwcDisplayData().size() > static_cast<size_t>(mType)); - flinger->mutableHwcDisplayData()[mType].reset(); + flinger->mutableHwcDisplayData()[mType] = HWComposer::DisplayData(); flinger->mutableHwcDisplayData()[mType].hwcDisplay = display.get(); flinger->mutableHwcDisplaySlots().emplace(mHwcDisplayId, mType); @@ -274,8 +428,9 @@ public: class FakeDisplayDeviceInjector { public: FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger, DisplayDevice::DisplayType type, - int hwcId) - : mFlinger(flinger), mType(type), mHwcId(hwcId) {} + int32_t displayId) + : mFlinger(flinger), + mCreationArgs(flinger.mFlinger.get(), mDisplayToken, type, displayId) {} sp<IBinder> token() const { return mDisplayToken; } @@ -295,43 +450,64 @@ public: return mFlinger.mutableCurrentState().displays.valueFor(mDisplayToken); } - auto& mutableDisplayDevice() { return mFlinger.mutableDisplays().valueFor(mDisplayToken); } + auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; } auto& setNativeWindow(const sp<ANativeWindow>& nativeWindow) { - mNativeWindow = nativeWindow; + mCreationArgs.nativeWindow = nativeWindow; return *this; } auto& setDisplaySurface(const sp<DisplaySurface>& displaySurface) { - mDisplaySurface = displaySurface; + mCreationArgs.displaySurface = displaySurface; return *this; } - auto& setRenderSurface(std::unique_ptr<RE::Surface> renderSurface) { - mRenderSurface = std::move(renderSurface); + auto& setRenderSurface(std::unique_ptr<renderengine::Surface> renderSurface) { + mCreationArgs.renderSurface = std::move(renderSurface); return *this; } auto& setSecure(bool secure) { - mSecure = secure; + mCreationArgs.isSecure = secure; + return *this; + } + + auto& setDisplaySize(int width, int height) { + mCreationArgs.displayWidth = width; + mCreationArgs.displayHeight = height; + return *this; + } + + auto& setPowerMode(int mode) { + mCreationArgs.initialPowerMode = mode; + return *this; + } + + auto& setHwcColorModes( + const std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> + hwcColorModes) { + mCreationArgs.hwcColorModes = hwcColorModes; + return *this; + } + + auto& setHasWideColorGamut(bool hasWideColorGamut) { + mCreationArgs.hasWideColorGamut = hasWideColorGamut; return *this; } sp<DisplayDevice> inject() { - std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hdrAndRenderIntents; - sp<DisplayDevice> device = - new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, mSecure, mDisplayToken, - mNativeWindow, mDisplaySurface, std::move(mRenderSurface), 0, - 0, false, HdrCapabilities(), 0, hdrAndRenderIntents, - HWC_POWER_MODE_NORMAL); - mFlinger.mutableDisplays().add(mDisplayToken, device); - - DisplayDeviceState state(mType, mSecure); + DisplayDeviceState state; + state.type = mCreationArgs.type; + state.isSecure = mCreationArgs.isSecure; + + sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs)); + mFlinger.mutableDisplays().emplace(mDisplayToken, device); mFlinger.mutableCurrentState().displays.add(mDisplayToken, state); mFlinger.mutableDrawingState().displays.add(mDisplayToken, state); - if (mType >= DisplayDevice::DISPLAY_PRIMARY && mType < DisplayDevice::DISPLAY_VIRTUAL) { - mFlinger.mutableBuiltinDisplays()[mType] = mDisplayToken; + if (state.type >= DisplayDevice::DISPLAY_PRIMARY && + state.type < DisplayDevice::DISPLAY_VIRTUAL) { + mFlinger.mutableDisplayTokens()[state.type] = mDisplayToken; } return device; @@ -340,15 +516,11 @@ public: private: TestableSurfaceFlinger& mFlinger; sp<BBinder> mDisplayToken = new BBinder(); - DisplayDevice::DisplayType mType; - int mHwcId; - sp<ANativeWindow> mNativeWindow; - sp<DisplaySurface> mDisplaySurface; - std::unique_ptr<RE::Surface> mRenderSurface; - bool mSecure = false; + DisplayDeviceCreationArgs mCreationArgs; }; - sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(SurfaceFlinger::SkipInitialization); + surfaceflinger::test::Factory mFactory; + sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); // We need to keep a reference to these so they are properly destroyed. std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 267670aca5..c0395c01ce 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -30,10 +30,10 @@ namespace mock { using android::hardware::graphics::common::V1_0::ColorTransform; using android::hardware::graphics::common::V1_0::Hdr; using android::hardware::graphics::common::V1_0::Transform; -using android::hardware::graphics::common::V1_1::ColorMode; -using android::hardware::graphics::common::V1_1::Dataspace; using android::hardware::graphics::common::V1_1::PixelFormat; using android::hardware::graphics::common::V1_1::RenderIntent; +using android::hardware::graphics::common::V1_2::ColorMode; +using android::hardware::graphics::common::V1_2::Dataspace; using android::hardware::graphics::composer::V2_1::Config; using android::hardware::graphics::composer::V2_1::Display; @@ -74,9 +74,10 @@ public: MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*)); MOCK_METHOD2(getDozeSupport, Error(Display, bool*)); MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*)); - MOCK_METHOD2(getPerFrameMetadataKeys, - Error(Display, std::vector<IComposerClient::PerFrameMetadataKey>*)); + MOCK_METHOD1(getPerFrameMetadataKeys, + std::vector<IComposerClient::PerFrameMetadataKey>(Display)); MOCK_METHOD2(getDataspaceSaturationMatrix, Error(Dataspace, mat4*)); + MOCK_METHOD3(getDisplayIdentificationData, Error(Display, uint8_t*, std::vector<uint8_t>*)); MOCK_METHOD3(getReleaseFences, Error(Display, std::vector<Layer>*, std::vector<int>*)); MOCK_METHOD2(presentDisplay, Error(Display, int*)); MOCK_METHOD2(setActiveConfig, Error(Display, Config)); @@ -111,6 +112,7 @@ public: MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t)); MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t)); MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*)); + MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp new file mode 100644 index 0000000000..2f7e5ead0f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp @@ -0,0 +1,27 @@ +/* + * 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/MockDispSync.h" + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +DispSync::DispSync() = default; +DispSync::~DispSync() = default; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h new file mode 100644 index 0000000000..34e71cb32d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h @@ -0,0 +1,51 @@ +/* + * 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 <utils/String8.h> +#include "Scheduler/DispSync.h" + +namespace android { +namespace mock { + +class DispSync : public android::DispSync { +public: + DispSync(); + ~DispSync() override; + + MOCK_METHOD0(reset, void()); + MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&)); + MOCK_METHOD0(beginResync, void()); + MOCK_METHOD1(addResyncSample, bool(nsecs_t)); + MOCK_METHOD0(endResync, void()); + MOCK_METHOD1(setPeriod, void(nsecs_t)); + MOCK_METHOD0(getPeriod, nsecs_t()); + MOCK_METHOD1(setRefreshSkipCount, void(int)); + MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*)); + MOCK_METHOD1(removeEventListener, status_t(Callback*)); + MOCK_METHOD2(changePhaseOffset, status_t(Callback*, nsecs_t)); + MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int)); + MOCK_METHOD1(setIgnorePresentFences, void(bool)); + MOCK_METHOD0(expectedPresentTime, nsecs_t()); + + MOCK_CONST_METHOD1(dump, void(String8&)); +}; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h index 8ac09a962d..6ef352a548 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h @@ -18,7 +18,7 @@ #include <gmock/gmock.h> -#include "EventControlThread.h" +#include "Scheduler/EventControlThread.h" namespace android { namespace mock { diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index e6ea6634c0..ad2463dac2 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -18,7 +18,7 @@ #include <gmock/gmock.h> -#include "EventThread.h" +#include "Scheduler/EventThread.h" namespace android { namespace mock { @@ -31,7 +31,7 @@ public: MOCK_CONST_METHOD0(createEventConnection, sp<BnDisplayEventConnection>()); MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); - MOCK_METHOD2(onHotplugReceived, void(int, bool)); + MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool)); MOCK_CONST_METHOD1(dump, void(String8&)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index cf07cf7ba9..f2f36758f9 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h @@ -18,7 +18,7 @@ #include <gmock/gmock.h> -#include "MessageQueue.h" +#include "Scheduler/MessageQueue.h" namespace android { namespace mock { @@ -30,6 +30,7 @@ public: MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&)); MOCK_METHOD1(setEventThread, void(android::EventThread*)); + MOCK_METHOD1(setEventConnection, void(const sp<BnDisplayEventConnection>& connection)); MOCK_METHOD0(waitMessage, void()); MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t)); MOCK_METHOD0(invalidate, void()); diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp index 25ff39bbd1..9cc6d38397 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2018 The Android Open Source Project - * + * Copyright 2018 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,12 +16,10 @@ #include "mock/MockNativeWindowSurface.h" -namespace android { -namespace mock { +namespace android::surfaceflinger::mock { // Explicit default instantiation is recommended. NativeWindowSurface::NativeWindowSurface() = default; NativeWindowSurface::~NativeWindowSurface() = default; -} // namespace mock -} // namespace android +} // namespace android::surfaceflinger::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h index 88d1a9fc51..7bc1151d61 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h +++ b/services/surfaceflinger/tests/unittests/mock/MockNativeWindowSurface.h @@ -20,19 +20,17 @@ #include <system/window.h> // for ANativeWindow -#include "SurfaceFlinger.h" // for base NativeWindowSurface +#include "NativeWindowSurface.h" -namespace android { -namespace mock { +namespace android::surfaceflinger::mock { -class NativeWindowSurface : public android::NativeWindowSurface { +class NativeWindowSurface : public surfaceflinger::NativeWindowSurface { public: NativeWindowSurface(); - ~NativeWindowSurface(); + ~NativeWindowSurface() override; MOCK_CONST_METHOD0(getNativeWindow, sp<ANativeWindow>()); MOCK_METHOD0(preallocateBuffers, void()); }; -} // namespace mock -} // namespace android +} // namespace android::surfaceflinger::mock diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp index a98beceebd..af54df6d2b 100644 --- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp +++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp @@ -16,8 +16,10 @@ #include "mock/RenderEngine/MockRenderEngine.h" +#include <ui/Region.h> + namespace android { -namespace RE { +namespace renderengine { namespace mock { // Explicit default instantiation is recommended. @@ -30,6 +32,9 @@ Surface::~Surface() = default; Image::Image() = default; Image::~Image() = default; +Framebuffer::Framebuffer() = default; +Framebuffer::~Framebuffer() = default; + } // namespace mock -} // namespace RE +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h index ac082933a3..afca63ad39 100644 --- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h +++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h @@ -17,50 +17,54 @@ #pragma once #include <gmock/gmock.h> - -#include "RenderEngine/Image.h" -#include "RenderEngine/Mesh.h" -#include "RenderEngine/RenderEngine.h" -#include "RenderEngine/Surface.h" -#include "RenderEngine/Texture.h" +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <renderengine/Mesh.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/Surface.h> +#include <renderengine/Texture.h> +#include <ui/GraphicBuffer.h> namespace android { -namespace RE { +namespace renderengine { namespace mock { -class RenderEngine : public RE::RenderEngine { +class RenderEngine : public renderengine::RenderEngine { public: RenderEngine(); ~RenderEngine() override; - MOCK_METHOD0(createSurface, std::unique_ptr<RE::Surface>()); - MOCK_METHOD0(createImage, std::unique_ptr<RE::Image>()); + MOCK_METHOD0(createFramebuffer, std::unique_ptr<Framebuffer>()); + MOCK_METHOD0(createSurface, std::unique_ptr<renderengine::Surface>()); + MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>()); MOCK_CONST_METHOD0(primeCache, void()); MOCK_METHOD1(dump, void(String8&)); - MOCK_CONST_METHOD0(supportsImageCrop, bool()); + MOCK_CONST_METHOD0(useNativeFenceSync, bool()); + MOCK_CONST_METHOD0(useWaitSync, bool()); MOCK_CONST_METHOD0(isCurrent, bool()); - MOCK_METHOD1(setCurrentSurface, bool(const RE::Surface&)); + MOCK_METHOD1(setCurrentSurface, bool(const renderengine::Surface&)); MOCK_METHOD0(resetCurrentSurface, void()); MOCK_METHOD0(flush, base::unique_fd()); MOCK_METHOD0(finish, bool()); MOCK_METHOD1(waitFence, bool(base::unique_fd*)); bool waitFence(base::unique_fd fd) override { return waitFence(&fd); }; MOCK_METHOD4(clearWithColor, void(float, float, float, float)); - MOCK_METHOD6(fillRegionWithColor, void(const Region&, uint32_t, float, float, float, float)); - MOCK_METHOD4(setScissor, void(uint32_t, uint32_t, uint32_t, uint32_t)); + MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float)); + MOCK_METHOD1(setScissor, void(const Rect&)); MOCK_METHOD0(disableScissor, void()); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); - MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const RE::Image&)); - MOCK_METHOD5(readPixels, void(size_t, size_t, size_t, size_t, uint32_t*)); + MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); MOCK_CONST_METHOD0(checkErrors, void()); - MOCK_METHOD6(setViewportAndProjection, - void(size_t, size_t, Rect, size_t, bool, Transform::orientation_flags)); + MOCK_METHOD4(setViewportAndProjection, + void(size_t, size_t, Rect, ui::Transform::orientation_flags)); MOCK_METHOD4(setupLayerBlending, void(bool, bool, bool, const half4&)); MOCK_METHOD1(setupLayerTexturing, void(const Texture&)); MOCK_METHOD0(setupLayerBlackedOut, void()); MOCK_METHOD4(setupFillWithColor, void(float, float, float, float)); - MOCK_METHOD1(setupColorTransform, void(const mat4&)); + MOCK_METHOD1(setColorTransform, void(const mat4&)); MOCK_METHOD1(setSaturationMatrix, void(const mat4&)); MOCK_METHOD0(disableTexturing, void()); MOCK_METHOD0(disableBlending, void()); @@ -68,15 +72,17 @@ public: MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace)); MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace)); MOCK_METHOD1(setDisplayMaxLuminance, void(const float)); - MOCK_METHOD2(bindNativeBufferAsFrameBuffer, - void(ANativeWindowBuffer*, RE::BindNativeBufferAsFramebuffer*)); - MOCK_METHOD1(unbindNativeBufferAsFrameBuffer, void(RE::BindNativeBufferAsFramebuffer*)); + MOCK_METHOD1(bindFrameBuffer, status_t(Framebuffer*)); + MOCK_METHOD1(unbindFrameBuffer, void(Framebuffer*)); MOCK_METHOD1(drawMesh, void(const Mesh&)); MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); + MOCK_CONST_METHOD4(drawLayers, + status_t(const DisplaySettings&, const std::vector<LayerSettings>&, + ANativeWindowBuffer* const, base::unique_fd*)); }; -class Surface : public RE::Surface { +class Surface : public renderengine::Surface { public: Surface(); ~Surface() override; @@ -89,20 +95,27 @@ public: MOCK_CONST_METHOD0(queryGreenSize, int32_t()); MOCK_CONST_METHOD0(queryBlueSize, int32_t()); MOCK_CONST_METHOD0(queryAlphaSize, int32_t()); - MOCK_CONST_METHOD0(queryWidth, int32_t()); - MOCK_CONST_METHOD0(queryHeight, int32_t()); + MOCK_CONST_METHOD0(getWidth, int32_t()); + MOCK_CONST_METHOD0(getHeight, int32_t()); }; -class Image : public RE::Image { +class Image : public renderengine::Image { public: Image(); ~Image() override; - MOCK_METHOD4(setNativeWindowBuffer, - bool(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth, - int32_t cropHeight)); + MOCK_METHOD2(setNativeWindowBuffer, + bool(ANativeWindowBuffer* buffer, bool isProtected)); +}; + +class Framebuffer : public renderengine::Framebuffer { +public: + Framebuffer(); + ~Framebuffer() override; + + MOCK_METHOD1(setNativeWindowBuffer, bool(ANativeWindowBuffer*)); }; } // namespace mock -} // namespace RE +} // namespace renderengine } // namespace android diff --git a/services/thermalservice/Android.bp b/services/thermalservice/Android.bp index d754560ea5..2812c133af 100644 --- a/services/thermalservice/Android.bp +++ b/services/thermalservice/Android.bp @@ -40,11 +40,13 @@ cc_binary { include_dirs: ["frameworks/native"], shared_libs: [ + "libbase", "libthermalservice", "libbinder", "libutils", "libthermalcallback", "android.hardware.thermal@1.1", + "android.hardware.thermal@2.0", "libhidlbase", "libhidltransport", "liblog", diff --git a/services/thermalservice/ThermalService.cpp b/services/thermalservice/ThermalService.cpp index 6e09a83872..b1a80de9a4 100644 --- a/services/thermalservice/ThermalService.cpp +++ b/services/thermalservice/ThermalService.cpp @@ -15,11 +15,16 @@ */ #include "ThermalService.h" -#include <android/os/IThermalService.h> +#include <android-base/file.h> +#include <android-base/stringprintf.h> #include <android/os/IThermalEventListener.h> +#include <android/os/IThermalService.h> #include <android/os/Temperature.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/PermissionCache.h> +#include <log/log.h> +#include <private/android_filesystem_config.h> #include <utils/Errors.h> #include <utils/Mutex.h> #include <utils/String16.h> @@ -28,6 +33,35 @@ namespace android { namespace os { /** + * Dump thermal service + * @param fd file descriptor for dumping + * @param args not used + */ +status_t ThermalService::dump(int fd, const Vector<String16>& /* args */) { + status_t ret = OK; + std::string result; + const IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { + result = android::base:: + StringPrintf("Permission Denial: can't dump ThermalService from pid=%d, uid=%d\n", + pid, uid); + ret = PERMISSION_DENIED; + } else { + Mutex::Autolock _l(mListenersLock); + result = android::base::StringPrintf("ThermalEventListener registered: %d\n", + (int)mListeners.size()); + } + if (!android::base::WriteStringToFd(result, fd)) { + SLOGE("Failed to dump fd: %d", fd); + ret = FDS_NOT_ALLOWED; + } + return ret; +} + +/** * Notify registered listeners of a thermal throttling start/stop event. * @param temperature the temperature at which the event was generated */ @@ -62,8 +96,9 @@ binder::Status ThermalService::isThrottling(bool* _aidl_return) { binder::Status ThermalService::registerThermalEventListener( const sp<IThermalEventListener>& listener) { { - if (listener == NULL) + if (listener == NULL) { return binder::Status::ok(); + } Mutex::Autolock _l(mListenersLock); // check whether this is a duplicate for (size_t i = 0; i < mListeners.size(); i++) { @@ -87,8 +122,9 @@ binder::Status ThermalService::registerThermalEventListener( */ binder::Status ThermalService::unregisterThermalEventListener( const sp<IThermalEventListener>& listener) { - if (listener == NULL) + if (listener == NULL) { return binder::Status::ok(); + } Mutex::Autolock _l(mListenersLock); for (size_t i = 0; i < mListeners.size(); i++) { if (IInterface::asBinder(mListeners[i]) == diff --git a/services/thermalservice/ThermalService.h b/services/thermalservice/ThermalService.h index 17dfcbcd37..d3da90039d 100644 --- a/services/thermalservice/ThermalService.h +++ b/services/thermalservice/ThermalService.h @@ -34,6 +34,7 @@ public: void publish(const sp<ThermalService>& service); binder::Status notifyThrottling( const bool isThrottling, const Temperature& temperature); + status_t dump(int fd, const Vector<String16>& args) override; private: Mutex mListenersLock; diff --git a/services/thermalservice/libthermalcallback/Android.bp b/services/thermalservice/libthermalcallback/Android.bp index e98506e47e..312579c8ca 100644 --- a/services/thermalservice/libthermalcallback/Android.bp +++ b/services/thermalservice/libthermalcallback/Android.bp @@ -2,6 +2,7 @@ cc_library_shared { name: "libthermalcallback", srcs: [ "ThermalCallback.cpp", + "ThermalChangedCallback.cpp", ], cflags: [ "-Wall", @@ -10,6 +11,7 @@ cc_library_shared { include_dirs: ["frameworks/native"], shared_libs: [ "android.hardware.thermal@1.1", + "android.hardware.thermal@2.0", "libhidlbase", "libhidltransport", "liblog", diff --git a/services/thermalservice/libthermalcallback/ThermalCallback.cpp b/services/thermalservice/libthermalcallback/ThermalCallback.cpp index 5e094fa259..0f3132c284 100644 --- a/services/thermalservice/libthermalcallback/ThermalCallback.cpp +++ b/services/thermalservice/libthermalcallback/ThermalCallback.cpp @@ -1,11 +1,11 @@ #define LOG_TAG "android.hardware.thermal.thermalcallback@1.1-impl" #include <log/log.h> -#include "ThermalCallback.h" -#include "services/thermalservice/ThermalService.h" -#include <math.h> #include <android/os/Temperature.h> #include <hardware/thermal.h> +#include <cmath> +#include "ThermalCallback.h" +#include "services/thermalservice/ThermalService.h" namespace android { namespace hardware { @@ -57,7 +57,7 @@ Return<void> ThermalCallback::notifyThrottling( android::os::Temperature thermal_svc_temp(value, type); mThermalService->notifyThrottling(isThrottling, thermal_svc_temp); } else { - ALOGE("IThermalService binder service not created, drop throttling event"); + SLOGE("IThermalService binder service not created, drop throttling event"); } return Void(); } diff --git a/services/thermalservice/libthermalcallback/ThermalChangedCallback.cpp b/services/thermalservice/libthermalcallback/ThermalChangedCallback.cpp new file mode 100644 index 0000000000..bb48387c00 --- /dev/null +++ b/services/thermalservice/libthermalcallback/ThermalChangedCallback.cpp @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#define LOG_TAG "android.hardware.thermal.thermalchangedcallback@2.0-impl" +#include <log/log.h> + +#include <android/os/Temperature.h> +#include <hardware/thermal.h> +#include <cmath> +#include "ThermalChangedCallback.h" +#include "services/thermalservice/ThermalService.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::thermal::V2_0::TemperatureType; +using ::android::hardware::thermal::V2_0::ThrottlingSeverity; +using ::android::os::ThermalService; + +// Register a binder ThermalService object for sending events +void ThermalChangedCallback::registerThermalService(sp<ThermalService> thermalService) { + mThermalService = thermalService; +} + +// Methods from IThermalChangedCallback::V2_0 follow. +Return<void> ThermalChangedCallback::notifyThrottling( + const android::hardware::thermal::V2_0::Temperature& temperature) { + // Convert HIDL IThermal Temperature to binder IThermalService Temperature. + if (mThermalService != nullptr) { + float value = NAN; + int type = DEVICE_TEMPERATURE_UNKNOWN; + + switch (temperature.type) { + case TemperatureType::CPU: + type = DEVICE_TEMPERATURE_CPU; + break; + case TemperatureType::GPU: + type = DEVICE_TEMPERATURE_GPU; + break; + case TemperatureType::BATTERY: + type = DEVICE_TEMPERATURE_BATTERY; + break; + case TemperatureType::SKIN: + type = DEVICE_TEMPERATURE_SKIN; + break; + case TemperatureType::UNKNOWN: + default: + type = DEVICE_TEMPERATURE_UNKNOWN; + break; + } + bool isThrottling = (static_cast<size_t>(temperature.throttlingStatus) >= + static_cast<size_t>(ThrottlingSeverity::SEVERE)) + ? true + : false; + value = temperature.value == UNKNOWN_TEMPERATURE ? NAN : + temperature.value; + android::os::Temperature thermal_svc_temp(value, type); + mThermalService->notifyThrottling(isThrottling, thermal_svc_temp); + } else { + SLOGE("IThermalService binder service not created, drop throttling event"); + } + return Void(); +} + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android diff --git a/services/thermalservice/libthermalcallback/ThermalChangedCallback.h b/services/thermalservice/libthermalcallback/ThermalChangedCallback.h new file mode 100644 index 0000000000..03de0494c3 --- /dev/null +++ b/services/thermalservice/libthermalcallback/ThermalChangedCallback.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_THERMAL_V1_1_THERMALCHANGEDCALLBACK_H +#define ANDROID_HARDWARE_THERMAL_V1_1_THERMALCHANGEDCALLBACK_H + +#include <android/hardware/thermal/2.0/IThermalChangedCallback.h> +#include <android/hardware/thermal/2.0/types.h> +#include <android/os/Temperature.h> +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> +#include "services/thermalservice/ThermalService.h" + +namespace android { +namespace hardware { +namespace thermal { +namespace V2_0 { +namespace implementation { + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::os::ThermalService; + +class ThermalChangedCallback : public IThermalChangedCallback { +public: + // Register a binder ThermalService object for sending events + void registerThermalService(sp<ThermalService> thermalService); + + // Methods from I ThermalChangedCallback::V2_0 follow. + Return<void> notifyThrottling( + const android::hardware::thermal::V2_0::Temperature& temperature) override; + +private: + // Our registered binder ThermalService object to use for sending events + sp<android::os::ThermalService> mThermalService; +}; + +} // namespace implementation +} // namespace V2_0 +} // namespace thermal +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_THERMAL_V1_1_THERMALCHANGEDCALLBACK_H diff --git a/services/thermalservice/thermalservice.rc b/services/thermalservice/thermalservice.rc index 94c2c78df3..5e201709f8 100644 --- a/services/thermalservice/thermalservice.rc +++ b/services/thermalservice/thermalservice.rc @@ -2,3 +2,5 @@ service thermalservice /system/bin/thermalserviced class core user system group system + onrestart restart zygote + writepid /dev/cpuset/foreground/tasks diff --git a/services/thermalservice/thermalserviced.cpp b/services/thermalservice/thermalserviced.cpp index 8e2726669f..0bfaaffbfc 100644 --- a/services/thermalservice/thermalserviced.cpp +++ b/services/thermalservice/thermalserviced.cpp @@ -17,93 +17,155 @@ #define LOG_TAG "thermalserviced" #include <log/log.h> -#include "thermalserviced.h" #include "ThermalService.h" #include "libthermalcallback/ThermalCallback.h" +#include "libthermalcallback/ThermalChangedCallback.h" +#include "thermalserviced.h" #include <android/hardware/thermal/1.1/IThermal.h> +#include <android/hardware/thermal/2.0/IThermal.h> +#include <android/hardware/thermal/2.0/types.h> + #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <hidl/HidlTransportSupport.h> using namespace android; -using ::android::hardware::thermal::V1_1::IThermal; -using ::android::hardware::thermal::V1_0::Temperature; -using ::android::hardware::thermal::V1_1::IThermalCallback; -using ::android::hardware::thermal::V1_1::implementation::ThermalCallback; +using IThermal1_1 = ::android::hardware::thermal::V1_1::IThermal; +using IThermal2_0 = ::android::hardware::thermal::V2_0::IThermal; using ::android::hardware::configureRpcThreadpool; using ::android::hardware::hidl_death_recipient; +using ::android::hardware::Return; +using ::android::hardware::thermal::V1_0::ThermalStatus; +using ::android::hardware::thermal::V1_0::ThermalStatusCode; +using ::android::hardware::thermal::V1_1::IThermalCallback; +using ::android::hardware::thermal::V1_1::implementation::ThermalCallback; +using ::android::hardware::thermal::V2_0::IThermalChangedCallback; +using ::android::hardware::thermal::V2_0::TemperatureType; using ::android::hidl::base::V1_0::IBase; using ::android::os::ThermalService; -template<typename T> -using Return = hardware::Return<T>; - namespace { // Our thermalserviced main object ThermalServiceDaemon* gThermalServiceDaemon; -// Thermal HAL client -sp<IThermal> gThermalHal = nullptr; +// Thermal HAL 1.1 client +sp<IThermal1_1> gThermalHal1_1 = nullptr; +// Thermal HAL 2.0 client +sp<IThermal2_0> gThermalHal2_0 = nullptr; // Binder death notifier informing of Thermal HAL death. struct ThermalServiceDeathRecipient : hidl_death_recipient { virtual void serviceDied( uint64_t cookie __unused, const wp<IBase>& who __unused) { - gThermalHal = nullptr; - ALOGE("IThermal HAL died"); + SLOGE("IThermal HAL died"); + gThermalHal1_1 = nullptr; + gThermalHal2_0 = nullptr; gThermalServiceDaemon->getThermalHal(); } }; -sp<ThermalServiceDeathRecipient> gThermalHalDied = nullptr; - } // anonymous namespace void ThermalServiceDaemon::thermalServiceStartup() { - // Binder IThermalService startup + // Binder IThermal1_1Service startup mThermalService = new android::os::ThermalService; mThermalService->publish(mThermalService); - // Register IThermalService object with IThermalCallback - if (mThermalCallback != nullptr) - mThermalCallback->registerThermalService(mThermalService); + // Register IThermalService object to ThermalHAL callback + if (mThermalCallback_2_0 != nullptr) { + mThermalCallback_2_0->registerThermalService(mThermalService); + } else if (mThermalCallback_1_1 != nullptr) { + mThermalCallback_1_1->registerThermalService(mThermalService); + } IPCThreadState::self()->joinThreadPool(); } // Lookup Thermal HAL, register death notifier, register our // ThermalCallback with the Thermal HAL. void ThermalServiceDaemon::getThermalHal() { - gThermalHal = IThermal::getService(); - if (gThermalHal == nullptr) { - ALOGW("Unable to get Thermal HAL V1.1, vendor thermal event notification not available"); - return; - } - + static sp<ThermalServiceDeathRecipient> gThermalHalDied = nullptr; // Binder death notifier for Thermal HAL - if (gThermalHalDied == nullptr) + if (gThermalHalDied == nullptr) { gThermalHalDied = new ThermalServiceDeathRecipient(); + } - if (gThermalHalDied != nullptr) - gThermalHal->linkToDeath(gThermalHalDied, 0x451F /* cookie */); + gThermalHal2_0 = IThermal2_0::getService(); + if (gThermalHal2_0 == nullptr) { + SLOGW("Unable to get Thermal HAL V2.0, fallback to 1.1"); + gThermalHal1_1 = IThermal1_1::getService(); + if (gThermalHal1_1 == nullptr) { + SLOGW("Unable to get Thermal HAL V1.1, vendor thermal event " + "notification not available"); + return; + } + if (gThermalHalDied != nullptr) { + gThermalHal1_1->linkToDeath(gThermalHalDied, 0x451F /* cookie */); + } + + if (mThermalCallback_1_1 != nullptr) { + Return<void> ret = gThermalHal1_1->registerThermalCallback(mThermalCallback_1_1); + if (!ret.isOk()) { + SLOGE("registerThermalCallback failed, status: %s", ret.description().c_str()); + } + } + } else { + if (gThermalHalDied != nullptr) { + gThermalHal2_0->linkToDeath(gThermalHalDied, 0x451F /* cookie */); + } + + if (mThermalCallback_2_0 != nullptr) { + Return<void> ret = + gThermalHal2_0 + ->registerThermalChangedCallback(mThermalCallback_2_0, false, + TemperatureType::SKIN, // not used + [](ThermalStatus status) { + if (ThermalStatusCode::SUCCESS != + status.code) { + SLOGE("registerThermalChangedC" + "allback failed, " + "status: %s", + status.debugMessage + .c_str()); + } + }); + if (!ret.isOk()) { + SLOGE("registerThermalChangedCallback failed, status: %s", + ret.description().c_str()); + } + } + } +} - if (mThermalCallback != nullptr) { - Return<void> ret = gThermalHal->registerThermalCallback( - mThermalCallback); - if (!ret.isOk()) - ALOGE("registerThermalCallback failed, status: %s", - ret.description().c_str()); +ThermalServiceDaemon::~ThermalServiceDaemon() { + if (mThermalCallback_2_0 != nullptr && gThermalHal2_0 != nullptr) { + Return<void> ret = + gThermalHal2_0 + ->unregisterThermalChangedCallback( + mThermalCallback_2_0, + [](ThermalStatus status) { + if (ThermalStatusCode::SUCCESS != + status.code) { + SLOGE("unregisterThermalChangedCallback failed, status: %s", + status.debugMessage + .c_str()); + } + }); + if (!ret.isOk()) { + SLOGE("unregisterThermalChangedCallback failed, status: %s", ret.description().c_str()); + } } } void ThermalServiceDaemon::thermalCallbackStartup() { - // HIDL IThermalCallback startup + // HIDL IThermal Callback startup // Need at least 2 threads in thread pool since we wait for dead HAL // to come back on the binder death notification thread and we need // another thread for the incoming service now available call. configureRpcThreadpool(2, false /* callerWillJoin */); - mThermalCallback = new ThermalCallback(); - // Lookup Thermal HAL and register our ThermalCallback. + mThermalCallback_1_1 = new ThermalCallback(); + mThermalCallback_2_0 = new ThermalChangedCallback(); + // Lookup Thermal HAL 1.1 and 2.0 to register our Callback. getThermalHal(); } diff --git a/services/thermalservice/thermalserviced.h b/services/thermalservice/thermalserviced.h index 309e2fe422..ff7a483071 100644 --- a/services/thermalservice/thermalserviced.h +++ b/services/thermalservice/thermalserviced.h @@ -19,22 +19,26 @@ #include "ThermalService.h" #include "libthermalcallback/ThermalCallback.h" +#include "libthermalcallback/ThermalChangedCallback.h" using namespace android; using ::android::hardware::thermal::V1_0::Temperature; using ::android::hardware::thermal::V1_1::implementation::ThermalCallback; +using ::android::hardware::thermal::V2_0::implementation::ThermalChangedCallback; using ::android::os::ThermalService; class ThermalServiceDaemon { public: + ~ThermalServiceDaemon(); void thermalServiceStartup(); void thermalCallbackStartup(); void getThermalHal(); - ThermalServiceDaemon() {}; + ThermalServiceDaemon(){}; private: sp<ThermalService> mThermalService; - sp<ThermalCallback> mThermalCallback; + sp<ThermalCallback> mThermalCallback_1_1; + sp<ThermalChangedCallback> mThermalCallback_2_0; }; #endif // ANDROID_THERMALSERVICE_THERMALSERVICED_H diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp index 6122846540..5debd3dfb7 100644 --- a/services/vr/bufferhubd/Android.bp +++ b/services/vr/bufferhubd/Android.bp @@ -12,45 +12,68 @@ // See the License for the specific language governing permissions and // limitations under the License. -sourceFiles = [ - "buffer_hub.cpp", - "bufferhubd.cpp", - "consumer_channel.cpp", - "producer_channel.cpp", - "detached_buffer_channel.cpp", - "consumer_queue_channel.cpp", - "producer_queue_channel.cpp", -] - -headerLibraries = ["libdvr_headers"] - -staticLibraries = [ - "libperformance", - "libbufferhub", -] - sharedLibraries = [ "libbase", "libbinder", "libcutils", + "libgtest_prod", + "libgui", "liblog", + "libpdx_default_transport", "libsync", - "libutils", - "libgui", "libui", - "libpdx_default_transport", + "libutils", ] +cc_library_static { + name: "libbufferhubd", + srcs: [ + "buffer_channel.cpp", + "buffer_client.cpp", + "buffer_hub.cpp", + "buffer_hub_binder.cpp", + "buffer_node.cpp", + "consumer_channel.cpp", + "consumer_queue_channel.cpp", + "IBufferHub.cpp", + "producer_channel.cpp", + "producer_queue_channel.cpp", + ], + cflags: [ + "-DLOG_TAG=\"libbufferhubd\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + ], + export_include_dirs: ["include"], + header_libs: [ + "libdvr_headers", + "libnativewindow_headers", + ], + shared_libs: sharedLibraries, + static_libs: [ + "libbufferhub", + ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + cc_binary { - srcs: sourceFiles, + srcs: ["bufferhubd.cpp"], cflags: [ "-DLOG_TAG=\"bufferhubd\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], - header_libs: headerLibraries, - static_libs: staticLibraries, + header_libs: ["libdvr_headers"], shared_libs: sharedLibraries, + static_libs: [ + "libbufferhub", + "libbufferhubd", + "libperformance", + ], name: "bufferhubd", init_rc: ["bufferhubd.rc"], } + +subdirs = ["tests"]
\ No newline at end of file diff --git a/services/vr/bufferhubd/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp new file mode 100644 index 0000000000..022a9ccd0b --- /dev/null +++ b/services/vr/bufferhubd/IBufferHub.cpp @@ -0,0 +1,110 @@ +#include <log/log.h> +#include <private/dvr/IBufferHub.h> + +namespace android { +namespace dvr { + +class BpBufferHub : public BpInterface<IBufferHub> { + public: + explicit BpBufferHub(const sp<IBinder>& impl) + : BpInterface<IBufferHub>(impl) {} + + sp<IBufferClient> createBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t format, + uint64_t usage, + uint64_t user_metadata_size) override; + + status_t importBuffer(uint64_t token, sp<IBufferClient>* outClient) override; +}; + +IMPLEMENT_META_INTERFACE(BufferHub, "android.dvr.IBufferHub"); + +// Transaction code +enum { + CREATE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + IMPORT_BUFFER, +}; + +sp<IBufferClient> BpBufferHub::createBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, + uint32_t format, uint64_t usage, + uint64_t user_metadata_size) { + Parcel data, reply; + status_t ret = NO_ERROR; + ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor()); + ret |= data.writeUint32(width); + ret |= data.writeUint32(height); + ret |= data.writeUint32(layer_count); + ret |= data.writeUint32(format); + ret |= data.writeUint64(usage); + ret |= data.writeUint64(user_metadata_size); + + if (ret != NO_ERROR) { + ALOGE("BpBufferHub::createBuffer: failed to write into parcel"); + return nullptr; + } + + ret = remote()->transact(CREATE_BUFFER, data, &reply); + if (ret == NO_ERROR) { + return interface_cast<IBufferClient>(reply.readStrongBinder()); + } else { + ALOGE("BpBufferHub::createBuffer: failed to transact; errno=%d", ret); + return nullptr; + } +} + +status_t BpBufferHub::importBuffer(uint64_t token, + sp<IBufferClient>* outClient) { + Parcel data, reply; + status_t ret = NO_ERROR; + ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor()); + ret |= data.writeUint64(token); + if (ret != NO_ERROR) { + ALOGE("BpBufferHub::importBuffer: failed to write into parcel"); + return ret; + } + + ret = remote()->transact(IMPORT_BUFFER, data, &reply); + if (ret == NO_ERROR) { + *outClient = interface_cast<IBufferClient>(reply.readStrongBinder()); + return NO_ERROR; + } else { + ALOGE("BpBufferHub::importBuffer: failed to transact; errno=%d", ret); + return ret; + } +} + +status_t BnBufferHub::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + switch (code) { + case CREATE_BUFFER: { + CHECK_INTERFACE(IBufferHub, data, reply); + uint32_t width = data.readUint32(); + uint32_t height = data.readUint32(); + uint32_t layer_count = data.readUint32(); + uint32_t format = data.readUint32(); + uint64_t usage = data.readUint64(); + uint64_t user_metadata_size = data.readUint64(); + sp<IBufferClient> ret = createBuffer(width, height, layer_count, format, + usage, user_metadata_size); + return reply->writeStrongBinder(IInterface::asBinder(ret)); + } + case IMPORT_BUFFER: { + CHECK_INTERFACE(IBufferHub, data, reply); + uint64_t token = data.readUint64(); + sp<IBufferClient> client; + status_t ret = importBuffer(token, &client); + if (ret == NO_ERROR) { + return reply->writeStrongBinder(IInterface::asBinder(client)); + } else { + return ret; + } + } + default: + // Should not reach except binder defined transactions such as dumpsys + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace dvr +} // namespace android
\ No newline at end of file diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp new file mode 100644 index 0000000000..589b31a285 --- /dev/null +++ b/services/vr/bufferhubd/buffer_channel.cpp @@ -0,0 +1,136 @@ +#include <errno.h> +#include <private/dvr/buffer_channel.h> +#include <private/dvr/producer_channel.h> + +using android::pdx::BorrowedHandle; +using android::pdx::ErrorStatus; +using android::pdx::Message; +using android::pdx::RemoteChannelHandle; +using android::pdx::Status; +using android::pdx::rpc::DispatchRemoteMethod; + +namespace android { +namespace dvr { + +BufferChannel::BufferChannel(BufferHubService* service, int buffer_id, + uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t format, + uint64_t usage, size_t user_metadata_size) + : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType) { + buffer_node_ = std::make_shared<BufferNode>( + width, height, layer_count, format, usage, user_metadata_size); + if (!buffer_node_->IsValid()) { + ALOGE("BufferChannel::BufferChannel: Failed to create BufferNode."); + return; + } + client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask(); +} + +BufferChannel::BufferChannel(BufferHubService* service, int buffer_id, + int channel_id, + std::shared_ptr<BufferNode> buffer_node) + : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType), + buffer_node_(buffer_node) { + client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask(); + if (client_state_mask_ == 0ULL) { + ALOGE("BufferChannel::BufferChannel: %s", strerror(errno)); + buffer_node_ = nullptr; + } +} + +BufferChannel::~BufferChannel() { + ALOGD_IF(TRACE, "BufferChannel::~BufferChannel: channel_id=%d buffer_id=%d.", + channel_id(), buffer_id()); + if (client_state_mask_ != 0ULL) { + buffer_node_->RemoveClientsBitFromMask(client_state_mask_); + } + Hangup(); +} + +BufferHubChannel::BufferInfo BufferChannel::GetBufferInfo() const { + return BufferInfo( + buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width, + buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers, + buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage, + /*pending_count=*/0, /*state=*/0, /*signaled_mask=*/0, + /*index=*/0); +} + +void BufferChannel::HandleImpulse(Message& /*message*/) { + ATRACE_NAME("BufferChannel::HandleImpulse"); +} + +bool BufferChannel::HandleMessage(Message& message) { + ATRACE_NAME("BufferChannel::HandleMessage"); + switch (message.GetOp()) { + case DetachedBufferRPC::Import::Opcode: + DispatchRemoteMethod<DetachedBufferRPC::Import>( + *this, &BufferChannel::OnImport, message); + return true; + + case DetachedBufferRPC::Duplicate::Opcode: + DispatchRemoteMethod<DetachedBufferRPC::Duplicate>( + *this, &BufferChannel::OnDuplicate, message); + return true; + + default: + return false; + } +} + +Status<BufferTraits<BorrowedHandle>> BufferChannel::OnImport( + Message& /*message*/) { + ATRACE_NAME("BufferChannel::OnImport"); + ALOGD_IF(TRACE, "BufferChannel::OnImport: buffer=%d.", buffer_id()); + + // TODO(b/112057680) Move away from the GraphicBuffer-based IonBuffer. + return BufferTraits<BorrowedHandle>{ + /*buffer_handle=*/buffer_node_->buffer_handle(), + /*metadata_handle=*/buffer_node_->metadata().ashmem_handle().Borrow(), + /*id=*/buffer_id(), + /*client_state_mask=*/client_state_mask_, + /*metadata_size=*/buffer_node_->metadata().metadata_size(), + /*width=*/buffer_node_->buffer_desc().width, + /*height=*/buffer_node_->buffer_desc().height, + /*layer_count=*/buffer_node_->buffer_desc().layers, + /*format=*/buffer_node_->buffer_desc().format, + /*usage=*/buffer_node_->buffer_desc().usage, + /*stride=*/buffer_node_->buffer_desc().stride, + /*acquire_fence_fd=*/BorrowedHandle{}, + /*released_fence_fd=*/BorrowedHandle{}}; +} + +Status<RemoteChannelHandle> BufferChannel::OnDuplicate(Message& message) { + ATRACE_NAME("BufferChannel::OnDuplicate"); + ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.", buffer_id()); + + int channel_id; + auto status = message.PushChannel(0, nullptr, &channel_id); + if (!status.ok()) { + ALOGE("BufferChannel::OnDuplicate: Failed to push buffer channel: %s", + status.GetErrorMessage().c_str()); + return ErrorStatus(ENOMEM); + } + + auto channel = std::shared_ptr<BufferChannel>( + new BufferChannel(service(), buffer_id(), channel_id, buffer_node_)); + if (!channel->IsValid()) { + ALOGE("BufferChannel::OnDuplicate: Invalid buffer. %s", strerror(errno)); + return ErrorStatus(EINVAL); + } + + const auto channel_status = + service()->SetChannel(channel_id, std::move(channel)); + if (!channel_status) { + // Technically, this should never fail, as we just pushed the channel. Note + // that LOG_FATAL will be stripped out in non-debug build. + LOG_FATAL( + "BufferChannel::OnDuplicate: Failed to set new buffer channel: %s.", + channel_status.GetErrorMessage().c_str()); + } + + return status; +} + +} // namespace dvr +} // namespace android diff --git a/services/vr/bufferhubd/buffer_client.cpp b/services/vr/bufferhubd/buffer_client.cpp new file mode 100644 index 0000000000..f14faf73c0 --- /dev/null +++ b/services/vr/bufferhubd/buffer_client.cpp @@ -0,0 +1,18 @@ +#include <private/dvr/buffer_client.h> +#include <private/dvr/buffer_hub_binder.h> + +namespace android { +namespace dvr { + +status_t BufferClient::duplicate(uint64_t* outToken) { + if (!buffer_node_) { + // Should never happen + ALOGE("BufferClient::duplicate: node is missing."); + return UNEXPECTED_NULL; + } + return service_->registerToken(std::weak_ptr<BufferNode>(buffer_node_), + outToken); +} + +} // namespace dvr +} // namespace android
\ No newline at end of file diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp index e57c8edfe3..b73c47de6e 100644 --- a/services/vr/bufferhubd/buffer_hub.cpp +++ b/services/vr/bufferhubd/buffer_hub.cpp @@ -1,9 +1,5 @@ -#include "buffer_hub.h" - #include <inttypes.h> -#include <log/log.h> #include <poll.h> -#include <utils/Trace.h> #include <iomanip> #include <memory> @@ -11,12 +7,15 @@ #include <string> #include <thread> +#include <log/log.h> #include <pdx/default_transport/service_endpoint.h> #include <private/dvr/bufferhub_rpc.h> -#include "consumer_channel.h" -#include "detached_buffer_channel.h" -#include "producer_channel.h" -#include "producer_queue_channel.h" +#include <private/dvr/buffer_channel.h> +#include <private/dvr/buffer_hub.h> +#include <private/dvr/consumer_channel.h> +#include <private/dvr/producer_channel.h> +#include <private/dvr/producer_queue_channel.h> +#include <utils/Trace.h> using android::pdx::Channel; using android::pdx::ErrorStatus; @@ -257,23 +256,6 @@ pdx::Status<void> BufferHubService::HandleMessage(Message& message) { *this, &BufferHubService::OnCreateProducerQueue, message); return {}; - case BufferHubRPC::ProducerBufferDetach::Opcode: - // In addition to the message handler in the ProducerChannel's - // HandleMessage method, we also need to invalid the producer channel (and - // all associated consumer channels). Note that this has to be done after - // HandleMessage returns to make sure the IPC request has went back to the - // client first. - SetChannel(channel->channel_id(), nullptr); - return {}; - - case DetachedBufferRPC::Promote::Opcode: - // In addition to the message handler in the DetachedBufferChannel's - // HandleMessage method, we also need to invalid the channel. Note that - // this has to be done after HandleMessage returns to make sure the IPC - // request has went back to the client first. - SetChannel(channel->channel_id(), nullptr); - return {}; - default: return DefaultHandleMessage(message); } @@ -332,9 +314,9 @@ pdx::Status<void> BufferHubService::OnCreateDetachedBuffer( return ErrorStatus(EALREADY); } - std::unique_ptr<DetachedBufferChannel> channel = - DetachedBufferChannel::Create(this, buffer_id, width, height, layer_count, - format, usage, user_metadata_size); + std::unique_ptr<BufferChannel> channel = + BufferChannel::Create(this, buffer_id, width, height, layer_count, format, + usage, user_metadata_size); if (!channel) { ALOGE( "BufferHubService::OnCreateDetachedBuffer: Failed to allocate buffer, " diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp new file mode 100644 index 0000000000..afd34aaa2f --- /dev/null +++ b/services/vr/bufferhubd/buffer_hub_binder.cpp @@ -0,0 +1,129 @@ +#include <stdio.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <log/log.h> +#include <private/dvr/buffer_hub_binder.h> +#include <private/dvr/buffer_node.h> + +namespace android { +namespace dvr { + +status_t BufferHubBinderService::start( + const std::shared_ptr<BufferHubService>& pdx_service) { + IPCThreadState::self()->disableBackgroundScheduling(true); + + sp<BufferHubBinderService> service = new BufferHubBinderService(); + service->pdx_service_ = pdx_service; + + // Not using BinderService::publish because need to get an instance of this + // class (above). Following code is the same as + // BinderService::publishAndJoinThreadPool + sp<IServiceManager> sm = defaultServiceManager(); + status_t result = sm->addService( + String16(getServiceName()), service, + /*allowIsolated =*/false, + /*dump flags =*/IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT); + if (result != NO_ERROR) { + ALOGE("Publishing bufferhubd failed with error %d", result); + return result; + } + + sp<ProcessState> process_self(ProcessState::self()); + process_self->startThreadPool(); + + return result; +} + +status_t BufferHubBinderService::dump(int fd, const Vector<String16>& args) { + FILE* out = fdopen(dup(fd), "w"); + + // Currently not supporting args, so notify the user. + if (!args.isEmpty()) { + fprintf(out, + "Note: dumpsys bufferhubd currently does not support args." + "Input arguments are ignored.\n"); + } + + fprintf(out, "Binder service:\n"); + // Active buffers + fprintf(out, "Active BufferClients: %zu\n", client_list_.size()); + // TODO(b/117790952): print buffer information after BufferNode has it + // TODO(b/116526156): print more information once we have them + + if (pdx_service_) { + fprintf(out, "\nPDX service:\n"); + // BufferHubService::Dumpstate(size_t) is not actually using the param + // So just using 0 as the length + fprintf(out, "%s", pdx_service_->DumpState(0).c_str()); + } else { + fprintf(out, "PDX service not registered or died.\n"); + } + + fclose(out); + return NO_ERROR; +} + +status_t BufferHubBinderService::registerToken( + const std::weak_ptr<BufferNode> node, uint64_t* outToken) { + do { + *outToken = token_engine_(); + } while (token_map_.find(*outToken) != token_map_.end()); + + token_map_.emplace(*outToken, node); + return NO_ERROR; +} + +sp<IBufferClient> BufferHubBinderService::createBuffer( + uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, + uint64_t usage, uint64_t user_metadata_size) { + std::shared_ptr<BufferNode> node = std::make_shared<BufferNode>( + width, height, layer_count, format, usage, user_metadata_size); + + sp<BufferClient> client = new BufferClient(node, this); + // Add it to list for bookkeeping and dumpsys. + client_list_.push_back(client); + + return client; +} + +status_t BufferHubBinderService::importBuffer(uint64_t token, + sp<IBufferClient>* outClient) { + auto iter = token_map_.find(token); + + if (iter == token_map_.end()) { // Not found + ALOGE("BufferHubBinderService::importBuffer: token %" PRIu64 + "does not exist.", + token); + return PERMISSION_DENIED; + } + + if (iter->second.expired()) { // Gone + ALOGW( + "BufferHubBinderService::importBuffer: the original node of token " + "%" PRIu64 "has gone.", + token); + token_map_.erase(iter); + return DEAD_OBJECT; + } + + // Promote the weak_ptr + std::shared_ptr<BufferNode> node(iter->second); + if (!node) { + ALOGE("BufferHubBinderService::importBuffer: promote weak_ptr failed."); + token_map_.erase(iter); + return DEAD_OBJECT; + } + + sp<BufferClient> client = new BufferClient(node, this); + *outClient = client; + + token_map_.erase(iter); + client_list_.push_back(client); + + return NO_ERROR; +} + +} // namespace dvr +} // namespace android diff --git a/services/vr/bufferhubd/buffer_node.cpp b/services/vr/bufferhubd/buffer_node.cpp new file mode 100644 index 0000000000..31c6ef007c --- /dev/null +++ b/services/vr/bufferhubd/buffer_node.cpp @@ -0,0 +1,95 @@ +#include <errno.h> + +#include <private/dvr/IBufferHub.h> +#include <private/dvr/buffer_hub_defs.h> +#include <private/dvr/buffer_node.h> +#include <ui/GraphicBufferAllocator.h> + +namespace android { +namespace dvr { + +void BufferNode::InitializeMetadata() { + // Using placement new here to reuse shared memory instead of new allocation + // Initialize the atomic variables to zero. + BufferHubDefs::MetadataHeader* metadata_header = metadata_.metadata_header(); + buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint64_t>(0); + fence_state_ = new (&metadata_header->fence_state) std::atomic<uint64_t>(0); + active_clients_bit_mask_ = + new (&metadata_header->active_clients_bit_mask) std::atomic<uint64_t>(0); +} + +// Allocates a new BufferNode. +BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, + size_t user_metadata_size) { + uint32_t out_stride = 0; + // graphicBufferId is not used in GraphicBufferAllocator::allocate + int ret = GraphicBufferAllocator::get().allocate( + width, height, format, layer_count, usage, + const_cast<const native_handle_t**>(&buffer_handle_), &out_stride, + /*graphicBufferId=*/0, IBufferHub::getServiceName()); + + if (ret != OK || buffer_handle_ == nullptr) { + ALOGE("BufferNode::BufferNode: Failed to allocate buffer: %s", + strerror(-ret)); + return; + } + + buffer_desc_.width = width; + buffer_desc_.height = height; + buffer_desc_.layers = layer_count; + buffer_desc_.format = format; + buffer_desc_.usage = usage; + buffer_desc_.stride = out_stride; + + metadata_ = BufferHubMetadata::Create(user_metadata_size); + if (!metadata_.IsValid()) { + ALOGE("BufferNode::BufferNode: Failed to allocate metadata."); + return; + } + InitializeMetadata(); +} + +// Free the handle +BufferNode::~BufferNode() { + if (buffer_handle_ != nullptr) { + status_t ret = GraphicBufferAllocator::get().free(buffer_handle_); + if (ret != OK) { + ALOGE("BufferNode::~BufferNode: Failed to free handle; Got error: %d", + ret); + } + } +} + +uint64_t BufferNode::GetActiveClientsBitMask() const { + return active_clients_bit_mask_->load(std::memory_order_acquire); +} + +uint64_t BufferNode::AddNewActiveClientsBitToMask() { + uint64_t current_active_clients_bit_mask = GetActiveClientsBitMask(); + uint64_t client_state_mask = 0ULL; + uint64_t updated_active_clients_bit_mask = 0ULL; + do { + client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask( + current_active_clients_bit_mask); + if (client_state_mask == 0ULL) { + ALOGE( + "BufferNode::AddNewActiveClientsBitToMask: reached the maximum " + "mumber of channels per buffer node: 32."); + errno = E2BIG; + return 0ULL; + } + updated_active_clients_bit_mask = + current_active_clients_bit_mask | client_state_mask; + } while (!(active_clients_bit_mask_->compare_exchange_weak( + current_active_clients_bit_mask, updated_active_clients_bit_mask, + std::memory_order_acq_rel, std::memory_order_acquire))); + return client_state_mask; +} + +void BufferNode::RemoveClientsBitFromMask(const uint64_t& value) { + active_clients_bit_mask_->fetch_and(~value); +} + +} // namespace dvr +} // namespace android diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp index b27f218eb6..3e10b26d2f 100644 --- a/services/vr/bufferhubd/bufferhubd.cpp +++ b/services/vr/bufferhubd/bufferhubd.cpp @@ -1,17 +1,16 @@ #include <sched.h> -#include <unistd.h> - -#include <log/log.h> #include <sys/resource.h> +#include <unistd.h> #include <dvr/performance_client_api.h> +#include <log/log.h> #include <pdx/service_dispatcher.h> - -#include "buffer_hub.h" +#include <private/dvr/buffer_hub.h> +#include <private/dvr/buffer_hub_binder.h> int main(int, char**) { int ret = -1; - std::shared_ptr<android::pdx::Service> service; + std::shared_ptr<android::dvr::BufferHubService> pdx_service; std::unique_ptr<android::pdx::ServiceDispatcher> dispatcher; // We need to be able to create endpoints with full perms. @@ -37,9 +36,13 @@ int main(int, char**) { dispatcher = android::pdx::ServiceDispatcher::Create(); CHECK_ERROR(!dispatcher, error, "Failed to create service dispatcher\n"); - service = android::dvr::BufferHubService::Create(); - CHECK_ERROR(!service, error, "Failed to create buffer hub service\n"); - dispatcher->AddService(service); + pdx_service = android::dvr::BufferHubService::Create(); + CHECK_ERROR(!pdx_service, error, "Failed to create bufferhub pdx service\n"); + dispatcher->AddService(pdx_service); + + ret = android::dvr::BufferHubBinderService::start(pdx_service); + CHECK_ERROR(ret != android::NO_ERROR, error, + "Failed to create bufferhub binder service\n"); ret = dvrSetSchedulerClass(0, "graphics"); CHECK_ERROR(ret < 0, error, "Failed to set thread priority"); diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp index a6d2dbb60c..98ef9175e3 100644 --- a/services/vr/bufferhubd/consumer_channel.cpp +++ b/services/vr/bufferhubd/consumer_channel.cpp @@ -1,12 +1,10 @@ -#include "consumer_channel.h" - -#include <log/log.h> -#include <utils/Trace.h> - #include <thread> +#include <log/log.h> #include <private/dvr/bufferhub_rpc.h> -#include "producer_channel.h" +#include <private/dvr/consumer_channel.h> +#include <private/dvr/producer_channel.h> +#include <utils/Trace.h> using android::pdx::BorrowedHandle; using android::pdx::Channel; @@ -19,10 +17,10 @@ namespace android { namespace dvr { ConsumerChannel::ConsumerChannel(BufferHubService* service, int buffer_id, - int channel_id, uint64_t consumer_state_bit, + int channel_id, uint64_t client_state_mask, const std::shared_ptr<Channel> producer) : BufferHubChannel(service, buffer_id, channel_id, kConsumerType), - consumer_state_bit_(consumer_state_bit), + client_state_mask_(client_state_mask), producer_(producer) { GetProducer()->AddConsumer(this); } @@ -43,7 +41,7 @@ BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const { // If producer has not hung up, copy most buffer info from the producer. info = producer->GetBufferInfo(); } else { - info.signaled_mask = consumer_state_bit(); + info.signaled_mask = client_state_mask(); } info.id = buffer_id(); return info; @@ -92,11 +90,6 @@ bool ConsumerChannel::HandleMessage(Message& message) { *this, &ConsumerChannel::OnConsumerRelease, message); return true; - case BufferHubRPC::ConsumerSetIgnore::Opcode: - DispatchRemoteMethod<BufferHubRPC::ConsumerSetIgnore>( - *this, &ConsumerChannel::OnConsumerSetIgnore, message); - return true; - default: return false; } @@ -107,7 +100,7 @@ Status<BufferDescription<BorrowedHandle>> ConsumerChannel::OnGetBuffer( ATRACE_NAME("ConsumerChannel::OnGetBuffer"); ALOGD_IF(TRACE, "ConsumerChannel::OnGetBuffer: buffer=%d", buffer_id()); if (auto producer = GetProducer()) { - return {producer->GetBuffer(consumer_state_bit_)}; + return {producer->GetBuffer(client_state_mask_)}; } else { return ErrorStatus(EPIPE); } @@ -122,9 +115,8 @@ Status<LocalFence> ConsumerChannel::OnConsumerAcquire(Message& message) { if (acquired_ || released_) { ALOGE( "ConsumerChannel::OnConsumerAcquire: Acquire when not posted: " - "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d", - ignored_, acquired_, released_, message.GetChannelId(), - producer->buffer_id()); + "acquired=%d released=%d channel_id=%d buffer_id=%d", + acquired_, released_, message.GetChannelId(), producer->buffer_id()); return ErrorStatus(EBUSY); } else { auto status = producer->OnConsumerAcquire(message); @@ -146,9 +138,8 @@ Status<void> ConsumerChannel::OnConsumerRelease(Message& message, if (!acquired_ || released_) { ALOGE( "ConsumerChannel::OnConsumerRelease: Release when not acquired: " - "ignored=%d acquired=%d released=%d channel_id=%d buffer_id=%d", - ignored_, acquired_, released_, message.GetChannelId(), - producer->buffer_id()); + "acquired=%d released=%d channel_id=%d buffer_id=%d", + acquired_, released_, message.GetChannelId(), producer->buffer_id()); return ErrorStatus(EBUSY); } else { auto status = @@ -162,36 +153,11 @@ Status<void> ConsumerChannel::OnConsumerRelease(Message& message, } } -Status<void> ConsumerChannel::OnConsumerSetIgnore(Message&, bool ignored) { - ATRACE_NAME("ConsumerChannel::OnConsumerSetIgnore"); - auto producer = GetProducer(); - if (!producer) - return ErrorStatus(EPIPE); - - ignored_ = ignored; - if (ignored_ && acquired_) { - // Update the producer if ignore is set after the consumer acquires the - // buffer. - ClearAvailable(); - producer->OnConsumerIgnored(); - acquired_ = false; - released_ = true; - } - - return {}; -} - bool ConsumerChannel::OnProducerPosted() { - if (ignored_) { - acquired_ = false; - released_ = true; - return false; - } else { - acquired_ = false; - released_ = false; - SignalAvailable(); - return true; - } + acquired_ = false; + released_ = false; + SignalAvailable(); + return true; } void ConsumerChannel::OnProducerClosed() { diff --git a/services/vr/bufferhubd/consumer_queue_channel.cpp b/services/vr/bufferhubd/consumer_queue_channel.cpp index 4d430012f1..74b549d1f2 100644 --- a/services/vr/bufferhubd/consumer_queue_channel.cpp +++ b/services/vr/bufferhubd/consumer_queue_channel.cpp @@ -1,8 +1,6 @@ -#include "consumer_queue_channel.h" - #include <pdx/channel_handle.h> - -#include "producer_channel.h" +#include <private/dvr/consumer_queue_channel.h> +#include <private/dvr/producer_channel.h> using android::pdx::ErrorStatus; using android::pdx::RemoteChannelHandle; diff --git a/services/vr/bufferhubd/detached_buffer_channel.cpp b/services/vr/bufferhubd/detached_buffer_channel.cpp deleted file mode 100644 index a5cf68dcfd..0000000000 --- a/services/vr/bufferhubd/detached_buffer_channel.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "detached_buffer_channel.h" -#include "producer_channel.h" - -using android::pdx::BorrowedHandle; -using android::pdx::ErrorStatus; -using android::pdx::Message; -using android::pdx::RemoteChannelHandle; -using android::pdx::Status; -using android::pdx::rpc::DispatchRemoteMethod; - -namespace android { -namespace dvr { - -DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service, - int buffer_id, int channel_id, - IonBuffer buffer, - IonBuffer metadata_buffer, - size_t user_metadata_size) - : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType), - buffer_(std::move(buffer)), - metadata_buffer_(std::move(metadata_buffer)), - user_metadata_size_(user_metadata_size) { -} - -DetachedBufferChannel::DetachedBufferChannel(BufferHubService* service, - int buffer_id, uint32_t width, - uint32_t height, - uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size) - : BufferHubChannel(service, buffer_id, buffer_id, kDetachedBufferType), - user_metadata_size_(user_metadata_size) { - // The size the of metadata buffer is used as the "width" parameter during - // allocation. Thus it cannot overflow uint32_t. - if (user_metadata_size_ >= (std::numeric_limits<uint32_t>::max() - - BufferHubDefs::kMetadataHeaderSize)) { - ALOGE( - "DetachedBufferChannel::DetachedBufferChannel: metadata size too big."); - return; - } - - if (int ret = buffer_.Alloc(width, height, layer_count, format, usage)) { - ALOGE( - "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate " - "buffer: %s", - strerror(-ret)); - return; - } - - // Buffer metadata has two parts: 1) a fixed sized metadata header; and 2) - // user requested metadata. - const size_t size = BufferHubDefs::kMetadataHeaderSize + user_metadata_size_; - if (int ret = metadata_buffer_.Alloc(size, - /*height=*/1, - /*layer_count=*/1, - BufferHubDefs::kMetadataFormat, - BufferHubDefs::kMetadataUsage)) { - ALOGE( - "DetachedBufferChannel::DetachedBufferChannel: Failed to allocate " - "metadata: %s", - strerror(-ret)); - return; - } -} - -DetachedBufferChannel::~DetachedBufferChannel() { - ALOGD_IF(TRACE, - "DetachedBufferChannel::~DetachedBufferChannel: channel_id=%d " - "buffer_id=%d.", - channel_id(), buffer_id()); - Hangup(); -} - -BufferHubChannel::BufferInfo DetachedBufferChannel::GetBufferInfo() const { - return BufferInfo(buffer_id(), /*consumer_count=*/0, buffer_.width(), - buffer_.height(), buffer_.layer_count(), buffer_.format(), - buffer_.usage(), /*pending_count=*/0, /*state=*/0, - /*signaled_mask=*/0, /*index=*/0); -} - -void DetachedBufferChannel::HandleImpulse(Message& /*message*/) { - ATRACE_NAME("DetachedBufferChannel::HandleImpulse"); -} - -bool DetachedBufferChannel::HandleMessage(Message& message) { - ATRACE_NAME("DetachedBufferChannel::HandleMessage"); - switch (message.GetOp()) { - case DetachedBufferRPC::Import::Opcode: - DispatchRemoteMethod<DetachedBufferRPC::Import>( - *this, &DetachedBufferChannel::OnImport, message); - return true; - - case DetachedBufferRPC::Promote::Opcode: - DispatchRemoteMethod<DetachedBufferRPC::Promote>( - *this, &DetachedBufferChannel::OnPromote, message); - return true; - - default: - return false; - } -} - -Status<BufferDescription<BorrowedHandle>> DetachedBufferChannel::OnImport( - Message& /*message*/) { - ATRACE_NAME("DetachedBufferChannel::OnGetBuffer"); - ALOGD_IF(TRACE, "DetachedBufferChannel::OnGetBuffer: buffer=%d.", - buffer_id()); - - return BufferDescription<BorrowedHandle>{buffer_, - metadata_buffer_, - buffer_id(), - /*buffer_state_bit=*/0, - BorrowedHandle{}, - BorrowedHandle{}}; -} - -Status<RemoteChannelHandle> DetachedBufferChannel::OnPromote( - Message& message) { - ATRACE_NAME("DetachedBufferChannel::OnPromote"); - ALOGD_IF(TRACE, "DetachedBufferChannel::OnPromote: buffer_id=%d", - buffer_id()); - - // Note that the new ProducerChannel will have different channel_id, but - // inherits the buffer_id from the DetachedBuffer. - int channel_id; - auto status = message.PushChannel(0, nullptr, &channel_id); - if (!status) { - ALOGE( - "DetachedBufferChannel::OnPromote: Failed to push ProducerChannel: %s.", - status.GetErrorMessage().c_str()); - return ErrorStatus(ENOMEM); - } - - std::unique_ptr<ProducerChannel> channel = ProducerChannel::Create( - service(), buffer_id(), channel_id, std::move(buffer_), - std::move(metadata_buffer_), user_metadata_size_); - if (!channel) { - ALOGE( - "DetachedBufferChannel::OnPromote: Failed to create ProducerChannel " - "from a DetachedBufferChannel, buffer_id=%d.", - buffer_id()); - } - - const auto channel_status = - service()->SetChannel(channel_id, std::move(channel)); - if (!channel_status) { - // Technically, this should never fail, as we just pushed the channel. Note - // that LOG_FATAL will be stripped out in non-debug build. - LOG_FATAL( - "DetachedBufferChannel::OnPromote: Failed to set new producer buffer " - "channel: %s.", - channel_status.GetErrorMessage().c_str()); - } - - return status; -} - -} // namespace dvr -} // namespace android diff --git a/services/vr/bufferhubd/detached_buffer_channel.h b/services/vr/bufferhubd/detached_buffer_channel.h deleted file mode 100644 index 8b6dab8538..0000000000 --- a/services/vr/bufferhubd/detached_buffer_channel.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_ -#define ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_ - -#include "buffer_hub.h" - -#include <pdx/channel_handle.h> -#include <pdx/file_handle.h> - -namespace android { -namespace dvr { - -class DetachedBufferChannel : public BufferHubChannel { - public: - ~DetachedBufferChannel() override; - - template <typename... Args> - static std::unique_ptr<DetachedBufferChannel> Create(Args&&... args) { - auto buffer = std::unique_ptr<DetachedBufferChannel>( - new DetachedBufferChannel(std::forward<Args>(args)...)); - return buffer->IsValid() ? std::move(buffer) : nullptr; - } - - // Returns whether the object holds a valid graphic buffer. - bool IsValid() const { - return buffer_.IsValid() && metadata_buffer_.IsValid(); - } - - size_t user_metadata_size() const { return user_metadata_size_; } - - // Captures buffer info for use by BufferHubService::DumpState(). - BufferInfo GetBufferInfo() const override; - - bool HandleMessage(pdx::Message& message) override; - void HandleImpulse(pdx::Message& message) override; - - private: - // Creates a detached buffer from existing IonBuffers. - DetachedBufferChannel(BufferHubService* service, int buffer_id, - int channel_id, IonBuffer buffer, - IonBuffer metadata_buffer, size_t user_metadata_size); - - // Allocates a new detached buffer. - DetachedBufferChannel(BufferHubService* service, int buffer_id, - uint32_t width, uint32_t height, uint32_t layer_count, - uint32_t format, uint64_t usage, - size_t user_metadata_size); - - pdx::Status<BufferDescription<pdx::BorrowedHandle>> OnImport( - pdx::Message& message); - pdx::Status<pdx::RemoteChannelHandle> OnPromote(pdx::Message& message); - - // Gralloc buffer handles. - IonBuffer buffer_; - IonBuffer metadata_buffer_; - - // Size of user requested metadata. - const size_t user_metadata_size_; -}; - -} // namespace dvr -} // namespace android - -#endif // ANDROID_DVR_BUFFERHUBD_DETACHED_BUFFER_CHANNEL_H_ diff --git a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h new file mode 100644 index 0000000000..502c6d6041 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h @@ -0,0 +1,34 @@ +#ifndef ANDROID_DVR_IBUFFERHUB_H +#define ANDROID_DVR_IBUFFERHUB_H + +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <private/dvr/IBufferClient.h> + +namespace android { +namespace dvr { + +class IBufferHub : public IInterface { + public: + DECLARE_META_INTERFACE(BufferHub); + + static const char* getServiceName() { return "bufferhubd"; } + virtual sp<IBufferClient> createBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t format, + uint64_t usage, + uint64_t user_metadata_size) = 0; + + virtual status_t importBuffer(uint64_t token, + sp<IBufferClient>* outClient) = 0; +}; + +class BnBufferHub : public BnInterface<IBufferHub> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0); +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_IBUFFERHUB_H
\ No newline at end of file diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_channel.h b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h new file mode 100644 index 0000000000..744c095338 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h @@ -0,0 +1,61 @@ +#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ +#define ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub.h> +#include <private/dvr/buffer_hub_defs.h> +#include <private/dvr/buffer_node.h> + +namespace android { +namespace dvr { + +class BufferChannel : public BufferHubChannel { + public: + ~BufferChannel() override; + + template <typename... Args> + static std::unique_ptr<BufferChannel> Create(Args&&... args) { + auto buffer = std::unique_ptr<BufferChannel>( + new BufferChannel(std::forward<Args>(args)...)); + return buffer->IsValid() ? std::move(buffer) : nullptr; + } + + // Returns whether the object holds a valid graphic buffer. + bool IsValid() const { + return buffer_node_ != nullptr && buffer_node_->IsValid(); + } + + // Captures buffer info for use by BufferHubService::DumpState(). + BufferInfo GetBufferInfo() const override; + + bool HandleMessage(pdx::Message& message) override; + void HandleImpulse(pdx::Message& message) override; + + private: + + // Allocates a new detached buffer. + BufferChannel(BufferHubService* service, int buffer_id, uint32_t width, + uint32_t height, uint32_t layer_count, uint32_t format, + uint64_t usage, size_t user_metadata_size); + + // Creates a detached buffer from an existing BufferNode. This method is used + // in OnDuplicate method. + BufferChannel(BufferHubService* service, int buffer_id, int channel_id, + std::shared_ptr<BufferNode> buffer_node); + + pdx::Status<BufferTraits<pdx::BorrowedHandle>> OnImport( + pdx::Message& message); + pdx::Status<pdx::RemoteChannelHandle> OnDuplicate(pdx::Message& message); + + // The concrete implementation of the Buffer object. + std::shared_ptr<BufferNode> buffer_node_ = nullptr; + + // The state bit of this buffer. Must be one the lower 63 bits. + uint64_t client_state_mask_ = 0ULL; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_client.h b/services/vr/bufferhubd/include/private/dvr/buffer_client.h new file mode 100644 index 0000000000..d79ec0a38d --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_client.h @@ -0,0 +1,41 @@ +#ifndef ANDROID_DVR_BUFFERCLIENT_H +#define ANDROID_DVR_BUFFERCLIENT_H + +#include <private/dvr/IBufferClient.h> +#include <private/dvr/buffer_node.h> + +namespace android { +namespace dvr { + +// Forward declaration to avoid circular dependency +class BufferHubBinderService; + +class BufferClient : public BnBufferClient { + public: + // Creates a server-side buffer client from an existing BufferNode. Note that + // this funciton takes ownership of the shared_ptr. + explicit BufferClient(std::shared_ptr<BufferNode> node, + BufferHubBinderService* service) + : service_(service), buffer_node_(std::move(node)){}; + + // Binder IPC functions + bool isValid() override { + return buffer_node_ ? buffer_node_->IsValid() : false; + }; + + status_t duplicate(uint64_t* outToken) override; + + private: + // Hold a pointer to the service to bypass binder interface, as BufferClient + // and the service will be in the same process. Also, since service owns + // Client, if service dead the clients will be destroyed, so this pointer is + // guaranteed to be valid. + BufferHubBinderService* service_; + + std::shared_ptr<BufferNode> buffer_node_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_IBUFFERCLIENT_H
\ No newline at end of file diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h index e47ffa3417..e47ffa3417 100644 --- a/services/vr/bufferhubd/buffer_hub.h +++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h new file mode 100644 index 0000000000..cf6124bab3 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h @@ -0,0 +1,53 @@ +#ifndef ANDROID_DVR_BUFFER_HUB_BINDER_H +#define ANDROID_DVR_BUFFER_HUB_BINDER_H + +#include <random> +#include <unordered_map> +#include <vector> + +#include <binder/BinderService.h> +#include <private/dvr/IBufferHub.h> +#include <private/dvr/buffer_client.h> +#include <private/dvr/buffer_hub.h> +#include <private/dvr/buffer_node.h> + +namespace android { +namespace dvr { + +class BufferHubBinderService : public BinderService<BufferHubBinderService>, + public BnBufferHub { + public: + static status_t start(const std::shared_ptr<BufferHubService>& pdx_service); + // Dumps bufferhub related information to given fd (usually stdout) + // usage: adb shell dumpsys bufferhubd + virtual status_t dump(int fd, const Vector<String16>& args) override; + + // Marks a BufferNode to be duplicated. + // TODO(b/116681016): add importToken(int64_t) + status_t registerToken(const std::weak_ptr<BufferNode> node, + uint64_t* outToken); + + // Binder IPC functions + sp<IBufferClient> createBuffer(uint32_t width, uint32_t height, + uint32_t layer_count, uint32_t format, + uint64_t usage, + uint64_t user_metadata_size) override; + + status_t importBuffer(uint64_t token, sp<IBufferClient>* outClient) override; + + private: + std::shared_ptr<BufferHubService> pdx_service_; + + std::vector<sp<BufferClient>> client_list_; + + // TODO(b/118180214): use a more secure implementation + std::mt19937_64 token_engine_; + // The mapping from token to a specific node. This is a many-to-one mapping. + // One node could be refered by 0 to multiple tokens. + std::unordered_map<uint64_t, std::weak_ptr<BufferNode>> token_map_; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFER_HUB_BINDER_H diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h new file mode 100644 index 0000000000..bc0a34ef08 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_node.h @@ -0,0 +1,79 @@ +#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ +#define ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ + +#include <android/hardware_buffer.h> +#include <private/dvr/ion_buffer.h> +#include <ui/BufferHubMetadata.h> + +namespace android { +namespace dvr { + +class BufferNode { + public: + // Allocates a new BufferNode. + BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, + uint32_t format, uint64_t usage, size_t user_metadata_size); + + ~BufferNode(); + + // Returns whether the object holds a valid metadata. + bool IsValid() const { return metadata_.IsValid(); } + + size_t user_metadata_size() const { return metadata_.user_metadata_size(); } + + // Accessors of the buffer description and handle + const native_handle_t* buffer_handle() const { return buffer_handle_; } + const AHardwareBuffer_Desc& buffer_desc() const { return buffer_desc_; } + + // Accessors of metadata. + const BufferHubMetadata& metadata() const { return metadata_; } + + // Gets the current value of active_clients_bit_mask in metadata_ with + // std::memory_order_acquire, so that all previous releases of + // active_clients_bit_mask from all threads will be returned here. + uint64_t GetActiveClientsBitMask() const; + + // Find and add a new client_state_mask to active_clients_bit_mask in + // metadata_. + // Return the new client_state_mask that is added to active_clients_bit_mask. + // Return 0ULL if there are already 32 bp clients of the buffer. + uint64_t AddNewActiveClientsBitToMask(); + + // Removes the value from active_clients_bit_mask in metadata_ with + // std::memory_order_release, so that the change will be visible to any + // acquire of active_clients_bit_mask_ in any threads after the succeed of + // this operation. + void RemoveClientsBitFromMask(const uint64_t& value); + + private: + // Helper method for constructors to initialize atomic metadata header + // variables in shared memory. + void InitializeMetadata(); + + // Gralloc buffer handles. + native_handle_t* buffer_handle_; + AHardwareBuffer_Desc buffer_desc_; + + // Metadata in shared memory. + BufferHubMetadata metadata_; + + // The following variables are atomic variables in metadata_ that are visible + // to Bn object and Bp objects. Please find more info in + // BufferHubDefs::MetadataHeader. + + // buffer_state_ tracks the state of the buffer. Buffer can be in one of these + // four states: gained, posted, acquired, released. + std::atomic<uint64_t>* buffer_state_ = nullptr; + + // TODO(b/112012161): add comments to fence_state_. + std::atomic<uint64_t>* fence_state_ = nullptr; + + // active_clients_bit_mask_ tracks all the bp clients of the buffer. It is the + // union of all client_state_mask of all bp clients. + std::atomic<uint64_t>* active_clients_bit_mask_ = nullptr; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h index 55cf96920d..3298529e1d 100644 --- a/services/vr/bufferhubd/consumer_channel.h +++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h @@ -1,10 +1,9 @@ #ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_ #define ANDROID_DVR_BUFFERHUBD_CONSUMER_CHANNEL_H_ -#include "buffer_hub.h" - #include <pdx/rpc/buffer_wrapper.h> #include <private/dvr/bufferhub_rpc.h> +#include <private/dvr/buffer_hub.h> namespace android { namespace dvr { @@ -17,14 +16,14 @@ class ConsumerChannel : public BufferHubChannel { using Message = pdx::Message; ConsumerChannel(BufferHubService* service, int buffer_id, int channel_id, - uint64_t consumer_state_bit, + uint64_t client_state_mask, const std::shared_ptr<Channel> producer); ~ConsumerChannel() override; bool HandleMessage(Message& message) override; void HandleImpulse(Message& message) override; - uint64_t consumer_state_bit() const { return consumer_state_bit_; } + uint64_t client_state_mask() const { return client_state_mask_; } BufferInfo GetBufferInfo() const override; bool OnProducerPosted(); @@ -38,12 +37,10 @@ class ConsumerChannel : public BufferHubChannel { pdx::Status<LocalFence> OnConsumerAcquire(Message& message); pdx::Status<void> OnConsumerRelease(Message& message, LocalFence release_fence); - pdx::Status<void> OnConsumerSetIgnore(Message& message, bool ignore); - uint64_t consumer_state_bit_{0}; + uint64_t client_state_mask_{0}; bool acquired_{false}; bool released_{true}; - bool ignored_{false}; // True if we are ignoring events. std::weak_ptr<Channel> producer_; ConsumerChannel(const ConsumerChannel&) = delete; diff --git a/services/vr/bufferhubd/consumer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h index 8437c4cd04..8f3543740c 100644 --- a/services/vr/bufferhubd/consumer_queue_channel.h +++ b/services/vr/bufferhubd/include/private/dvr/consumer_queue_channel.h @@ -1,14 +1,12 @@ #ifndef ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_ #define ANDROID_DVR_BUFFERHUBD_CONSUMER_QUEUE_CHANNEL_H_ -#include "buffer_hub.h" - -#include <private/dvr/bufferhub_rpc.h> - #include <queue> -#include "consumer_channel.h" -#include "producer_queue_channel.h" +#include <private/dvr/bufferhub_rpc.h> +#include <private/dvr/buffer_hub.h> +#include <private/dvr/consumer_channel.h> +#include <private/dvr/producer_queue_channel.h> namespace android { namespace dvr { @@ -44,7 +42,7 @@ class ConsumerQueueChannel : public BufferHubChannel { private: std::shared_ptr<ProducerQueueChannel> GetProducer() const; - // Pointer to the prodcuer channel + // Pointer to the producer channel. std::weak_ptr<Channel> producer_; // Tracks newly allocated buffer producers along with it's slot number. diff --git a/services/vr/bufferhubd/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h index 67fdf150a0..5868b0994c 100644 --- a/services/vr/bufferhubd/producer_channel.h +++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h @@ -1,8 +1,6 @@ #ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_ #define ANDROID_DVR_BUFFERHUBD_PRODUCER_CHANNEL_H_ -#include "buffer_hub.h" - #include <functional> #include <memory> #include <vector> @@ -10,6 +8,7 @@ #include <pdx/channel_handle.h> #include <pdx/file_handle.h> #include <pdx/rpc/buffer_wrapper.h> +#include <private/dvr/buffer_hub.h> #include <private/dvr/bufferhub_rpc.h> #include <private/dvr/ion_buffer.h> @@ -43,12 +42,16 @@ class ProducerChannel : public BufferHubChannel { ~ProducerChannel() override; + uint64_t buffer_state() const { + return buffer_state_->load(std::memory_order_acquire); + } + bool HandleMessage(Message& message) override; void HandleImpulse(Message& message) override; BufferInfo GetBufferInfo() const override; - BufferDescription<BorrowedHandle> GetBuffer(uint64_t buffer_state_bit); + BufferDescription<BorrowedHandle> GetBuffer(uint64_t client_state_mask); pdx::Status<RemoteChannelHandle> CreateConsumer(Message& message); pdx::Status<RemoteChannelHandle> OnNewConsumer(Message& message); @@ -57,7 +60,7 @@ class ProducerChannel : public BufferHubChannel { pdx::Status<void> OnConsumerRelease(Message& message, LocalFence release_fence); - void OnConsumerIgnored(); + void DecrementPendingConsumers(); void OnConsumerOrphaned(ConsumerChannel* channel); void AddConsumer(ConsumerChannel* channel); @@ -80,10 +83,8 @@ class ProducerChannel : public BufferHubChannel { BufferHubDefs::MetadataHeader* metadata_header_ = nullptr; std::atomic<uint64_t>* buffer_state_ = nullptr; std::atomic<uint64_t>* fence_state_ = nullptr; + std::atomic<uint64_t>* active_clients_bit_mask_ = nullptr; - // All active consumer bits. Valid bits are the lower 63 bits, while the - // highest bit is reserved for the producer and should not be set. - uint64_t active_consumer_bit_mask_{0ULL}; // All orphaned consumer bits. Valid bits are the lower 63 bits, while the // highest bit is reserved for the producer and should not be set. uint64_t orphaned_consumer_bit_mask_{0ULL}; @@ -109,7 +110,6 @@ class ProducerChannel : public BufferHubChannel { pdx::Status<BufferDescription<BorrowedHandle>> OnGetBuffer(Message& message); pdx::Status<void> OnProducerPost(Message& message, LocalFence acquire_fence); pdx::Status<LocalFence> OnProducerGain(Message& message); - pdx::Status<RemoteChannelHandle> OnProducerDetach(Message& message); ProducerChannel(const ProducerChannel&) = delete; void operator=(const ProducerChannel&) = delete; diff --git a/services/vr/bufferhubd/producer_queue_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h index e825f47774..c4003da419 100644 --- a/services/vr/bufferhubd/producer_queue_channel.h +++ b/services/vr/bufferhubd/include/private/dvr/producer_queue_channel.h @@ -1,10 +1,9 @@ #ifndef ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_ #define ANDROID_DVR_BUFFERHUBD_PRODUCER_QUEUE_CHANNEL_H_ -#include "buffer_hub.h" - #include <pdx/status.h> #include <private/dvr/bufferhub_rpc.h> +#include <private/dvr/buffer_hub.h> namespace android { namespace dvr { @@ -38,8 +37,12 @@ class ProducerQueueChannel : public BufferHubChannel { uint32_t format, uint64_t usage, size_t buffer_count); - // Detach a BufferHubProducer indicated by |slot|. Note that the buffer must - // be in Gain'ed state for the producer queue to detach. + // Inserts a BufferProducer into the queue. Note that the buffer must be in + // Gain'ed state for the operation to succeed. + pdx::Status<size_t> OnProducerQueueInsertBuffer(pdx::Message& message, int buffer_cid); + + // Removes a BufferProducer indicated by |slot|. Note that the buffer must be + // in Gain'ed state for the operation to succeed. pdx::Status<void> OnProducerQueueRemoveBuffer(pdx::Message& message, size_t slot); diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp index b6977aaa63..241eee7ab9 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -1,19 +1,18 @@ -#include "producer_channel.h" - -#include <log/log.h> -#include <sync/sync.h> #include <sys/epoll.h> #include <sys/eventfd.h> #include <sys/poll.h> -#include <utils/Trace.h> #include <algorithm> #include <atomic> #include <thread> +#include <log/log.h> +#include <private/dvr/buffer_channel.h> #include <private/dvr/bufferhub_rpc.h> -#include "consumer_channel.h" -#include "detached_buffer_channel.h" +#include <private/dvr/consumer_channel.h> +#include <private/dvr/producer_channel.h> +#include <sync/sync.h> +#include <utils/Trace.h> using android::pdx::BorrowedHandle; using android::pdx::ErrorStatus; @@ -27,14 +26,6 @@ using android::pdx::rpc::WrapBuffer; namespace android { namespace dvr { -namespace { - -static inline uint64_t FindNextClearedBit(uint64_t bits) { - return ~bits - (~bits & (~bits - 1)); -} - -} // namespace - ProducerChannel::ProducerChannel(BufferHubService* service, int buffer_id, int channel_id, IonBuffer buffer, IonBuffer metadata_buffer, @@ -105,8 +96,15 @@ int ProducerChannel::InitializeBuffer() { // and also initialize the value to zero. buffer_state_ = new (&metadata_header_->buffer_state) std::atomic<uint64_t>(0); - fence_state_ = - new (&metadata_header_->fence_state) std::atomic<uint64_t>(0); + fence_state_ = new (&metadata_header_->fence_state) std::atomic<uint64_t>(0); + active_clients_bit_mask_ = + new (&metadata_header_->active_clients_bit_mask) std::atomic<uint64_t>(0); + + // Producer channel is never created after consumer channel, and one buffer + // only have one fixed producer for now. Thus, it is correct to assume + // producer state bit is kFirstClientBitMask for now. + active_clients_bit_mask_->store(BufferHubDefs::kFirstClientBitMask, + std::memory_order_release); acquire_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); release_fence_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); @@ -169,7 +167,8 @@ ProducerChannel::~ProducerChannel() { ALOGD_IF(TRACE, "ProducerChannel::~ProducerChannel: channel_id=%d buffer_id=%d " "state=%" PRIx64 ".", - channel_id(), buffer_id(), buffer_state_->load()); + channel_id(), buffer_id(), + buffer_state_->load(std::memory_order_acquire)); for (auto consumer : consumer_channels_) { consumer->OnProducerClosed(); } @@ -178,14 +177,15 @@ ProducerChannel::~ProducerChannel() { BufferHubChannel::BufferInfo ProducerChannel::GetBufferInfo() const { // Derive the mask of signaled buffers in this producer / consumer set. - uint64_t signaled_mask = signaled() ? BufferHubDefs::kProducerStateBit : 0; + uint64_t signaled_mask = signaled() ? BufferHubDefs::kFirstClientBitMask : 0; for (const ConsumerChannel* consumer : consumer_channels_) { - signaled_mask |= consumer->signaled() ? consumer->consumer_state_bit() : 0; + signaled_mask |= consumer->signaled() ? consumer->client_state_mask() : 0; } return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(), buffer_.height(), buffer_.layer_count(), buffer_.format(), - buffer_.usage(), pending_consumers_, buffer_state_->load(), + buffer_.usage(), pending_consumers_, + buffer_state_->load(std::memory_order_acquire), signaled_mask, metadata_header_->queue_index); } @@ -224,29 +224,28 @@ bool ProducerChannel::HandleMessage(Message& message) { *this, &ProducerChannel::OnProducerGain, message); return true; - case BufferHubRPC::ProducerBufferDetach::Opcode: - DispatchRemoteMethod<BufferHubRPC::ProducerBufferDetach>( - *this, &ProducerChannel::OnProducerDetach, message); - return true; - default: return false; } } BufferDescription<BorrowedHandle> ProducerChannel::GetBuffer( - uint64_t buffer_state_bit) { - return { - buffer_, metadata_buffer_, buffer_id(), - buffer_state_bit, acquire_fence_fd_.Borrow(), release_fence_fd_.Borrow()}; + uint64_t client_state_mask) { + return {buffer_, + metadata_buffer_, + buffer_id(), + channel_id(), + client_state_mask, + acquire_fence_fd_.Borrow(), + release_fence_fd_.Borrow()}; } Status<BufferDescription<BorrowedHandle>> ProducerChannel::OnGetBuffer( Message& /*message*/) { ATRACE_NAME("ProducerChannel::OnGetBuffer"); ALOGD_IF(TRACE, "ProducerChannel::OnGetBuffer: buffer=%d, state=%" PRIx64 ".", - buffer_id(), buffer_state_->load()); - return {GetBuffer(BufferHubDefs::kProducerStateBit)}; + buffer_id(), buffer_state_->load(std::memory_order_acquire)); + return {GetBuffer(BufferHubDefs::kFirstClientBitMask)}; } Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { @@ -266,36 +265,59 @@ Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(Message& message) { // Try find the next consumer state bit which has not been claimed by any // consumer yet. - uint64_t consumer_state_bit = FindNextClearedBit( - active_consumer_bit_mask_ | orphaned_consumer_bit_mask_ | - BufferHubDefs::kProducerStateBit); - if (consumer_state_bit == 0ULL) { + // memory_order_acquire is chosen here because all writes in other threads + // that release active_clients_bit_mask_ need to be visible here. + uint64_t current_active_clients_bit_mask = + active_clients_bit_mask_->load(std::memory_order_acquire); + uint64_t client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask( + current_active_clients_bit_mask | orphaned_consumer_bit_mask_); + if (client_state_mask == 0ULL) { ALOGE( "ProducerChannel::CreateConsumer: reached the maximum mumber of " "consumers per producer: 63."); return ErrorStatus(E2BIG); } + uint64_t updated_active_clients_bit_mask = + current_active_clients_bit_mask | client_state_mask; + // Set the updated value only if the current value stays the same as what was + // read before. If the comparison succeeds, update the value without + // reordering anything before or after this read-modify-write in the current + // thread, and the modification will be visible in other threads that acquire + // active_clients_bit_mask_. If the comparison fails, load the result of + // all writes from all threads to updated_active_clients_bit_mask. + if (!active_clients_bit_mask_->compare_exchange_weak( + current_active_clients_bit_mask, updated_active_clients_bit_mask, + std::memory_order_acq_rel, std::memory_order_acquire)) { + ALOGE("Current active clients bit mask is changed to %" PRIx64 + ", which was expected to be %" PRIx64 ".", + updated_active_clients_bit_mask, current_active_clients_bit_mask); + return ErrorStatus(EBUSY); + } + auto consumer = std::make_shared<ConsumerChannel>(service(), buffer_id(), channel_id, - consumer_state_bit, shared_from_this()); + client_state_mask, shared_from_this()); const auto channel_status = service()->SetChannel(channel_id, consumer); if (!channel_status) { ALOGE( "ProducerChannel::CreateConsumer: failed to set new consumer channel: " "%s", channel_status.GetErrorMessage().c_str()); + // Restore the consumer state bit and make it visible in other threads that + // acquire the active_clients_bit_mask_. + active_clients_bit_mask_->fetch_and(~client_state_mask, + std::memory_order_release); return ErrorStatus(ENOMEM); } - if (!producer_owns_ && - !BufferHubDefs::IsBufferReleased(buffer_state_->load())) { + if (!producer_owns_ && !BufferHubDefs::IsBufferReleased( + buffer_state_->load(std::memory_order_acquire))) { // Signal the new consumer when adding it to a posted producer. if (consumer->OnProducerPosted()) pending_consumers_++; } - active_consumer_bit_mask_ |= consumer_state_bit; return {status.take()}; } @@ -305,8 +327,8 @@ Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) { return CreateConsumer(message); } -Status<void> ProducerChannel::OnProducerPost( - Message&, LocalFence acquire_fence) { +Status<void> ProducerChannel::OnProducerPost(Message&, + LocalFence acquire_fence) { ATRACE_NAME("ProducerChannel::OnProducerPost"); ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id()); if (!producer_owns_) { @@ -320,9 +342,9 @@ Status<void> ProducerChannel::OnProducerPost( int ret = epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD, dummy_fence_fd_.Get(), &event); ALOGE_IF(ret < 0, - "ProducerChannel::OnProducerPost: Failed to modify the shared " - "release fence to include the dummy fence: %s", - strerror(errno)); + "ProducerChannel::OnProducerPost: Failed to modify the shared " + "release fence to include the dummy fence: %s", + strerror(errno)); eventfd_t dummy_fence_count = 0ULL; if (eventfd_read(dummy_fence_fd_.Get(), &dummy_fence_count) < 0) { @@ -383,13 +405,15 @@ Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) { return {std::move(returned_fence_)}; } -Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( +// TODO(b/112338294) Keep here for reference. Remove it after new logic is +// written. +/* Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( Message& message) { ATRACE_NAME("ProducerChannel::OnProducerDetach"); ALOGD_IF(TRACE, "ProducerChannel::OnProducerDetach: buffer_id=%d", buffer_id()); - uint64_t buffer_state = buffer_state_->load(); + uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); if (!BufferHubDefs::IsBufferGained(buffer_state)) { // Can only detach a BufferProducer when it's in gained state. ALOGW( @@ -415,10 +439,9 @@ Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( return ErrorStatus(-ret); }; - std::unique_ptr<DetachedBufferChannel> channel = - DetachedBufferChannel::Create( - service(), buffer_id(), channel_id, std::move(buffer_), - std::move(metadata_buffer_), user_metadata_size_); + std::unique_ptr<BufferChannel> channel = + BufferChannel::Create(service(), buffer_id(), channel_id, + std::move(buffer_), user_metadata_size_); if (!channel) { ALOGE("ProducerChannel::OnProducerDetach: Invalid buffer."); return ErrorStatus(EINVAL); @@ -436,7 +459,7 @@ Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( } return status; -} +} */ Status<LocalFence> ProducerChannel::OnConsumerAcquire(Message& /*message*/) { ATRACE_NAME("ProducerChannel::OnConsumerAcquire"); @@ -480,17 +503,17 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, } } - OnConsumerIgnored(); + DecrementPendingConsumers(); if (pending_consumers_ == 0) { // Clear the producer bit atomically to transit into released state. This // has to done by BufferHub as it requries synchronization among all // consumers. BufferHubDefs::ModifyBufferState(buffer_state_, - BufferHubDefs::kProducerStateBit, 0ULL); + BufferHubDefs::kFirstClientBitMask, 0ULL); ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: releasing last consumer: " "buffer_id=%d state=%" PRIx64 ".", - buffer_id(), buffer_state_->load()); + buffer_id(), buffer_state_->load(std::memory_order_acquire)); if (orphaned_consumer_bit_mask_) { ALOGW( @@ -505,49 +528,52 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, SignalAvailable(); } - ALOGE_IF(pending_consumers_ && - BufferHubDefs::IsBufferReleased(buffer_state_->load()), - "ProducerChannel::OnConsumerRelease: buffer state inconsistent: " - "pending_consumers=%d, buffer buffer is in releaed state.", - pending_consumers_); + ALOGE_IF( + pending_consumers_ && BufferHubDefs::IsBufferReleased( + buffer_state_->load(std::memory_order_acquire)), + "ProducerChannel::OnConsumerRelease: buffer state inconsistent: " + "pending_consumers=%d, buffer buffer is in releaed state.", + pending_consumers_); return {}; } -void ProducerChannel::OnConsumerIgnored() { +void ProducerChannel::DecrementPendingConsumers() { if (pending_consumers_ == 0) { - ALOGE("ProducerChannel::OnConsumerIgnored: no pending consumer."); + ALOGE("ProducerChannel::DecrementPendingConsumers: no pending consumer."); return; } --pending_consumers_; ALOGD_IF(TRACE, - "ProducerChannel::OnConsumerIgnored: buffer_id=%d %d consumers left", + "ProducerChannel::DecrementPendingConsumers: buffer_id=%d %d " + "consumers left", buffer_id(), pending_consumers_); } void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) { // Ignore the orphaned consumer. - OnConsumerIgnored(); + DecrementPendingConsumers(); - const uint64_t consumer_state_bit = channel->consumer_state_bit(); - ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_bit, + const uint64_t client_state_mask = channel->client_state_mask(); + ALOGE_IF(orphaned_consumer_bit_mask_ & client_state_mask, "ProducerChannel::OnConsumerOrphaned: Consumer " - "(consumer_state_bit=%" PRIx64 ") is already orphaned.", - consumer_state_bit); - orphaned_consumer_bit_mask_ |= consumer_state_bit; + "(client_state_mask=%" PRIx64 ") is already orphaned.", + client_state_mask); + orphaned_consumer_bit_mask_ |= client_state_mask; // Atomically clear the fence state bit as an orphaned consumer will never // signal a release fence. Also clear the buffer state as it won't be released // as well. - fence_state_->fetch_and(~consumer_state_bit); - BufferHubDefs::ModifyBufferState(buffer_state_, consumer_state_bit, 0ULL); + fence_state_->fetch_and(~client_state_mask); + BufferHubDefs::ModifyBufferState(buffer_state_, client_state_mask, 0ULL); ALOGW( "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer " - "buffer_id=%d consumer_state_bit=%" PRIx64 " queue_index=%" PRIu64 + "buffer_id=%d client_state_mask=%" PRIx64 " queue_index=%" PRIu64 " buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".", - buffer_id(), consumer_state_bit, metadata_header_->queue_index, - buffer_state_->load(), fence_state_->load()); + buffer_id(), client_state_mask, metadata_header_->queue_index, + buffer_state_->load(std::memory_order_acquire), + fence_state_->load(std::memory_order_acquire)); } void ProducerChannel::AddConsumer(ConsumerChannel* channel) { @@ -557,9 +583,12 @@ void ProducerChannel::AddConsumer(ConsumerChannel* channel) { void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) { consumer_channels_.erase( std::find(consumer_channels_.begin(), consumer_channels_.end(), channel)); - active_consumer_bit_mask_ &= ~channel->consumer_state_bit(); + // Restore the consumer state bit and make it visible in other threads that + // acquire the active_clients_bit_mask_. + active_clients_bit_mask_->fetch_and(~channel->client_state_mask(), + std::memory_order_release); - const uint64_t buffer_state = buffer_state_->load(); + const uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire); if (BufferHubDefs::IsBufferPosted(buffer_state) || BufferHubDefs::IsBufferAcquired(buffer_state)) { // The consumer client is being destoryed without releasing. This could @@ -572,10 +601,11 @@ void ProducerChannel::RemoveConsumer(ConsumerChannel* channel) { BufferHubDefs::IsBufferGained(buffer_state)) { // The consumer is being close while it is suppose to signal a release // fence. Signal the dummy fence here. - if (fence_state_->load() & channel->consumer_state_bit()) { + if (fence_state_->load(std::memory_order_acquire) & + channel->client_state_mask()) { epoll_event event; event.events = EPOLLIN; - event.data.u64 = channel->consumer_state_bit(); + event.data.u64 = channel->client_state_mask(); if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD, dummy_fence_fd_.Get(), &event) < 0) { ALOGE( diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp index c0c48c2dc1..6b5027c582 100644 --- a/services/vr/bufferhubd/producer_queue_channel.cpp +++ b/services/vr/bufferhubd/producer_queue_channel.cpp @@ -1,9 +1,8 @@ -#include "producer_queue_channel.h" - #include <inttypes.h> -#include "consumer_queue_channel.h" -#include "producer_channel.h" +#include <private/dvr/consumer_queue_channel.h> +#include <private/dvr/producer_channel.h> +#include <private/dvr/producer_queue_channel.h> using android::pdx::ErrorStatus; using android::pdx::Message; @@ -76,6 +75,11 @@ bool ProducerQueueChannel::HandleMessage(Message& message) { message); return true; + case BufferHubRPC::ProducerQueueInsertBuffer::Opcode: + DispatchRemoteMethod<BufferHubRPC::ProducerQueueInsertBuffer>( + *this, &ProducerQueueChannel::OnProducerQueueInsertBuffer, message); + return true; + case BufferHubRPC::ProducerQueueRemoveBuffer::Opcode: DispatchRemoteMethod<BufferHubRPC::ProducerQueueRemoveBuffer>( *this, &ProducerQueueChannel::OnProducerQueueRemoveBuffer, message); @@ -278,6 +282,81 @@ ProducerQueueChannel::AllocateBuffer(Message& message, uint32_t width, return {{std::move(buffer_handle), slot}}; } +Status<size_t> ProducerQueueChannel::OnProducerQueueInsertBuffer( + pdx::Message& message, int buffer_cid) { + ATRACE_NAME("ProducerQueueChannel::InsertBuffer"); + ALOGD_IF(TRACE, + "ProducerQueueChannel::InsertBuffer: channel_id=%d, buffer_cid=%d", + channel_id(), buffer_cid); + + if (capacity_ >= BufferHubRPC::kMaxQueueCapacity) { + ALOGE("ProducerQueueChannel::InsertBuffer: reaches kMaxQueueCapacity."); + return ErrorStatus(E2BIG); + } + auto producer_channel = std::static_pointer_cast<ProducerChannel>( + service()->GetChannel(buffer_cid)); + if (producer_channel == nullptr || + producer_channel->channel_type() != BufferHubChannel::kProducerType) { + // Rejects the request if the requested buffer channel is invalid and/or + // it's not a ProducerChannel. + ALOGE( + "ProducerQueueChannel::InsertBuffer: Invalid buffer_cid=%d, " + "producer_buffer=0x%p, channel_type=%d.", + buffer_cid, producer_channel.get(), + producer_channel == nullptr ? -1 : producer_channel->channel_type()); + return ErrorStatus(EINVAL); + } + if (producer_channel->GetActiveProcessId() != message.GetProcessId()) { + // Rejects the request if the requested buffer channel is not currently + // connected to the caller this is IPC request. This effectively prevents + // fake buffer_cid from being injected. + ALOGE( + "ProducerQueueChannel::InsertBuffer: Requested buffer channel " + "(buffer_cid=%d) is not connected to the calling process (pid=%d). " + "It's connected to a different process (pid=%d).", + buffer_cid, message.GetProcessId(), + producer_channel->GetActiveProcessId()); + return ErrorStatus(EINVAL); + } + uint64_t buffer_state = producer_channel->buffer_state(); + if (!BufferHubDefs::IsBufferGained(buffer_state)) { + // Rejects the request if the requested buffer is not in Gained state. + ALOGE( + "ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, " + "state=0x%" PRIx64 ") is not in gained state.", + buffer_cid, buffer_state); + return ErrorStatus(EINVAL); + } + + // Register the to-be-inserted buffer's channel_id into the first empty + // buffer slot. + size_t slot = 0; + for (; slot < BufferHubRPC::kMaxQueueCapacity; slot++) { + if (buffers_[slot].expired()) + break; + } + if (slot == BufferHubRPC::kMaxQueueCapacity) { + ALOGE( + "ProducerQueueChannel::AllocateBuffer: Cannot find empty slot for new " + "buffer allocation."); + return ErrorStatus(E2BIG); + } + + buffers_[slot] = producer_channel; + capacity_++; + + // Notify each consumer channel about the new buffer. + for (auto* consumer_channel : consumer_channels_) { + ALOGD( + "ProducerQueueChannel::AllocateBuffer: Notified consumer with new " + "buffer, buffer_cid=%d", + buffer_cid); + consumer_channel->RegisterNewBuffer(producer_channel, slot); + } + + return {slot}; +} + Status<void> ProducerQueueChannel::OnProducerQueueRemoveBuffer( Message& /*message*/, size_t slot) { if (buffers_[slot].expired()) { diff --git a/services/vr/bufferhubd/tests/Android.bp b/services/vr/bufferhubd/tests/Android.bp new file mode 100644 index 0000000000..c77d2d252c --- /dev/null +++ b/services/vr/bufferhubd/tests/Android.bp @@ -0,0 +1,56 @@ +cc_test { + name: "buffer_hub_binder_service-test", + srcs: ["buffer_hub_binder_service-test.cpp"], + cflags: [ + "-DLOG_TAG=\"buffer_hub_binder_service-test\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + ], + header_libs: ["libdvr_headers"], + static_libs: [ + "libbufferhub", + "libbufferhubd", + "libgmock", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libpdx_default_transport", + "libui", + "libutils", + ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + +cc_test { + name: "buffer_node-test", + srcs: ["buffer_node-test.cpp"], + cflags: [ + "-DLOG_TAG=\"buffer_node-test\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + ], + header_libs: [ + "libdvr_headers", + "libnativewindow_headers", + ], + static_libs: [ + "libbufferhub", + "libbufferhubd", + "libgmock", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libpdx_default_transport", + "libui", + "libutils", + ], + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + diff --git a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp new file mode 100644 index 0000000000..7c00fa6e6f --- /dev/null +++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp @@ -0,0 +1,85 @@ +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <private/dvr/IBufferClient.h> +#include <private/dvr/IBufferHub.h> +#include <ui/PixelFormat.h> + +namespace android { +namespace dvr { + +namespace { + +using testing::IsNull; +using testing::NotNull; + +const int kWidth = 640; +const int kHeight = 480; +const int kLayerCount = 1; +const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +const int kUsage = 0; +const size_t kUserMetadataSize = 0; + +class BufferHubBinderServiceTest : public ::testing::Test { + protected: + void SetUp() override { + status_t ret = getService<IBufferHub>( + String16(IBufferHub::getServiceName()), &service); + ASSERT_EQ(ret, OK); + ASSERT_THAT(service, NotNull()); + } + + sp<IBufferHub> service; +}; + +TEST_F(BufferHubBinderServiceTest, TestCreateBuffer) { + sp<IBufferClient> bufferClient = service->createBuffer( + kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + ASSERT_THAT(bufferClient, NotNull()); + EXPECT_TRUE(bufferClient->isValid()); +} + +TEST_F(BufferHubBinderServiceTest, TestDuplicateAndImportBuffer) { + sp<IBufferClient> bufferClient = service->createBuffer( + kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + ASSERT_THAT(bufferClient, NotNull()); + EXPECT_TRUE(bufferClient->isValid()); + + uint64_t token1 = 0ULL; + status_t ret = bufferClient->duplicate(&token1); + EXPECT_EQ(ret, NO_ERROR); + + // Tokens should be unique even corresponding to the same buffer + uint64_t token2 = 0ULL; + ret = bufferClient->duplicate(&token2); + EXPECT_EQ(ret, NO_ERROR); + EXPECT_NE(token2, token1); + + sp<IBufferClient> bufferClient1; + ret = service->importBuffer(token1, &bufferClient1); + EXPECT_EQ(ret, NO_ERROR); + ASSERT_THAT(bufferClient1, NotNull()); + EXPECT_TRUE(bufferClient1->isValid()); + + // Consumes the token to keep the service clean + sp<IBufferClient> bufferClient2; + ret = service->importBuffer(token2, &bufferClient2); + EXPECT_EQ(ret, NO_ERROR); + ASSERT_THAT(bufferClient2, NotNull()); + EXPECT_TRUE(bufferClient2->isValid()); +} + +TEST_F(BufferHubBinderServiceTest, TestImportUnexistingToken) { + // There is very little chance that this test fails if there is a token = 0 + // in the service. + uint64_t unexistingToken = 0ULL; + sp<IBufferClient> bufferClient; + status_t ret = service->importBuffer(unexistingToken, &bufferClient); + EXPECT_EQ(ret, PERMISSION_DENIED); + EXPECT_THAT(bufferClient, IsNull()); +} + +} // namespace + +} // namespace dvr +} // namespace android
\ No newline at end of file diff --git a/services/vr/bufferhubd/tests/buffer_node-test.cpp b/services/vr/bufferhubd/tests/buffer_node-test.cpp new file mode 100644 index 0000000000..30ecbecdd1 --- /dev/null +++ b/services/vr/bufferhubd/tests/buffer_node-test.cpp @@ -0,0 +1,105 @@ +#include <errno.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <private/dvr/buffer_node.h> +#include <ui/GraphicBufferMapper.h> + +namespace android { +namespace dvr { + +namespace { + +using testing::NotNull; + +const uint32_t kWidth = 640; +const uint32_t kHeight = 480; +const uint32_t kLayerCount = 1; +const uint32_t kFormat = 1; +const uint64_t kUsage = 0; +const size_t kUserMetadataSize = 0; +const size_t kMaxClientsCount = BufferHubDefs::kMaxNumberOfClients; + +class BufferNodeTest : public ::testing::Test { + protected: + void SetUp() override { + buffer_node = new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_TRUE(buffer_node->IsValid()); + } + + void TearDown() override { + if (buffer_node != nullptr) { + delete buffer_node; + } + } + + BufferNode* buffer_node = nullptr; +}; + +TEST_F(BufferNodeTest, TestCreateBufferNode) { + EXPECT_EQ(buffer_node->user_metadata_size(), kUserMetadataSize); + // Test the handle just allocated is good (i.e. able to be imported) + GraphicBufferMapper& mapper = GraphicBufferMapper::get(); + const native_handle_t* outHandle; + status_t ret = mapper.importBuffer( + buffer_node->buffer_handle(), buffer_node->buffer_desc().width, + buffer_node->buffer_desc().height, buffer_node->buffer_desc().layers, + buffer_node->buffer_desc().format, buffer_node->buffer_desc().usage, + buffer_node->buffer_desc().stride, &outHandle); + EXPECT_EQ(ret, OK); + EXPECT_THAT(outHandle, NotNull()); +} + +TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) { + uint64_t new_client_state_mask_1 = + buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_client_state_mask_1); + + // Request and add a new client_state_mask again. + // Active clients bit mask should be the union of the two new + // client_state_masks. + uint64_t new_client_state_mask_2 = + buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), + new_client_state_mask_1 | new_client_state_mask_2); +} + +TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) { + uint64_t new_client_state_mask = 0ULL; + uint64_t current_mask = 0ULL; + uint64_t expected_mask = 0ULL; + + for (int i = 0; i < kMaxClientsCount; ++i) { + new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_NE(new_client_state_mask, 0); + EXPECT_FALSE(new_client_state_mask & current_mask); + expected_mask = current_mask | new_client_state_mask; + current_mask = buffer_node->GetActiveClientsBitMask(); + EXPECT_EQ(current_mask, expected_mask); + } + + // Method should fail upon requesting for more than maximum allowable clients. + new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(new_client_state_mask, 0ULL); + EXPECT_EQ(errno, E2BIG); +} + +TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) { + buffer_node->AddNewActiveClientsBitToMask(); + uint64_t current_mask = buffer_node->GetActiveClientsBitMask(); + uint64_t new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask); + + buffer_node->RemoveClientsBitFromMask(new_client_state_mask); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask); + + // Remove the test_mask again to the active client bit mask should not modify + // the value of active clients bit mask. + buffer_node->RemoveClientsBitFromMask(new_client_state_mask); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask); +} + +} // namespace + +} // namespace dvr +} // namespace android diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp index 90edf69697..003775b7dc 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -39,6 +39,10 @@ cc_library_shared { "android.hardware.graphics.composer@2.1-hal", ], + export_static_lib_headers: [ + "libdisplay", + ], + export_shared_lib_headers: [ "android.frameworks.vr.composer@1.0", "android.hardware.graphics.composer@2.1", @@ -48,6 +52,7 @@ cc_library_shared { cflags: [ "-DLOG_TAG=\"vr_hwc\"", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-Wall", "-Werror", // mVrClient unused in vr_composer_client.cpp @@ -115,6 +120,7 @@ cc_library_static { cc_binary { name: "vr_hwc", + vintf_fragments: ["manifest_vr_hwc.xml"], srcs: [ "vr_hardware_composer_service.cpp" ], diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp index 4af47d2a35..417460cdce 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ b/services/vr/hardware_composer/impl/vr_hwc.cpp @@ -16,9 +16,11 @@ #include "impl/vr_hwc.h" #include "android-base/stringprintf.h" +#include <binder/IServiceManager.h> #include <cutils/properties.h> #include <private/dvr/display_client.h> #include <ui/Fence.h> +#include <utils/Trace.h> #include <mutex> @@ -244,29 +246,38 @@ void HwcDisplay::dumpDebugInfo(std::string* result) const { //////////////////////////////////////////////////////////////////////////////// // VrHwcClient -VrHwc::VrHwc() {} +VrHwc::VrHwc() { + vsync_callback_ = new VsyncCallback; +} -VrHwc::~VrHwc() {} +VrHwc::~VrHwc() { + vsync_callback_->SetEventCallback(nullptr); +} bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; } void VrHwc::registerEventCallback(EventCallback* callback) { - { - std::lock_guard<std::mutex> guard(mutex_); - event_callback_ = callback; - int32_t width, height; - GetPrimaryDisplaySize(&width, &height); - // Create the primary display late to avoid initialization issues between - // VR HWC and SurfaceFlinger. - displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height)); - } + std::unique_lock<std::mutex> lock(mutex_); + event_callback_ = callback; + int32_t width, height; + GetPrimaryDisplaySize(&width, &height); + // Create the primary display late to avoid initialization issues between + // VR HWC and SurfaceFlinger. + displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height)); + + // Surface flinger will make calls back into vr_hwc when it receives the + // onHotplug() call, so it's important to release mutex_ here. + lock.unlock(); event_callback_->onHotplug(kDefaultDisplayId, IComposerCallback::Connection::CONNECTED); + lock.lock(); + UpdateVsyncCallbackEnabledLocked(); } void VrHwc::unregisterEventCallback() { std::lock_guard<std::mutex> guard(mutex_); event_callback_ = nullptr; + UpdateVsyncCallbackEnabledLocked(); } uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; } @@ -321,10 +332,14 @@ Error VrHwc::getActiveConfig(Display display, Config* outConfig) { return Error::NONE; } -Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */, +Error VrHwc::getClientTargetSupport(Display display, uint32_t /* width */, uint32_t /* height */, PixelFormat /* format */, Dataspace /* dataspace */) { + std::lock_guard<std::mutex> guard(mutex_); + if (!FindDisplay(display)) + return Error::BAD_DISPLAY; + return Error::NONE; } @@ -455,16 +470,37 @@ Error VrHwc::setColorMode(Display display, ColorMode mode) { if (!display_ptr) return Error::BAD_DISPLAY; + if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3) + return Error::BAD_PARAMETER; + display_ptr->set_color_mode(mode); return Error::NONE; } Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) { + bool dozeSupported = false; + + Error dozeSupportError = getDozeSupport(display, &dozeSupported); + + if (dozeSupportError != Error::NONE) + return dozeSupportError; + std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); if (!display_ptr) return Error::BAD_DISPLAY; + if (mode < IComposerClient::PowerMode::OFF || + mode > IComposerClient::PowerMode::DOZE_SUSPEND) { + return Error::BAD_PARAMETER; + } + + if (!dozeSupported && + (mode == IComposerClient::PowerMode::DOZE || + mode == IComposerClient::PowerMode::DOZE_SUSPEND)) { + return Error::UNSUPPORTED; + } + display_ptr->set_power_mode(mode); return Error::NONE; } @@ -475,8 +511,45 @@ Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) { if (!display_ptr) return Error::BAD_DISPLAY; - display_ptr->set_vsync_enabled(enabled); - return Error::NONE; + if (enabled != IComposerClient::Vsync::ENABLE && + enabled != IComposerClient::Vsync::DISABLE) { + return Error::BAD_PARAMETER; + } + + Error set_vsync_result = Error::NONE; + if (display == kDefaultDisplayId) { + sp<IVsyncService> vsync_service = interface_cast<IVsyncService>( + defaultServiceManager()->getService( + String16(IVsyncService::GetServiceName()))); + if (vsync_service == nullptr) { + ALOGE("Failed to get vsync service"); + return Error::NO_RESOURCES; + } + + if (enabled == IComposerClient::Vsync::ENABLE) { + ALOGI("Enable vsync"); + display_ptr->set_vsync_enabled(true); + status_t result = vsync_service->registerCallback(vsync_callback_); + if (result != OK) { + ALOGE("%s service registerCallback() failed: %s (%d)", + IVsyncService::GetServiceName(), strerror(-result), result); + set_vsync_result = Error::NO_RESOURCES; + } + } else if (enabled == IComposerClient::Vsync::DISABLE) { + ALOGI("Disable vsync"); + display_ptr->set_vsync_enabled(false); + status_t result = vsync_service->unregisterCallback(vsync_callback_); + if (result != OK) { + ALOGE("%s service unregisterCallback() failed: %s (%d)", + IVsyncService::GetServiceName(), strerror(-result), result); + set_vsync_result = Error::NO_RESOURCES; + } + } + + UpdateVsyncCallbackEnabledLocked(); + } + + return set_vsync_result; } Error VrHwc::setColorTransform(Display display, const float* matrix, @@ -559,7 +632,8 @@ Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, frame.display_height = display_ptr->height(); frame.active_config = display_ptr->active_config(); frame.power_mode = display_ptr->power_mode(); - frame.vsync_enabled = display_ptr->vsync_enabled(); + frame.vsync_enabled = display_ptr->vsync_enabled() ? + IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE; frame.color_transform_hint = display_ptr->color_transform_hint(); frame.color_mode = display_ptr->color_mode(); memcpy(frame.color_transform, display_ptr->color_transform(), @@ -911,6 +985,15 @@ HwcDisplay* VrHwc::FindDisplay(Display display) { return iter == displays_.end() ? nullptr : iter->second.get(); } +void VrHwc::UpdateVsyncCallbackEnabledLocked() { + auto primary_display = FindDisplay(kDefaultDisplayId); + LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr, + "Should have created the primary display by now"); + bool send_vsync = + event_callback_ != nullptr && primary_display->vsync_enabled(); + vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr); +} + void HwcLayer::dumpDebugInfo(std::string* result) const { if (!result) { return; @@ -928,5 +1011,18 @@ void HwcLayer::dumpDebugInfo(std::string* result) const { buffer_metadata.layerCount, buffer_metadata.format); } +status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) { + ATRACE_NAME("vr_hwc onVsync"); + std::lock_guard<std::mutex> guard(mutex_); + if (callback_ != nullptr) + callback_->onVsync(kDefaultDisplayId, vsync_timestamp); + return NO_ERROR; +} + +void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) { + std::lock_guard<std::mutex> guard(mutex_); + callback_ = callback; +} + } // namespace dvr } // namespace android diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h index 85e587abbe..f9872b2290 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.h +++ b/services/vr/hardware_composer/impl/vr_hwc.h @@ -20,6 +20,7 @@ #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> #include <android/hardware/graphics/composer/2.1/IComposer.h> #include <composer-hal/2.1/ComposerHal.h> +#include <private/dvr/vsync_service.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> @@ -156,10 +157,8 @@ class HwcDisplay { IComposerClient::PowerMode power_mode() const { return power_mode_; } void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; } - IComposerClient::Vsync vsync_enabled() const { return vsync_enabled_; } - void set_vsync_enabled(IComposerClient::Vsync vsync) { - vsync_enabled_ = vsync; - } + bool vsync_enabled() const { return vsync_enabled_; } + void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;} const float* color_transform() const { return color_transform_; } int32_t color_transform_hint() const { return color_transform_hint_; } @@ -187,7 +186,7 @@ class HwcDisplay { Config active_config_; ColorMode color_mode_; IComposerClient::PowerMode power_mode_; - IComposerClient::Vsync vsync_enabled_; + bool vsync_enabled_ = false; float color_transform_[16]; int32_t color_transform_hint_; @@ -299,8 +298,23 @@ class VrHwc : public IComposer, public ComposerHal, public ComposerView { void UnregisterObserver(Observer* observer) override; private: + class VsyncCallback : public BnVsyncCallback { + public: + status_t onVsync(int64_t vsync_timestamp) override; + void SetEventCallback(EventCallback* callback); + private: + std::mutex mutex_; + EventCallback* callback_; + }; + HwcDisplay* FindDisplay(Display display); + // Re-evaluate whether or not we should start making onVsync() callbacks to + // the client. We need enableCallback(true) to have been called, and + // setVsyncEnabled() to have been called for the primary display. The caller + // must have mutex_ locked already. + void UpdateVsyncCallbackEnabledLocked(); + wp<VrComposerClient> client_; // Guard access to internal state from binder threads. @@ -312,6 +326,8 @@ class VrHwc : public IComposer, public ComposerHal, public ComposerView { EventCallback* event_callback_ = nullptr; Observer* observer_ = nullptr; + sp<VsyncCallback> vsync_callback_; + VrHwc(const VrHwc&) = delete; void operator=(const VrHwc&) = delete; }; diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml new file mode 100644 index 0000000000..1068cac33a --- /dev/null +++ b/services/vr/hardware_composer/manifest_vr_hwc.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="framework"> + <hal> + <name>android.hardware.graphics.composer</name> + <transport>hwbinder</transport> + <version>2.1</version> + <interface> + <name>IComposer</name> + <instance>vr</instance> + </interface> + </hal> +</manifest> diff --git a/services/vr/performanced/cpu_set.cpp b/services/vr/performanced/cpu_set.cpp index 1a7264c701..d940b79086 100644 --- a/services/vr/performanced/cpu_set.cpp +++ b/services/vr/performanced/cpu_set.cpp @@ -106,7 +106,7 @@ std::vector<CpuSet*> CpuSetManager::GetCpuSets() { return sets; } -std::string CpuSetManager::DumpState() const { +void CpuSetManager::DumpState(std::ostringstream& stream) const { size_t max_path = 0; std::vector<CpuSet*> sets; @@ -119,8 +119,6 @@ std::string CpuSetManager::DumpState() const { return a->path() < b->path(); }); - std::ostringstream stream; - stream << std::left; stream << std::setw(max_path) << "Path"; stream << " "; @@ -146,8 +144,6 @@ std::string CpuSetManager::DumpState() const { stream << std::setw(6) << set->GetTasks().size(); stream << std::endl; } - - return stream.str(); } void CpuSetManager::MoveUnboundTasks(const std::string& target_set) { diff --git a/services/vr/performanced/cpu_set.h b/services/vr/performanced/cpu_set.h index 6879272dc4..4c25e9ec4a 100644 --- a/services/vr/performanced/cpu_set.h +++ b/services/vr/performanced/cpu_set.h @@ -5,6 +5,7 @@ #include <memory> #include <mutex> +#include <sstream> #include <string> #include <unordered_map> #include <vector> @@ -83,7 +84,7 @@ class CpuSetManager { // to shield the system from interference from unbound kernel threads. void MoveUnboundTasks(const std::string& target_set); - std::string DumpState() const; + void DumpState(std::ostringstream& stream) const; operator bool() const { return root_set_ != nullptr; } diff --git a/services/vr/performanced/performance_service.cpp b/services/vr/performanced/performance_service.cpp index 4c26671b11..73dcf76fd0 100644 --- a/services/vr/performanced/performance_service.cpp +++ b/services/vr/performanced/performance_service.cpp @@ -1,5 +1,7 @@ #include "performance_service.h" +#include <sstream> + #include <sched.h> #include <sys/prctl.h> #include <unistd.h> @@ -31,6 +33,10 @@ const char kCpuSetBasePath[] = "/dev/cpuset"; const char kRootCpuSet[] = "/"; +const char kVrAppRenderPolicy[] = "vr:app:render"; + +const bool kAllowAppsToRequestVrAppRenderPolicy = false; + constexpr unsigned long kTimerSlackForegroundNs = 50000; constexpr unsigned long kTimerSlackBackgroundNs = 40000000; @@ -124,9 +130,6 @@ PerformanceService::PerformanceService() // TODO(eieio): Replace this witha device-specific config file. This is just a // hack for now to put some form of permission logic in place while a longer // term solution is developed. - using AllowRootSystem = - CheckAnd<SameProcess, - CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>; using AllowRootSystemGraphics = CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>, GroupId<AID_SYSTEM, AID_GRAPHICS>>>; @@ -136,6 +139,17 @@ PerformanceService::PerformanceService() using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>; + auto vr_app_render_permission_check = []( + const pdx::Message& sender, const Task& task) { + // For vr:app:render, in addition to system/root apps and VrCore, we + // also allow apps to request vr:app:render if + // kAllowAppsToRequestVrAppRenderPolicy == true, but not for other + // apps, only for themselves. + return (task && task.thread_group_id() == sender.GetProcessId() && + kAllowAppsToRequestVrAppRenderPolicy) + || AllowRootSystemTrusted::Check(sender, task); + }; + partition_permission_check_ = AllowRootSystemTrusted::Check; // Setup the scheduler classes. @@ -170,28 +184,28 @@ PerformanceService::PerformanceService() {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low, - .permission_check = AllowRootSystem::Check}}, + .permission_check = AllowRootSystemTrusted::Check}}, {"sensors:low", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low, - .permission_check = AllowRootSystem::Check}}, + .permission_check = AllowRootSystemTrusted::Check}}, {"sensors:high", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low + 1, - .permission_check = AllowRootSystem::Check}}, + .permission_check = AllowRootSystemTrusted::Check}}, {"vr:system:arp", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 2, .permission_check = AllowRootSystemTrusted::Check, "/system/performance"}}, - {"vr:app:render", + {kVrAppRenderPolicy, {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 1, - .permission_check = AllowRootSystemTrusted::Check, + .permission_check = vr_app_render_permission_check, "/application/performance"}}, {"normal", {.timer_slack = kTimerSlackForegroundNs, @@ -218,7 +232,10 @@ bool PerformanceService::IsInitialized() const { } std::string PerformanceService::DumpState(size_t /*max_length*/) { - return cpuset_.DumpState(); + std::ostringstream stream; + stream << "vr_app_render_thread: " << vr_app_render_thread_ << std::endl; + cpuset_.DumpState(stream); + return stream.str(); } Status<void> PerformanceService::OnSetSchedulerPolicy( @@ -244,7 +261,12 @@ Status<void> PerformanceService::OnSetSchedulerPolicy( // Make sure the sending process is allowed to make the requested change to // this task. if (!config.IsAllowed(message, task)) - return ErrorStatus(EINVAL); + return ErrorStatus(EPERM); + + if (scheduler_policy == kVrAppRenderPolicy) { + // We only allow one vr:app:render thread at a time + SetVrAppRenderThread(task_id); + } // Get the thread group's cpu set. Policies that do not specify a cpuset // should default to this cpuset. @@ -302,14 +324,16 @@ Status<void> PerformanceService::OnSetSchedulerPolicy( Status<void> PerformanceService::OnSetCpuPartition( Message& message, pid_t task_id, const std::string& partition) { Task task(task_id); - if (!task || task.thread_group_id() != message.GetProcessId()) + if (!task) return ErrorStatus(EINVAL); + if (task.thread_group_id() != message.GetProcessId()) + return ErrorStatus(EPERM); // Temporary permission check. // TODO(eieio): Replace this with a configuration file. if (partition_permission_check_ && !partition_permission_check_(message, task)) { - return ErrorStatus(EINVAL); + return ErrorStatus(EPERM); } auto target_set = cpuset_.Lookup(partition); @@ -336,7 +360,12 @@ Status<void> PerformanceService::OnSetSchedulerClass( // Make sure the sending process is allowed to make the requested change to // this task. if (!config.IsAllowed(message, task)) - return ErrorStatus(EINVAL); + return ErrorStatus(EPERM); + + if (scheduler_class == kVrAppRenderPolicy) { + // We only allow one vr:app:render thread at a time + SetVrAppRenderThread(task_id); + } struct sched_param param; param.sched_priority = config.priority; @@ -359,8 +388,10 @@ Status<std::string> PerformanceService::OnGetCpuPartition(Message& message, pid_t task_id) { // Make sure the task id is valid and belongs to the sending process. Task task(task_id); - if (!task || task.thread_group_id() != message.GetProcessId()) + if (!task) return ErrorStatus(EINVAL); + if (task.thread_group_id() != message.GetProcessId()) + return ErrorStatus(EPERM); return task.GetCpuSetPath(); } @@ -393,5 +424,38 @@ Status<void> PerformanceService::HandleMessage(Message& message) { } } +void PerformanceService::SetVrAppRenderThread(pid_t new_vr_app_render_thread) { + ALOGI("SetVrAppRenderThread old=%d new=%d", + vr_app_render_thread_, new_vr_app_render_thread); + + if (vr_app_render_thread_ >= 0 && + vr_app_render_thread_ != new_vr_app_render_thread) { + // Restore the default scheduler policy and priority on the previous + // vr:app:render thread. + struct sched_param param; + param.sched_priority = 0; + if (sched_setscheduler(vr_app_render_thread_, SCHED_NORMAL, ¶m) < 0) { + if (errno == ESRCH) { + ALOGI("Failed to revert %s scheduler policy. Couldn't find thread %d." + " Was the app killed?", kVrAppRenderPolicy, vr_app_render_thread_); + } else { + ALOGE("Failed to revert %s scheduler policy: %s", + kVrAppRenderPolicy, strerror(errno)); + } + } + + // Restore the default timer slack on the previous vr:app:render thread. + prctl(PR_SET_TIMERSLACK_PID, kTimerSlackForegroundNs, + vr_app_render_thread_); + } + + // We could also reset the thread's cpuset here, but the cpuset is already + // managed by Android. Better to let Android adjust the cpuset as the app + // moves to the background, rather than adjust it ourselves here, and risk + // stomping on the value set by Android. + + vr_app_render_thread_ = new_vr_app_render_thread; +} + } // namespace dvr } // namespace android diff --git a/services/vr/performanced/performance_service.h b/services/vr/performanced/performance_service.h index 6b519abac3..fe63756dbd 100644 --- a/services/vr/performanced/performance_service.h +++ b/services/vr/performanced/performance_service.h @@ -39,6 +39,14 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { pdx::Status<std::string> OnGetCpuPartition(pdx::Message& message, pid_t task_id); + // Set which thread gets the vr:app:render policy. Only one thread at a time + // is allowed to have vr:app:render. If multiple threads are allowed + // vr:app:render, and those threads busy loop, the system can freeze. When + // SetVrAppRenderThread() is called, the thread which we had previously + // assigned vr:app:render will have its scheduling policy reset to default + // values. + void SetVrAppRenderThread(pid_t new_vr_app_render_thread); + CpuSetManager cpuset_; int sched_fifo_min_priority_; @@ -70,6 +78,8 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { std::function<bool(const pdx::Message& message, const Task& task)> partition_permission_check_; + pid_t vr_app_render_thread_ = -1; + PerformanceService(const PerformanceService&) = delete; void operator=(const PerformanceService&) = delete; }; diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index fdbd969d66..a607a5d098 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -984,7 +984,7 @@ VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, uint32_t icd_api_version; PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version = reinterpret_cast<PFN_vkEnumerateInstanceVersion>( - Hal::Device().GetInstanceProcAddr(NULL, + Hal::Device().GetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); if (!pfn_enumerate_instance_version) { icd_api_version = VK_API_VERSION_1_0; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 3db8a3962e..86013737c6 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -335,15 +335,15 @@ uint32_t get_num_ready_timings(Swapchain& swapchain) { swapchain.surface.window.get(), ti.native_frame_id_, &desired_present_time, &render_complete_time, &composition_latch_time, - NULL, //&first_composition_start_time, - NULL, //&last_composition_start_time, - NULL, //&composition_finish_time, + nullptr, //&first_composition_start_time, + nullptr, //&last_composition_start_time, + nullptr, //&composition_finish_time, // TODO(ianelliott): Maybe ask if this one is // supported, at startup time (since it may not be // supported): &actual_present_time, - NULL, //&dequeue_ready_time, - NULL /*&reads_done_time*/); + nullptr, //&dequeue_ready_time, + nullptr /*&reads_done_time*/); if (ret != android::NO_ERROR) { continue; @@ -569,21 +569,17 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, switch (native_format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_FP16: format_supported = true; break; default: break; } - // USAGE_CPU_READ_MASK 0xFUL - // USAGE_CPU_WRITE_MASK (0xFUL << 4) - // The currently used bits are as below: - // USAGE_CPU_READ_RARELY = 2UL - // USAGE_CPU_READ_OFTEN = 3UL - // USAGE_CPU_WRITE_RARELY = (2UL << 4) - // USAGE_CPU_WRITE_OFTEN = (3UL << 4) - *supported = static_cast<VkBool32>(format_supported || - (surface->consumer_usage & 0xFFUL) == 0); + *supported = static_cast<VkBool32>( + format_supported || (surface->consumer_usage & + (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) == 0); return VK_SUCCESS; } @@ -700,6 +696,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, + {VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}, + {VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}, }; const uint32_t kNumWideColorFormats = sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]); diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp index 93620f4157..78d6694270 100644 --- a/vulkan/vkjson/Android.bp +++ b/vulkan/vkjson/Android.bp @@ -10,7 +10,6 @@ cc_library_static { "-Wimplicit-fallthrough", ], cppflags: [ - "-std=c++11", "-Wno-sign-compare", ], export_include_dirs: [ @@ -37,7 +36,6 @@ cc_library_static { "-Wimplicit-fallthrough", ], cppflags: [ - "-std=c++11", "-Wno-sign-compare", ], export_include_dirs: [ |