diff options
129 files changed, 6750 insertions, 978 deletions
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index b3cbdef67f..b850390d93 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp @@ -3,6 +3,10 @@ cc_binary { name: "atrace", srcs: ["atrace.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbinder", diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 9cdc9e906d..9dbbb7757c 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -226,8 +226,11 @@ static const char* k_traceClockPath = static const char* k_traceBufferSizePath = "buffer_size_kb"; +#if 0 +// TODO: Re-enable after stabilization static const char* k_traceCmdlineSizePath = "saved_cmdlines_size"; +#endif static const char* k_tracingOverwriteEnablePath = "options/overwrite"; @@ -250,9 +253,6 @@ static const char* k_funcgraphProcPath = static const char* k_funcgraphFlatPath = "options/funcgraph-flat"; -static const char* k_funcgraphDurationPath = - "options/funcgraph-duration"; - static const char* k_ftraceFilterPath = "set_ftrace_filter"; @@ -445,7 +445,6 @@ static bool clearTrace() static bool setTraceBufferSizeKB(int size) { char str[32] = "1"; - int len; if (size < 1) { size = 1; } @@ -453,6 +452,8 @@ static bool setTraceBufferSizeKB(int size) return writeStr(k_traceBufferSizePath, str); } +#if 0 +// TODO: Re-enable after stabilization // Set the default size of cmdline hashtable static bool setCmdlineSize() { @@ -461,6 +462,7 @@ static bool setCmdlineSize() } return true; } +#endif // Set the clock to the best available option while tracing. Use 'boot' if it's // available; otherwise, use 'mono'. If neither are available use 'global'. @@ -481,8 +483,8 @@ static bool setClock() newClock = "global"; } - size_t begin = clockStr.find("[") + 1; - size_t end = clockStr.find("]"); + size_t begin = clockStr.find('[') + 1; + size_t end = clockStr.find(']'); if (newClock.compare(0, std::string::npos, clockStr, begin, end-begin) == 0) { return true; } @@ -543,7 +545,7 @@ static void pokeHalServices() auto listRet = sm->list([&](const auto &interfaces) { for (size_t i = 0; i < interfaces.size(); i++) { string fqInstanceName = interfaces[i]; - string::size_type n = fqInstanceName.find("/"); + string::size_type n = fqInstanceName.find('/'); if (n == std::string::npos || interfaces[i].size() == n+1) continue; hidl_string fqInterfaceName = fqInstanceName.substr(0, n); diff --git a/cmds/bugreport/Android.bp b/cmds/bugreport/Android.bp index 139e4b2bf1..24044a64b0 100644 --- a/cmds/bugreport/Android.bp +++ b/cmds/bugreport/Android.bp @@ -1,6 +1,6 @@ cc_binary { name: "bugreport", srcs: ["bugreport.cpp"], - cflags: ["-Wall"], + cflags: ["-Wall", "-Werror"], shared_libs: ["libcutils"], } diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index f0b6203578..5149b7fc4f 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -18,6 +18,7 @@ #include "DumpstateInternal.h" +#include <errno.h> #include <grp.h> #include <pwd.h> #include <stdint.h> diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index e866b8b652..ea7109bacd 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -103,6 +103,12 @@ CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::As return *this; } +CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() { + if (!PropertiesHelper::IsUserBuild()) + values.account_mode_ = SU_ROOT; + return *this; +} + CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() { values.account_mode_ = DROP_ROOT; return *this; diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 5a8ce5b055..698ceffcc4 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -89,6 +89,8 @@ class CommandOptions { CommandOptionsBuilder& Always(); /* Sets the command's PrivilegeMode as `SU_ROOT` */ CommandOptionsBuilder& AsRoot(); + /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */ + CommandOptionsBuilder& AsRootIfAvailable(); /* Sets the command's PrivilegeMode as `DROP_ROOT` */ CommandOptionsBuilder& DropRoot(); /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */ diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 5f93e9906a..79397d2b4f 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -697,7 +697,7 @@ bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { std::string valid_name = entry_name; // Rename extension if necessary. - size_t idx = entry_name.rfind("."); + size_t idx = entry_name.rfind('.'); if (idx != std::string::npos) { std::string extension = entry_name.substr(idx); std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); @@ -1069,14 +1069,15 @@ static void dumpstate() { RunCommand( "HARDWARE HALS", {"lshal", std::string("--debug=") + kLsHalDebugPath}, - CommandOptions::AS_ROOT); + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath); unlink(kLsHalDebugPath.c_str()); } else { RunCommand( - "HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::AS_ROOT); + "HARDWARE HALS", {"lshal", "--debug"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); } RunCommand("PRINTENV", {"printenv"}); @@ -1438,7 +1439,7 @@ bool Dumpstate::FinishZipFile() { return true; } -static std::string SHA256_file_hash(std::string filepath) { +static std::string SHA256_file_hash(const std::string& filepath) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC | O_NOFOLLOW))); if (fd == -1) { diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index a94cf9978a..92b0c0d8bc 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -477,6 +477,48 @@ TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) { EXPECT_THAT(err, StrEq("stderr\n")); } +TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n") + return; + } + if (!PropertiesHelper::IsUserBuild()) { + // Emulates user build if necessarily. + SetBuildType("user"); + } + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + +TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE("Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n") + return; + } + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); + return; + } + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + EXPECT_THAT(out, StrEq("0\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) { EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist")); EXPECT_THAT(out, @@ -1053,6 +1095,51 @@ TEST_F(DumpstateUtilTest, RunCommandAsRootNonUserBuild) { EXPECT_THAT(err, StrEq("stderr\n")); } + +TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnUserBuild) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnUserBuild() on test suite\n") + return; + } + CreateFd("RunCommandAsRootIfAvailableOnUserBuild.txt"); + if (!PropertiesHelper::IsUserBuild()) { + // Emulates user build if necessarily. + SetBuildType("user"); + } + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + +TEST_F(DumpstateUtilTest, RunCommandAsRootIfAvailableOnDebugBuild) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE("Skipping DumpstateUtilTest.RunCommandAsRootIfAvailableOnDebugBuild() on test suite\n") + return; + } + CreateFd("RunCommandAsRootIfAvailableOnDebugBuild.txt"); + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootNonUserBuild on user builds\n"); + return; + } + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + EXPECT_THAT(out, StrEq("0\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + TEST_F(DumpstateUtilTest, RunCommandDropRoot) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index fa6f6dfef3..32277499a6 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -177,7 +177,7 @@ int Dumpsys::main(int argc, char* const argv[]) { } for (size_t i = 0; i < N; i++) { - String16 service_name = std::move(services[i]); + const String16& service_name = std::move(services[i]); if (IsSkipped(skippedServices, service_name)) continue; sp<IBinder> service = sm_->checkService(service_name); diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp index 127e0f360d..39fcb80631 100644 --- a/cmds/dumpsys/tests/Android.bp +++ b/cmds/dumpsys/tests/Android.bp @@ -4,6 +4,7 @@ cc_test { test_suites: ["device-tests"], srcs: ["dumpsys_test.cpp"], + cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index cfd53e578c..16fefe64ba 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -96,7 +96,7 @@ MATCHER_P(AndroidElementsAre, expected, "") { } int i = 0; std::ostringstream actual_stream, expected_stream; - for (String16 actual : arg) { + for (const String16& actual : arg) { std::string actual_str = String8(actual).c_str(); std::string expected_str = expected[i]; actual_stream << "'" << actual_str << "' "; diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp index 3eb39b9bed..ea0cd9e4e2 100644 --- a/cmds/installd/CacheTracker.cpp +++ b/cmds/installd/CacheTracker.cpp @@ -60,7 +60,7 @@ void CacheTracker::loadStats() { ATRACE_BEGIN("loadStats tree"); cacheUsed = 0; - for (auto path : mDataPaths) { + for (const auto& path : mDataPaths) { auto cachePath = read_path_inode(path, "cache", kXattrInodeCache); auto codeCachePath = read_path_inode(path, "code_cache", kXattrInodeCodeCache); calculate_tree_size(cachePath, &cacheUsed); @@ -170,7 +170,7 @@ void CacheTracker::loadItems() { items.clear(); ATRACE_BEGIN("loadItems"); - for (auto path : mDataPaths) { + for (const auto& path : mDataPaths) { loadItemsFrom(read_path_inode(path, "cache", kXattrInodeCache)); loadItemsFrom(read_path_inode(path, "code_cache", kXattrInodeCodeCache)); } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 8a79ee17a6..4a93b1fd3d 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -525,6 +525,9 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st if (access(path.c_str(), F_OK) == 0) { if (delete_dir_contents(path) != 0) { res = error("Failed to delete contents of " + path); + } else if ((flags & (FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE_ONLY)) == 0) { + remove_path_xattr(path, kXattrInodeCache); + remove_path_xattr(path, kXattrInodeCodeCache); } } } @@ -1349,7 +1352,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - for (auto packageName : packageNames) { + for (const auto& packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); } // NOTE: Locking is relaxed on this method, since it's limited to @@ -1388,7 +1391,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri } ATRACE_BEGIN("obb"); - for (auto packageName : packageNames) { + for (const auto& packageName : packageNames) { auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } @@ -1396,7 +1399,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { ATRACE_BEGIN("code"); - for (auto codePath : codePaths) { + for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, multiuser_get_shared_gid(0, appId)); } @@ -1407,7 +1410,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri ATRACE_END(); } else { ATRACE_BEGIN("code"); - for (auto codePath : codePaths) { + for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize); } ATRACE_END(); diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index f29da17457..6f7ab6b52f 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -28,6 +28,7 @@ #include <unistd.h> #include <android-base/logging.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> @@ -67,6 +68,10 @@ static unique_fd invalid_unique_fd() { return unique_fd(-1); } +static bool is_debug_runtime() { + return android::base::GetProperty("persist.sys.dalvik.vm.lib.2", "") == "libartd.so"; +} + static bool clear_profile(const std::string& profile) { unique_fd ufd(open(profile.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC)); if (ufd.get() < 0) { @@ -267,7 +272,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd dex2oat_large_app_threshold); } - static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; + // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat. + const char* dex2oat_bin = is_debug_runtime() ? "/system/bin/dex2oatd" : "/system/bin/dex2oat"; static const char* RUNTIME_ARG = "--runtime-arg"; @@ -376,7 +382,7 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd } - ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name); + ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); const char* argv[9 // program name, mandatory arguments and the final NULL + (have_dex2oat_isa_variant ? 1 : 0) @@ -397,7 +403,7 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd + (has_base_dir ? 1 : 0) + (have_dex2oat_large_app_threshold ? 1 : 0)]; int i = 0; - argv[i++] = DEX2OAT_BIN; + argv[i++] = dex2oat_bin; argv[i++] = zip_fd_arg; argv[i++] = zip_location_arg; argv[i++] = input_vdex_fd_arg; @@ -463,8 +469,8 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; - execv(DEX2OAT_BIN, (char * const *)argv); - ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); + execv(dex2oat_bin, (char * const *)argv); + ALOGE("execv(%s) failed: %s\n", dex2oat_bin, strerror(errno)); } /* @@ -643,7 +649,7 @@ static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, const unique_fd& reference_profile_fd) { static const size_t MAX_INT_LEN = 32; - static const char* PROFMAN_BIN = "/system/bin/profman"; + const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman"; std::vector<std::string> profile_args(profiles_fd.size()); char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN]; @@ -657,7 +663,7 @@ static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, // program name, reference profile fd, the final NULL and the profile fds const char* argv[3 + profiles_fd.size()]; int i = 0; - argv[i++] = PROFMAN_BIN; + argv[i++] = profman_bin; argv[i++] = reference_profile_arg; for (size_t k = 0; k < profile_args.size(); k++) { argv[i++] = profile_args[k].c_str(); @@ -665,8 +671,8 @@ static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, // Do not add after dex2oat_flags, they should override others for debugging. argv[i] = NULL; - execv(PROFMAN_BIN, (char * const *)argv); - ALOGE("execv(%s) failed: %s\n", PROFMAN_BIN, strerror(errno)); + execv(profman_bin, (char * const *)argv); + ALOGE("execv(%s) failed: %s\n", profman_bin, strerror(errno)); exit(68); /* only get here on exec failure */ } @@ -1371,7 +1377,10 @@ void update_out_oat_access_times(const char* apk_path, const char* out_oat_path) // the profile has changed. static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& instruction_set, const std::string& compiler_filter, bool profile_was_updated, bool downgrade) { - static const char* DEXOPTANALYZER_BIN = "/system/bin/dexoptanalyzer"; + const char* dexoptanalyzer_bin = + is_debug_runtime() + ? "/system/bin/dexoptanalyzerd" + : "/system/bin/dexoptanalyzer"; static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) { @@ -1392,7 +1401,7 @@ static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& (downgrade ? 1 : 0); const char* argv[argc]; int i = 0; - argv[i++] = DEXOPTANALYZER_BIN; + argv[i++] = dexoptanalyzer_bin; argv[i++] = dex_file_arg.c_str(); argv[i++] = isa_arg.c_str(); argv[i++] = compiler_filter_arg.c_str(); @@ -1404,8 +1413,8 @@ static void exec_dexoptanalyzer(const std::string& dex_file, const std::string& } argv[i] = NULL; - execv(DEXOPTANALYZER_BIN, (char * const *)argv); - ALOGE("execv(%s) failed: %s\n", DEXOPTANALYZER_BIN, strerror(errno)); + execv(dexoptanalyzer_bin, (char * const *)argv); + ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno)); } // Prepares the oat dir for the secondary dex files. diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 630c1f3652..c6ebb249d0 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -3,6 +3,7 @@ cc_test { name: "installd_utils_test", clang: true, srcs: ["installd_utils_test.cpp"], + cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "liblog", @@ -19,6 +20,7 @@ cc_test { name: "installd_cache_test", clang: true, srcs: ["installd_cache_test.cpp"], + cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", @@ -38,6 +40,7 @@ cc_test { name: "installd_service_test", clang: true, srcs: ["installd_service_test.cpp"], + cflags: ["-Wall", "-Werror"], shared_libs: [ "libbase", "libbinder", diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index dd32ac6425..93d5c0b9c0 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -756,6 +756,12 @@ std::string read_path_inode(const std::string& parent, const char* name, const c } } +void remove_path_xattr(const std::string& path, const char* inode_xattr) { + if (removexattr(path.c_str(), inode_xattr) && errno != ENODATA) { + PLOG(ERROR) << "Failed to remove xattr " << inode_xattr << " at " << path; + } +} + /** * Validate that the path is valid in the context of the provided directory. * The path is allowed to have at most one subdirectory and no indirections diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index e938042a3b..a1d8443d7d 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -122,6 +122,7 @@ int get_path_inode(const std::string& path, ino_t *inode); int write_path_inode(const std::string& parent, const char* name, const char* inode_xattr); std::string read_path_inode(const std::string& parent, const char* name, const char* inode_xattr); +void remove_path_xattr(const std::string& path, const char* inode_xattr); int validate_system_app_path(const char* path); bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path, diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 67b5b46829..409c206375 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -20,16 +20,24 @@ cc_library_shared { "libutils", "libhidlbase", "libhidltransport", + "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", ], srcs: [ "DebugCommand.cpp", + "HelpCommand.cpp", "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", + "TableEntry.cpp", + "TextTable.cpp", "utils.cpp", ], + cflags: [ + "-Wall", + "-Werror", + ], } cc_defaults { @@ -40,7 +48,8 @@ cc_defaults { "libhidltransport", "liblshal", "libutils", - ] + ], + cflags: ["-Wall", "-Werror"], } cc_binary { @@ -59,6 +68,7 @@ cc_test { "libgmock" ], shared_libs: [ + "libvintf", "android.hardware.tests.baz@1.0" ], srcs: [ diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h new file mode 100644 index 0000000000..4f128ab56b --- /dev/null +++ b/cmds/lshal/Command.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ + +#include "utils.h" + +namespace android { +namespace lshal { + +class Lshal; + +// Base class for all *Commands +class Command { +public: + Command(Lshal& lshal) : mLshal(lshal) {} + virtual ~Command() = default; + // Expect optind to be set by Lshal::main and points to the next argument + // to process. + virtual Status main(const Arg &arg) = 0; + + virtual void usage() const = 0; + + // e.g. "list" + virtual std::string getName() const = 0; + + // e.g. "list HALs" + virtual std::string getSimpleDescription() const = 0; + +protected: + Lshal& mLshal; +}; + + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp index 672cad67cb..622f866617 100644 --- a/cmds/lshal/DebugCommand.cpp +++ b/cmds/lshal/DebugCommand.cpp @@ -21,12 +21,16 @@ namespace android { namespace lshal { -DebugCommand::DebugCommand(Lshal &lshal) : mLshal(lshal) { +std::string DebugCommand::getName() const { + return "debug"; } -Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) { +std::string DebugCommand::getSimpleDescription() const { + return "Debug a specified HAL."; +} + +Status DebugCommand::parseArgs(const Arg &arg) { if (optind >= arg.argc) { - mLshal.usage(command); return USAGE; } mInterfaceName = arg.argv[optind]; @@ -37,8 +41,8 @@ Status DebugCommand::parseArgs(const std::string &command, const Arg &arg) { return OK; } -Status DebugCommand::main(const std::string &command, const Arg &arg) { - Status status = parseArgs(command, arg); +Status DebugCommand::main(const Arg &arg) { + Status status = parseArgs(arg); if (status != OK) { return status; } @@ -49,6 +53,19 @@ Status DebugCommand::main(const std::string &command, const Arg &arg) { mLshal.err()); } +void DebugCommand::usage() const { + + static const std::string debug = + "debug:\n" + " lshal debug <interface> [options [options [...]]] \n" + " Print debug information of a specified interface.\n" + " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n" + " If instance name is missing `default` is used.\n" + " options: space separated options to IBase::debug.\n"; + + mLshal.err() << debug; +} + } // namespace lshal } // namespace android diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h index fa0f0faafb..9b91084bb8 100644 --- a/cmds/lshal/DebugCommand.h +++ b/cmds/lshal/DebugCommand.h @@ -21,6 +21,7 @@ #include <android-base/macros.h> +#include "Command.h" #include "utils.h" namespace android { @@ -28,14 +29,17 @@ namespace lshal { class Lshal; -class DebugCommand { +class DebugCommand : public Command { public: - DebugCommand(Lshal &lshal); - Status main(const std::string &command, const Arg &arg); + DebugCommand(Lshal &lshal) : Command(lshal) {} + ~DebugCommand() = default; + Status main(const Arg &arg) override; + void usage() const override; + std::string getSimpleDescription() const override; + std::string getName() const override; private: - Status parseArgs(const std::string &command, const Arg &arg); + Status parseArgs(const Arg &arg); - Lshal &mLshal; std::string mInterfaceName; std::vector<std::string> mOptions; diff --git a/cmds/lshal/HelpCommand.cpp b/cmds/lshal/HelpCommand.cpp new file mode 100644 index 0000000000..6773ace6d2 --- /dev/null +++ b/cmds/lshal/HelpCommand.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#include "HelpCommand.h" + +#include "Lshal.h" + +namespace android { +namespace lshal { + +std::string HelpCommand::GetName() { + return "help"; +} + +std::string HelpCommand::getSimpleDescription() const { + return "Print help message."; +} + +Status HelpCommand::main(const Arg &arg) { + if (optind >= arg.argc) { + // `lshal help` prints global usage. + mLshal.usage(); + return OK; + } + (void)usageOfCommand(arg.argv[optind]); + return OK; +} + +Status HelpCommand::usageOfCommand(const std::string& c) const { + if (c.empty()) { + mLshal.usage(); + return USAGE; + } + auto command = mLshal.selectCommand(c); + if (command == nullptr) { + // from HelpCommand::main, `lshal help unknown` + mLshal.usage(); + return USAGE; + } + + command->usage(); + return USAGE; + +} + +void HelpCommand::usage() const { + mLshal.err() + << "help:" << std::endl + << " lshal -h" << std::endl + << " lshal --help" << std::endl + << " lshal help" << std::endl + << " Print this help message" << std::endl; + mLshal.forEachCommand([&](const Command* e) { + mLshal.err() << " lshal help " << e->getName() << std::endl + << " Print help message for " << e->getName() << std::endl; + }); + +} + +} // namespace lshal +} // namespace android + diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h new file mode 100644 index 0000000000..cc709f8ee4 --- /dev/null +++ b/cmds/lshal/HelpCommand.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ + +#include <string> + +#include <android-base/macros.h> + +#include "Command.h" +#include "utils.h" + +namespace android { +namespace lshal { + +class Lshal; + +class HelpCommand : public Command { +public: + HelpCommand(Lshal &lshal) : Command(lshal) {} + ~HelpCommand() = default; + Status main(const Arg &arg) override; + void usage() const override; + std::string getSimpleDescription() const override; + std::string getName() const override { return GetName(); } + static std::string GetName(); + Status usageOfCommand(const std::string& c) const; +}; + + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 7c6cfd94ab..73996925b1 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -27,6 +27,7 @@ #include <android-base/parseint.h> #include <android/hidl/manager/1.0/IServiceManager.h> +#include <hidl-hash/Hash.h> #include <hidl-util/FQName.h> #include <private/android_filesystem_config.h> #include <sys/stat.h> @@ -39,15 +40,30 @@ #include "utils.h" using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hidl::base::V1_0::DebugInfo; +using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; namespace android { namespace lshal { -ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) { +NullableOStream<std::ostream> ListCommand::out() const { + return mLshal.out(); } -std::string getCmdline(pid_t pid) { +NullableOStream<std::ostream> ListCommand::err() const { + return mLshal.err(); +} + +std::string ListCommand::GetName() { + return "list"; +} +std::string ListCommand::getSimpleDescription() const { + return "List HALs."; +} + +std::string ListCommand::parseCmdline(pid_t pid) const { std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); std::string cmdline; if (!ifs.is_open()) { @@ -62,7 +78,7 @@ const std::string &ListCommand::getCmdline(pid_t pid) { if (pair != mCmdlines.end()) { return pair->second; } - mCmdlines[pid] = ::android::lshal::getCmdline(pid); + mCmdlines[pid] = parseCmdline(pid); return mCmdlines[pid]; } @@ -73,7 +89,7 @@ void ListCommand::removeDeadProcesses(Pids *pids) { }), pids->end()); } -bool scanBinderContext(pid_t pid, +static bool scanBinderContext(pid_t pid, const std::string &contextName, std::function<void(const std::string&)> eachLine) { std::ifstream ifs("/d/binder/proc/" + std::to_string(pid)); @@ -113,7 +129,7 @@ bool ListCommand::getPidInfo( uint64_t ptr; if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) { // Should not reach here, but just be tolerant. - mErr << "Could not parse number " << ptrString << std::endl; + err() << "Could not parse number " << ptrString << std::endl; return; } const std::string proc = " proc "; @@ -122,7 +138,7 @@ bool ListCommand::getPidInfo( for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) { int32_t pid; if (!::android::base::ParseInt(pidStr, &pid)) { - mErr << "Could not parse number " << pidStr << std::endl; + err() << "Could not parse number " << pidStr << std::endl; return; } pidInfo->refPids[ptr].push_back(pid); @@ -159,6 +175,16 @@ bool ListCommand::getPidInfo( }); } +const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) { + auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}}); + if (pair.second /* did insertion take place? */) { + if (!getPidInfo(serverPid, &pair.first->second)) { + return nullptr; + } + } + return &pair.first->second; +} + // Must process hwbinder services first, then passthrough services. void ListCommand::forEachTable(const std::function<void(Table &)> &f) { f(mServicesTable); @@ -204,44 +230,18 @@ void ListCommand::postprocess() { } } } -} -void ListCommand::printLine( - const std::string &interfaceName, - const std::string &transport, - const std::string &arch, - const std::string &threadUsage, - const std::string &server, - const std::string &serverCmdline, - const std::string &address, - const std::string &clients, - const std::string &clientCmdlines) const { - if (mSelectedColumns & ENABLE_INTERFACE_NAME) - mOut << std::setw(80) << interfaceName << "\t"; - if (mSelectedColumns & ENABLE_TRANSPORT) - mOut << std::setw(10) << transport << "\t"; - if (mSelectedColumns & ENABLE_ARCH) - mOut << std::setw(5) << arch << "\t"; - if (mSelectedColumns & ENABLE_THREADS) { - mOut << std::setw(8) << threadUsage << "\t"; - } - if (mSelectedColumns & ENABLE_SERVER_PID) { - if (mEnableCmdlines) { - mOut << std::setw(15) << serverCmdline << "\t"; - } else { - mOut << std::setw(5) << server << "\t"; - } - } - if (mSelectedColumns & ENABLE_SERVER_ADDR) - mOut << std::setw(16) << address << "\t"; - if (mSelectedColumns & ENABLE_CLIENT_PIDS) { - if (mEnableCmdlines) { - mOut << std::setw(0) << clientCmdlines; - } else { - mOut << std::setw(0) << clients; - } - } - mOut << std::endl; + mServicesTable.setDescription( + "All binderized services (registered services through hwservicemanager)"); + mPassthroughRefTable.setDescription( + "All interfaces that getService() has ever return as a passthrough interface;\n" + "PIDs / processes shown below might be inaccurate because the process\n" + "might have relinquished the interface or might have died.\n" + "The Server / Server CMD column can be ignored.\n" + "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" + "the library and successfully fetched the passthrough implementation."); + mImplementationsTable.setDescription( + "All available passthrough implementations (all -impl.so files)"); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { @@ -254,9 +254,9 @@ static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Vers return false; } -void ListCommand::dumpVintf() const { +void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; - mOut << "<!-- " << std::endl + out << "<!-- " << std::endl << " This is a skeleton device manifest. Notes: " << std::endl << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl @@ -283,7 +283,7 @@ void ListCommand::dumpVintf() const { auto splittedFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName(splittedFqInstanceName.first); if (!fqName.isValid()) { - mErr << "Warning: '" << splittedFqInstanceName.first + err() << "Warning: '" << splittedFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } @@ -316,12 +316,12 @@ void ListCommand::dumpVintf() const { arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: - mErr << "Warning: '" << fqName.package() + err() << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; } } else { - mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; + err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; } @@ -329,7 +329,7 @@ void ListCommand::dumpVintf() const { for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { - mErr << "Fatal: should not reach here. Generated result may be wrong for '" + err() << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } @@ -360,29 +360,11 @@ void ListCommand::dumpVintf() const { .versions = {version}, .transportArch = {transport, arch}, .interfaces = interfaces})) { - mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; + err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; } } }); - mOut << vintf::gHalManifestConverter(manifest); -} - -static const std::string &getArchString(Architecture arch) { - static const std::string sStr64 = "64"; - static const std::string sStr32 = "32"; - static const std::string sStrBoth = "32+64"; - static const std::string sStrUnknown = ""; - switch (arch) { - case ARCH64: - return sStr64; - case ARCH32: - return sStr32; - case ARCH_BOTH: - return sStrBoth; - case ARCH_UNKNOWN: // fall through - default: - return sStrUnknown; - } + out << vintf::gHalManifestConverter(manifest); } static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { @@ -397,68 +379,54 @@ static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo: } } -void ListCommand::dumpTable() { - mServicesTable.description = - "All binderized services (registered services through hwservicemanager)"; - mPassthroughRefTable.description = - "All interfaces that getService() has ever return as a passthrough interface;\n" - "PIDs / processes shown below might be inaccurate because the process\n" - "might have relinquished the interface or might have died.\n" - "The Server / Server CMD column can be ignored.\n" - "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" - "the library and successfully fetched the passthrough implementation."; - mImplementationsTable.description = - "All available passthrough implementations (all -impl.so files)"; - forEachTable([this] (const Table &table) { - if (!mNeat) { - mOut << table.description << std::endl; - } - mOut << std::left; - if (!mNeat) { - printLine("Interface", "Transport", "Arch", "Thread Use", "Server", - "Server CMD", "PTR", "Clients", "Clients CMD"); - } +void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const { + if (mNeat) { + MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable}) + .createTextTable().dump(out.buf()); + return; + } - for (const auto &entry : table) { - printLine(entry.interfaceName, - entry.transport, - getArchString(entry.arch), - entry.getThreadUsage(), - entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid), - entry.serverCmdline, - entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress), - join(entry.clientPids, " "), - join(entry.clientCmdlines, ";")); - - // We're only interested in dumping debug info for already - // instantiated services. There's little value in dumping the - // debug info for a service we create on the fly, so we only operate - // on the "mServicesTable". - if (mEmitDebugInfo && &table == &mServicesTable) { - auto pair = splitFirst(entry.interfaceName, '/'); - mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(), - NullableOStream<std::ostream>(nullptr)); - } - } - if (!mNeat) { - mOut << std::endl; - } + forEachTable([this, &out](const Table &table) { + + // We're only interested in dumping debug info for already + // instantiated services. There's little value in dumping the + // debug info for a service we create on the fly, so we only operate + // on the "mServicesTable". + std::function<std::string(const std::string&)> emitDebugInfo = nullptr; + if (mEmitDebugInfo && &table == &mServicesTable) { + emitDebugInfo = [this](const auto& iName) { + std::stringstream ss; + auto pair = splitFirst(iName, '/'); + mLshal.emitDebugInfo(pair.first, pair.second, {}, ss, + NullableOStream<std::ostream>(nullptr)); + return ss.str(); + }; + } + table.createTextTable(mNeat, emitDebugInfo).dump(out.buf()); + out << std::endl; }); - } -void ListCommand::dump() { - if (mVintf) { - dumpVintf(); - if (!!mFileOutput) { - mFileOutput.buf().close(); - delete &mFileOutput.buf(); - mFileOutput = nullptr; - } - mOut = std::cout; - } else { - dumpTable(); +Status ListCommand::dump() { + auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable; + + if (mFileOutputPath.empty()) { + (*this.*dump)(out()); + return OK; + } + + std::ofstream fileOutput(mFileOutputPath); + if (!fileOutput.is_open()) { + err() << "Could not open file '" << mFileOutputPath << "'." << std::endl; + return IO_ERROR; } + chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL); + + (*this.*dump)(NullableOStream<std::ostream>(fileOutput)); + + fileOutput.flush(); + fileOutput.close(); + return OK; } void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { @@ -471,10 +439,10 @@ void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { case LIST_DLLIB : table = &mImplementationsTable; break; default: - mErr << "Error: Unknown source of entry " << source << std::endl; + err() << "Error: Unknown source of entry " << source << std::endl; } if (table) { - table->entries.push_back(std::forward<TableEntry>(entry)); + table->add(std::forward<TableEntry>(entry)); } } @@ -491,10 +459,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { entries.emplace(interfaceName, TableEntry{ .interfaceName = interfaceName, .transport = "passthrough", - .serverPid = NO_PID, - .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, - .arch = ARCH_UNKNOWN }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { @@ -502,7 +467,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { } }); if (!ret.isOk()) { - mErr << "Error: Failed to call list on getPassthroughServiceManager(): " + err() << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } @@ -525,14 +490,13 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { std::string{info.instanceName.c_str()}, .transport = "passthrough", .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, - .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) }); } }); if (!ret.isOk()) { - mErr << "Error: Failed to call debugDump on defaultServiceManager(): " + err() << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } @@ -540,10 +504,6 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { } Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { - using namespace ::std; - using namespace ::android::hardware; - using namespace ::android::hidl::manager::V1_0; - using namespace ::android::hidl::base::V1_0; const std::string mode = "hwbinder"; hidl_vec<hidl_string> fqInstanceNames; @@ -552,86 +512,123 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { fqInstanceNames = names; }); if (!listRet.isOk()) { - mErr << "Error: Failed to list services for " << mode << ": " + err() << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; return DUMP_BINDERIZED_ERROR; } Status status = OK; - // server pid, .ptr value of binder object, child pids - std::map<std::string, DebugInfo> allDebugInfos; - std::map<pid_t, PidInfo> allPids; + std::map<std::string, TableEntry> allTableEntries; for (const auto &fqInstanceName : fqInstanceNames) { - const auto pair = splitFirst(fqInstanceName, '/'); - const auto &serviceName = pair.first; - const auto &instanceName = pair.second; - auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); - if (!getRet.isOk()) { - mErr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "cannot be fetched from service manager:" - << getRet.description() << std::endl; - status |= DUMP_BINDERIZED_ERROR; - continue; - } - sp<IBase> service = getRet; - if (service == nullptr) { - mErr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "cannot be fetched from service manager (null)" - << std::endl; - status |= DUMP_BINDERIZED_ERROR; - continue; - } - auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { - allDebugInfos[fqInstanceName] = debugInfo; - if (debugInfo.pid >= 0) { - allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo(); - } - }); - if (!debugRet.isOk()) { - mErr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "debugging information cannot be retrieved:" - << debugRet.description() << std::endl; - status |= DUMP_BINDERIZED_ERROR; - } + // create entry and default assign all fields. + TableEntry& entry = allTableEntries[fqInstanceName]; + entry.interfaceName = fqInstanceName; + entry.transport = mode; + + status |= fetchBinderizedEntry(manager, &entry); } - for (auto &pair : allPids) { - pid_t serverPid = pair.first; - if (!getPidInfo(serverPid, &allPids[serverPid])) { - mErr << "Warning: no information for PID " << serverPid - << ", are you root?" << std::endl; - status |= DUMP_BINDERIZED_ERROR; - } + for (auto& pair : allTableEntries) { + putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second)); } - for (const auto &fqInstanceName : fqInstanceNames) { - auto it = allDebugInfos.find(fqInstanceName); - if (it == allDebugInfos.end()) { - putEntry(HWSERVICEMANAGER_LIST, { - .interfaceName = fqInstanceName, - .transport = mode, - .serverPid = NO_PID, - .serverObjectAddress = NO_PTR, - .clientPids = {}, - .threadUsage = 0, - .threadCount = 0, - .arch = ARCH_UNKNOWN - }); - continue; - } - const DebugInfo &info = it->second; - bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR; - - putEntry(HWSERVICEMANAGER_LIST, { - .interfaceName = fqInstanceName, - .transport = mode, - .serverPid = info.pid, - .serverObjectAddress = info.ptr, - .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{}, - .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0, - .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0, - .arch = fromBaseArchitecture(info.arch), - }); + return status; +} + +Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, + TableEntry *entry) { + Status status = OK; + const auto handleError = [&](Status additionalError, const std::string& msg) { + err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl; + status |= DUMP_BINDERIZED_ERROR | additionalError; + }; + + const auto pair = splitFirst(entry->interfaceName, '/'); + const auto &serviceName = pair.first; + const auto &instanceName = pair.second; + auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); + if (!getRet.isOk()) { + handleError(TRANSACTION_ERROR, + "cannot be fetched from service manager:" + getRet.description()); + return status; + } + sp<IBase> service = getRet; + if (service == nullptr) { + handleError(NO_INTERFACE, "cannot be fetched from service manager (null)"); + return status; } + + // getDebugInfo + do { + DebugInfo debugInfo; + auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) { + debugInfo = received; + }); + if (!debugRet.isOk()) { + handleError(TRANSACTION_ERROR, + "debugging information cannot be retrieved: " + debugRet.description()); + break; // skip getPidInfo + } + + entry->serverPid = debugInfo.pid; + entry->serverObjectAddress = debugInfo.ptr; + entry->arch = fromBaseArchitecture(debugInfo.arch); + + if (debugInfo.pid != NO_PID) { + const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid); + if (pidInfo == nullptr) { + handleError(IO_ERROR, + "no information for PID " + std::to_string(debugInfo.pid) + + ", are you root?"); + break; + } + if (debugInfo.ptr != NO_PTR) { + auto it = pidInfo->refPids.find(debugInfo.ptr); + if (it != pidInfo->refPids.end()) { + entry->clientPids = it->second; + } + } + entry->threadUsage = pidInfo->threadUsage; + entry->threadCount = pidInfo->threadCount; + } + } while (0); + + // hash + do { + ssize_t hashIndex = -1; + auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) { + for (size_t i = 0; i < c.size(); ++i) { + if (serviceName == c[i]) { + hashIndex = static_cast<ssize_t>(i); + break; + } + } + }); + if (!ifaceChainRet.isOk()) { + handleError(TRANSACTION_ERROR, + "interfaceChain fails: " + ifaceChainRet.description()); + break; // skip getHashChain + } + if (hashIndex < 0) { + handleError(BAD_IMPL, "Interface name does not exist in interfaceChain."); + break; // skip getHashChain + } + auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) { + if (static_cast<size_t>(hashIndex) >= hashChain.size()) { + handleError(BAD_IMPL, + "interfaceChain indicates position " + std::to_string(hashIndex) + + " but getHashChain returns " + std::to_string(hashChain.size()) + + " hashes"); + return; + } + + auto&& hashArray = hashChain[hashIndex]; + std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()}; + entry->hash = Hash::hexString(hashVec); + }); + if (!hashRet.isOk()) { + handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); + } + } while (0); return status; } @@ -639,7 +636,7 @@ Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); if (bManager == nullptr) { - mErr << "Failed to get defaultServiceManager()!" << std::endl; + err() << "Failed to get defaultServiceManager()!" << std::endl; status |= NO_BINDERIZED_MANAGER; } else { status |= fetchBinderized(bManager); @@ -649,7 +646,7 @@ Status ListCommand::fetch() { auto pManager = mLshal.passthroughManager(); if (pManager == nullptr) { - mErr << "Failed to get getPassthroughServiceManager()!" << std::endl; + err() << "Failed to get getPassthroughServiceManager()!" << std::endl; status |= NO_PASSTHROUGH_MANAGER; } else { status |= fetchAllLibraries(pManager); @@ -657,139 +654,270 @@ Status ListCommand::fetch() { return status; } -Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { - static struct option longOptions[] = { - // long options with short alternatives - {"help", no_argument, 0, 'h' }, - {"interface", no_argument, 0, 'i' }, - {"transport", no_argument, 0, 't' }, - {"arch", no_argument, 0, 'r' }, - {"pid", no_argument, 0, 'p' }, - {"address", no_argument, 0, 'a' }, - {"clients", no_argument, 0, 'c' }, - {"threads", no_argument, 0, 'e' }, - {"cmdline", no_argument, 0, 'm' }, - {"debug", optional_argument, 0, 'd' }, - - // long options without short alternatives - {"sort", required_argument, 0, 's' }, - {"init-vintf",optional_argument, 0, 'v' }, - {"neat", no_argument, 0, 'n' }, - { 0, 0, 0, 0 } - }; +void ListCommand::registerAllOptions() { + int v = mOptions.size(); + // A list of acceptable command line options + // key: value returned by getopt_long + // long options with short alternatives + mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) { + return USAGE; + }, ""}); + mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); + return OK; + }, "print the instance name column"}); + mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::RELEASED); + return OK; + }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"}); + mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT); + return OK; + }, "print the transport mode column"}); + mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::ARCH); + return OK; + }, "print the bitness column"}); + mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::HASH); + return OK; + }, "print hash of the interface"}); + mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID); + return OK; + }, "print the server PID, or server cmdline if -m is set"}); + mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR); + return OK; + }, "print the server object address column"}); + mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS); + return OK; + }, "print the client PIDs, or client cmdlines if -m is set"}); + mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::THREADS); + return OK; + }, "print currently used/available threads\n(note, available threads created lazily)"}); + mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mEnableCmdlines = true; + return OK; + }, "print cmdline instead of PIDs"}); + mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) { + thiz->mEmitDebugInfo = true; + if (arg) thiz->mFileOutputPath = arg; + return OK; + }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n" + "Writes to specified file if 'arg' is provided, otherwise stdout."}); + + // long options without short alternatives + mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { + thiz->mVintf = true; + if (arg) thiz->mFileOutputPath = arg; + return OK; + }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."}); + mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) { + if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) { + thiz->mSortColumn = TableEntry::sortByInterfaceName; + } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) { + thiz->mSortColumn = TableEntry::sortByServerPid; + } else { + thiz->err() << "Unrecognized sorting column: " << arg << std::endl; + return USAGE; + } + return OK; + }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."}); + mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mNeat = true; + return OK; + }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."}); +} + +// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining +// the lifetime of "options" during the usage of the returned array. +static std::unique_ptr<struct option[]> getLongOptions( + const ListCommand::RegisteredOptions& options, + int* longOptFlag) { + std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]}; + int i = 0; + for (const auto& e : options) { + ret[i].name = e.longOption.c_str(); + ret[i].has_arg = e.hasArg; + ret[i].flag = longOptFlag; + ret[i].val = e.val; + + i++; + } + // getopt_long last option has all zeros + ret[i].name = NULL; + ret[i].has_arg = 0; + ret[i].flag = NULL; + ret[i].val = 0; + + return ret; +} + +// Create 'optstring' argument to getopt_long. +static std::string getShortOptions(const ListCommand::RegisteredOptions& options) { + std::stringstream ss; + for (const auto& e : options) { + if (e.shortOption != '\0') { + ss << e.shortOption; + } + } + return ss.str(); +} + +Status ListCommand::parseArgs(const Arg &arg) { + + if (mOptions.empty()) { + registerAllOptions(); + } + int longOptFlag; + std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag); + std::string shortOptions = getShortOptions(mOptions); + + // suppress output to std::err for unknown options + opterr = 0; int optionIndex; int c; // Lshal::parseArgs has set optind to the next option to parse for (;;) { - // using getopt_long in case we want to add other options in the future c = getopt_long(arg.argc, arg.argv, - "hitrpacmde", longOptions, &optionIndex); + shortOptions.c_str(), longOptions.get(), &optionIndex); if (c == -1) { break; } - switch (c) { - case 's': { - if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) { - mSortColumn = TableEntry::sortByInterfaceName; - } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) { - mSortColumn = TableEntry::sortByServerPid; - } else { - mErr << "Unrecognized sorting column: " << optarg << std::endl; - mLshal.usage(command); - return USAGE; - } - break; - } - case 'v': { - if (optarg) { - mFileOutput = new std::ofstream{optarg}; - mOut = mFileOutput; - if (!mFileOutput.buf().is_open()) { - mErr << "Could not open file '" << optarg << "'." << std::endl; - return IO_ERROR; - } + const RegisteredOption* found = nullptr; + if (c == 0) { + // see long option + for (const auto& e : mOptions) { + if (longOptFlag == e.val) found = &e; } - mVintf = true; - } - case 'i': { - mSelectedColumns |= ENABLE_INTERFACE_NAME; - break; - } - case 't': { - mSelectedColumns |= ENABLE_TRANSPORT; - break; - } - case 'r': { - mSelectedColumns |= ENABLE_ARCH; - break; - } - case 'p': { - mSelectedColumns |= ENABLE_SERVER_PID; - break; - } - case 'a': { - mSelectedColumns |= ENABLE_SERVER_ADDR; - break; - } - case 'c': { - mSelectedColumns |= ENABLE_CLIENT_PIDS; - break; - } - case 'e': { - mSelectedColumns |= ENABLE_THREADS; - break; - } - case 'm': { - mEnableCmdlines = true; - break; - } - case 'd': { - mEmitDebugInfo = true; - - if (optarg) { - mFileOutput = new std::ofstream{optarg}; - mOut = mFileOutput; - if (!mFileOutput.buf().is_open()) { - mErr << "Could not open file '" << optarg << "'." << std::endl; - return IO_ERROR; - } - chown(optarg, AID_SHELL, AID_SHELL); + } else { + // see short option + for (const auto& e : mOptions) { + if (c == e.shortOption) found = &e; } - break; - } - case 'n': { - mNeat = true; - break; } - case 'h': // falls through - default: // see unrecognized options - mLshal.usage(command); + + if (found == nullptr) { + // see unrecognized options + err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl; return USAGE; } + + Status status = found->op(this, optarg); + if (status != OK) { + return status; + } } if (optind < arg.argc) { // see non option - mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; + err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl; + return USAGE; + } + + if (mNeat && mEmitDebugInfo) { + err() << "Error: --neat should not be used with --debug." << std::endl; + return USAGE; } - if (mSelectedColumns == 0) { - mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS; + if (mSelectedColumns.empty()) { + mSelectedColumns = {TableColumnType::RELEASED, + TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, + TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; + } + + if (mEnableCmdlines) { + for (size_t i = 0; i < mSelectedColumns.size(); ++i) { + if (mSelectedColumns[i] == TableColumnType::SERVER_PID) { + mSelectedColumns[i] = TableColumnType::SERVER_CMD; + } + if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) { + mSelectedColumns[i] = TableColumnType::CLIENT_CMDS; + } + } } + + forEachTable([this] (Table& table) { + table.setSelectedColumns(this->mSelectedColumns); + }); + return OK; } -Status ListCommand::main(const std::string &command, const Arg &arg) { - Status status = parseArgs(command, arg); +Status ListCommand::main(const Arg &arg) { + Status status = parseArgs(arg); if (status != OK) { return status; } status = fetch(); postprocess(); - dump(); + status |= dump(); return status; } +static std::vector<std::string> splitString(const std::string &s, char c) { + std::vector<std::string> components; + + size_t startPos = 0; + size_t matchPos; + while ((matchPos = s.find(c, startPos)) != std::string::npos) { + components.push_back(s.substr(startPos, matchPos - startPos)); + startPos = matchPos + 1; + } + + if (startPos <= s.length()) { + components.push_back(s.substr(startPos)); + } + return components; +} + +const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const { + static const std::string empty{}; + static const std::string optional{"[=<arg>]"}; + static const std::string required{"=<arg>"}; + + if (hasArg == optional_argument) { + return optional; + } + if (hasArg == required_argument) { + return required; + } + return empty; +} + +void ListCommand::usage() const { + + err() << "list:" << std::endl + << " lshal" << std::endl + << " lshal list" << std::endl + << " List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl + << " lshal list [-h|--help]" << std::endl + << " -h, --help: Print help message for list (`lshal help list`)" << std::endl + << " lshal [list] [OPTIONS...]" << std::endl; + for (const auto& e : mOptions) { + if (e.help.empty()) { + continue; + } + err() << " "; + if (e.shortOption != '\0') + err() << "-" << e.shortOption << e.getHelpMessageForArgument(); + if (e.shortOption != '\0' && !e.longOption.empty()) + err() << ", "; + if (!e.longOption.empty()) + err() << "--" << e.longOption << e.getHelpMessageForArgument(); + err() << ": "; + std::vector<std::string> lines = splitString(e.help, '\n'); + for (const auto& line : lines) { + if (&line != &lines.front()) + err() << " "; + err() << line << std::endl; + } + } +} + } // namespace lshal } // namespace android diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index a75db04960..7e252fc167 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -17,6 +17,7 @@ #ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ #define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ +#include <getopt.h> #include <stdint.h> #include <fstream> @@ -26,8 +27,10 @@ #include <android-base/macros.h> #include <android/hidl/manager/1.0/IServiceManager.h> +#include "Command.h" #include "NullableOStream.h" #include "TableEntry.h" +#include "TextTable.h" #include "utils.h" namespace android { @@ -35,39 +38,71 @@ namespace lshal { class Lshal; -class ListCommand { +struct PidInfo { + std::map<uint64_t, Pids> refPids; // pids that are referenced + uint32_t threadUsage; // number of threads in use + uint32_t threadCount; // number of threads total +}; + +class ListCommand : public Command { public: - ListCommand(Lshal &lshal); - Status main(const std::string &command, const Arg &arg); -private: - Status parseArgs(const std::string &command, const Arg &arg); + ListCommand(Lshal &lshal) : Command(lshal) {} + virtual ~ListCommand() = default; + Status main(const Arg &arg) override; + void usage() const override; + std::string getSimpleDescription() const override; + std::string getName() const override { return GetName(); } + + static std::string GetName(); + + struct RegisteredOption { + // short alternative, e.g. 'v'. If '\0', no short options is available. + char shortOption; + // long alternative, e.g. 'init-vintf' + std::string longOption; + // no_argument, required_argument or optional_argument + int hasArg; + // value written to 'flag' by getopt_long + int val; + // operation when the argument is present + std::function<Status(ListCommand* thiz, const char* arg)> op; + // help message + std::string help; + + const std::string& getHelpMessageForArgument() const; + }; + // A list of acceptable command line options + // key: value returned by getopt_long + using RegisteredOptions = std::vector<RegisteredOption>; + +protected: + Status parseArgs(const Arg &arg); Status fetch(); - void postprocess(); - void dump(); + virtual void postprocess(); + Status dump(); void putEntry(TableEntrySource source, TableEntry &&entry); Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); - struct PidInfo { - std::map<uint64_t, Pids> refPids; // pids that are referenced - uint32_t threadUsage; // number of threads in use - uint32_t threadCount; // number of threads total - }; - bool getPidInfo(pid_t serverPid, PidInfo *info) const; - - void dumpTable(); - void dumpVintf() const; - void printLine( - const std::string &interfaceName, - const std::string &transport, - const std::string &arch, - const std::string &threadUsage, - const std::string &server, - const std::string &serverCmdline, - const std::string &address, - const std::string &clients, - const std::string &clientCmdlines) const; + Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, + TableEntry *entry); + + // Get relevant information for a PID by parsing files under /d/binder. + // It is a virtual member function so that it can be mocked. + virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; + // Retrieve from mCachedPidInfos and call getPidInfo if necessary. + const PidInfo* getPidInfoCached(pid_t serverPid); + + void dumpTable(const NullableOStream<std::ostream>& out) const; + void dumpVintf(const NullableOStream<std::ostream>& out) const; + void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport, + const std::string &arch, const std::string &threadUsage, const std::string &server, + const std::string &serverCmdline, const std::string &address, + const std::string &clients, const std::string &clientCmdlines) const; + void addLine(TextTable *table, const TableEntry &entry); + // Read and return /proc/{pid}/cmdline. + virtual std::string parseCmdline(pid_t pid) const; // Return /proc/{pid}/cmdline if it exists, else empty string. const std::string &getCmdline(pid_t pid); // Call getCmdline on all pid in pids. If it returns empty string, the process might @@ -76,21 +111,18 @@ private: void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; - Lshal &mLshal; + NullableOStream<std::ostream> err() const; + NullableOStream<std::ostream> out() const; + + void registerAllOptions(); Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; - NullableOStream<std::ostream> mErr; - NullableOStream<std::ostream> mOut; - NullableOStream<std::ofstream> mFileOutput = nullptr; + std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; - TableEntrySelect mSelectedColumns = 0; - // If true, cmdlines will be printed instead of pid. - bool mEnableCmdlines = false; - // If true, calls IBase::debug(...) on each service. bool mEmitDebugInfo = false; // If true, output in VINTF format. @@ -104,6 +136,16 @@ private: // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline. std::map<pid_t, std::string> mCmdlines; + // Cache for getPidInfo. + std::map<pid_t, PidInfo> mCachedPidInfos; + + RegisteredOptions mOptions; + // All selected columns + std::vector<TableColumnType> mSelectedColumns; + // If true, emit cmdlines instead of PIDs + bool mEnableCmdlines = false; + +private: DISALLOW_COPY_AND_ASSIGN(ListCommand); }; diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index e2d5f6df0a..c6f28ac502 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -34,9 +34,8 @@ namespace lshal { using ::android::hidl::manager::V1_0::IServiceManager; Lshal::Lshal() - : mOut(std::cout), mErr(std::cerr), - mServiceManager(::android::hardware::defaultServiceManager()), - mPassthroughManager(::android::hardware::getPassthroughServiceManager()) { + : Lshal(std::cout, std::cerr, ::android::hardware::defaultServiceManager(), + ::android::hardware::getPassthroughServiceManager()) { } Lshal::Lshal(std::ostream &out, std::ostream &err, @@ -46,76 +45,39 @@ Lshal::Lshal(std::ostream &out, std::ostream &err, mServiceManager(serviceManager), mPassthroughManager(passthroughManager) { + mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)}); + mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)}); + mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)}); } -void Lshal::usage(const std::string &command) const { - static const std::string helpSummary = - "lshal: List and debug HALs.\n" - "\n" - "commands:\n" - " help Print help message\n" - " list list HALs\n" - " debug debug a specified HAL\n" - "\n" - "If no command is specified, `list` is the default.\n"; - - static const std::string list = - "list:\n" - " lshal\n" - " lshal list\n" - " List all hals with default ordering and columns (`lshal list -ipc`)\n" - " lshal list [-h|--help]\n" - " -h, --help: Print help message for list (`lshal help list`)\n" - " lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n" - " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n" - " [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n" - " [--debug|-d[=<output file>]]\n" - " -i, --interface: print the interface name column\n" - " -n, --instance: print the instance name column\n" - " -t, --transport: print the transport mode column\n" - " -r, --arch: print if the HAL is in 64-bit or 32-bit\n" - " -e, --threads: print currently used/available threads\n" - " (note, available threads created lazily)\n" - " -p, --pid: print the server PID, or server cmdline if -m is set\n" - " -a, --address: print the server object address column\n" - " -c, --clients: print the client PIDs, or client cmdlines if -m is set\n" - " -m, --cmdline: print cmdline instead of PIDs\n" - " -d[=<output file>], --debug[=<output file>]: emit debug info from \n" - " IBase::debug with empty options\n" - " --sort=i, --sort=interface: sort by interface name\n" - " --sort=p, --sort=pid: sort by server pid\n" - " --init-vintf=<output file>: form a skeleton HAL manifest to specified\n" - " file, or stdout if no file specified.\n"; - - static const std::string debug = - "debug:\n" - " lshal debug <interface> [options [options [...]]] \n" - " Print debug information of a specified interface.\n" - " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n" - " If instance name is missing `default` is used.\n" - " options: space separated options to IBase::debug.\n"; - - static const std::string help = - "help:\n" - " lshal -h\n" - " lshal --help\n" - " lshal help\n" - " Print this help message\n" - " lshal help list\n" - " Print help message for list\n" - " lshal help debug\n" - " Print help message for debug\n"; - - if (command == "list") { - mErr << list; - return; - } - if (command == "debug") { - mErr << debug; - return; - } +void Lshal::forEachCommand(const std::function<void(const Command* c)>& f) const { + for (const auto& e : mRegisteredCommands) f(e.get()); +} - mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help; +void Lshal::usage() { + err() << "lshal: List and debug HALs." << std::endl << std::endl + << "commands:" << std::endl; + + size_t nameMaxLength = 0; + forEachCommand([&](const Command* e) { + nameMaxLength = std::max(nameMaxLength, e->getName().length()); + }); + bool first = true; + forEachCommand([&](const Command* e) { + if (!first) err() << std::endl; + first = false; + err() << " " << std::left << std::setw(nameMaxLength + 8) << e->getName() + << e->getSimpleDescription(); + }); + err() << std::endl << "If no command is specified, `" << ListCommand::GetName() + << "` is the default." << std::endl << std::endl; + + first = true; + forEachCommand([&](const Command* e) { + if (!first) err() << std::endl; + first = false; + e->usage(); + }); } // A unique_ptr type using a custom deleter function. @@ -186,26 +148,24 @@ Status Lshal::emitDebugInfo( } Status Lshal::parseArgs(const Arg &arg) { - static std::set<std::string> sAllCommands{"list", "debug", "help"}; optind = 1; if (optind >= arg.argc) { // no options at all. return OK; } mCommand = arg.argv[optind]; - if (sAllCommands.find(mCommand) != sAllCommands.end()) { + if (selectCommand(mCommand) != nullptr) { ++optind; return OK; // mCommand is set correctly } if (mCommand.size() > 0 && mCommand[0] == '-') { // first argument is an option, set command to "" (which is recognized as "list") - mCommand = ""; + mCommand.clear(); return OK; } - mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; - usage(); + err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "'" << std::endl; return USAGE; } @@ -216,27 +176,43 @@ void signalHandler(int sig) { } } +Command* Lshal::selectCommand(const std::string& command) const { + if (command.empty()) { + return selectCommand(ListCommand::GetName()); + } + for (const auto& e : mRegisteredCommands) { + if (e->getName() == command) { + return e.get(); + } + } + return nullptr; +} + Status Lshal::main(const Arg &arg) { // Allow SIGINT to terminate all threads. signal(SIGINT, signalHandler); Status status = parseArgs(arg); if (status != OK) { + usage(); return status; } - if (mCommand == "help") { - usage(optind < arg.argc ? arg.argv[optind] : ""); + auto c = selectCommand(mCommand); + if (c == nullptr) { + // unknown command, print global usage + usage(); return USAGE; } - // Default command is list - if (mCommand == "list" || mCommand == "") { - return ListCommand{*this}.main(mCommand, arg); - } - if (mCommand == "debug") { - return DebugCommand{*this}.main(mCommand, arg); + status = c->main(arg); + if (status == USAGE) { + // bad options. Run `lshal help ${mCommand}` instead. + // For example, `lshal --unknown-option` becomes `lshal help` (prints global help) + // and `lshal list --unknown-option` becomes `lshal help list` + auto&& help = selectCommand(HelpCommand::GetName()); + return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand); } - usage(); - return USAGE; + + return status; } NullableOStream<std::ostream> Lshal::err() const { diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index 00db5d090a..690f30eb21 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -24,6 +24,8 @@ #include <android/hidl/manager/1.0/IServiceManager.h> #include <utils/StrongPointer.h> +#include "Command.h" +#include "HelpCommand.h" #include "NullableOStream.h" #include "utils.h" @@ -33,13 +35,15 @@ namespace lshal { class Lshal { public: Lshal(); + virtual ~Lshal() {} Lshal(std::ostream &out, std::ostream &err, sp<hidl::manager::V1_0::IServiceManager> serviceManager, sp<hidl::manager::V1_0::IServiceManager> passthroughManager); Status main(const Arg &arg); - void usage(const std::string &command = "") const; - NullableOStream<std::ostream> err() const; - NullableOStream<std::ostream> out() const; + // global usage + void usage(); + virtual NullableOStream<std::ostream> err() const; + virtual NullableOStream<std::ostream> out() const; const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const; const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const; @@ -49,16 +53,23 @@ public: const std::vector<std::string> &options, std::ostream &out, NullableOStream<std::ostream> err) const; + + Command* selectCommand(const std::string& command) const; + + void forEachCommand(const std::function<void(const Command* c)>& f) const; + private: Status parseArgs(const Arg &arg); + std::string mCommand; - Arg mCmdArgs; NullableOStream<std::ostream> mOut; NullableOStream<std::ostream> mErr; sp<hidl::manager::V1_0::IServiceManager> mServiceManager; sp<hidl::manager::V1_0::IServiceManager> mPassthroughManager; + std::vector<std::unique_ptr<Command>> mRegisteredCommands; + DISALLOW_COPY_AND_ASSIGN(Lshal); }; diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp index 54d19f6d1b..fc407495ca 100644 --- a/cmds/lshal/PipeRelay.cpp +++ b/cmds/lshal/PipeRelay.cpp @@ -57,8 +57,7 @@ bool PipeRelay::RelayThread::threadLoop() { //////////////////////////////////////////////////////////////////////////////// PipeRelay::PipeRelay(std::ostream &os) - : mOutStream(os), - mInitCheck(NO_INIT) { + : mInitCheck(NO_INIT) { int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, mFds); if (res < 0) { diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h index 76b2b23485..8dc3093742 100644 --- a/cmds/lshal/PipeRelay.h +++ b/cmds/lshal/PipeRelay.h @@ -42,7 +42,6 @@ struct PipeRelay { private: struct RelayThread; - std::ostream &mOutStream; status_t mInitCheck; int mFds[2]; sp<RelayThread> mThread; diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp new file mode 100644 index 0000000000..e8792a4307 --- /dev/null +++ b/cmds/lshal/TableEntry.cpp @@ -0,0 +1,166 @@ +/* + * 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_TAG "lshal" +#include <android-base/logging.h> + +#include <hidl-hash/Hash.h> + +#include "TableEntry.h" + +#include "TextTable.h" +#include "utils.h" + +namespace android { +namespace lshal { + +static const std::string &getArchString(Architecture arch) { + static const std::string sStr64 = "64"; + static const std::string sStr32 = "32"; + static const std::string sStrBoth = "32+64"; + static const std::string sStrUnknown = ""; + switch (arch) { + case ARCH64: + return sStr64; + case ARCH32: + return sStr32; + case ARCH_BOTH: + return sStrBoth; + case ARCH_UNKNOWN: // fall through + default: + return sStrUnknown; + } +} + +static std::string getTitle(TableColumnType type) { + switch (type) { + case TableColumnType::INTERFACE_NAME: return "Interface"; + case TableColumnType::TRANSPORT: return "Transport"; + case TableColumnType::SERVER_PID: return "Server"; + case TableColumnType::SERVER_CMD: return "Server CMD"; + case TableColumnType::SERVER_ADDR: return "PTR"; + case TableColumnType::CLIENT_PIDS: return "Clients"; + case TableColumnType::CLIENT_CMDS: return "Clients CMD"; + case TableColumnType::ARCH: return "Arch"; + case TableColumnType::THREADS: return "Thread Use"; + case TableColumnType::RELEASED: return "R"; + case TableColumnType::HASH: return "Hash"; + default: + LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); + return ""; + } +} + +std::string TableEntry::getField(TableColumnType type) const { + switch (type) { + case TableColumnType::INTERFACE_NAME: + return interfaceName; + case TableColumnType::TRANSPORT: + return transport; + case TableColumnType::SERVER_PID: + return serverPid == NO_PID ? "N/A" : std::to_string(serverPid); + case TableColumnType::SERVER_CMD: + return serverCmdline; + case TableColumnType::SERVER_ADDR: + return serverObjectAddress == NO_PTR ? "N/A" : toHexString(serverObjectAddress); + case TableColumnType::CLIENT_PIDS: + return join(clientPids, " "); + case TableColumnType::CLIENT_CMDS: + return join(clientCmdlines, ";"); + case TableColumnType::ARCH: + return getArchString(arch); + case TableColumnType::THREADS: + return getThreadUsage(); + case TableColumnType::RELEASED: + return isReleased(); + case TableColumnType::HASH: + return hash; + default: + LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); + return ""; + } +} + +std::string TableEntry::isReleased() const { + static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); + + if (hash.empty() || hash == unreleased) { + return " "; // unknown or unreleased + } + return "Y"; // released +} + +TextTable Table::createTextTable(bool neat, + const std::function<std::string(const std::string&)>& emitDebugInfo) const { + + TextTable textTable; + std::vector<std::string> row; + if (!neat) { + textTable.add(mDescription); + + row.clear(); + for (TableColumnType type : mSelectedColumns) { + row.push_back(getTitle(type)); + } + textTable.add(std::move(row)); + } + + for (const auto& entry : mEntries) { + row.clear(); + for (TableColumnType type : mSelectedColumns) { + row.push_back(entry.getField(type)); + } + textTable.add(std::move(row)); + + if (emitDebugInfo) { + std::string debugInfo = emitDebugInfo(entry.interfaceName); + if (!debugInfo.empty()) textTable.add(debugInfo); + } + } + return textTable; +} + +TextTable MergedTable::createTextTable() { + TextTable textTable; + for (const Table* table : mTables) { + textTable.addAll(table->createTextTable()); + } + return textTable; +} + +bool TableEntry::operator==(const TableEntry& other) const { + if (this == &other) { + return true; + } + return interfaceName == other.interfaceName && transport == other.transport && + serverPid == other.serverPid && threadUsage == other.threadUsage && + threadCount == other.threadCount && serverCmdline == other.serverCmdline && + serverObjectAddress == other.serverObjectAddress && clientPids == other.clientPids && + clientCmdlines == other.clientCmdlines && arch == other.arch; +} + +std::string TableEntry::to_string() const { + std::stringstream ss; + ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage() + << ";server=" << serverPid + << "(" << serverObjectAddress << ";" << serverCmdline << ");clients=[" + << join(clientPids, ";") << "](" << join(clientCmdlines, ";") << ");arch=" + << getArchString(arch); + return ss.str(); + +} + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index e04c3ca252..69206cc51b 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -23,6 +23,8 @@ #include <vector> #include <iostream> +#include "TextTable.h" + namespace android { namespace lshal { @@ -43,17 +45,38 @@ enum : unsigned int { }; using Architecture = unsigned int; +enum class TableColumnType : unsigned int { + INTERFACE_NAME, + TRANSPORT, + SERVER_PID, + SERVER_CMD, + SERVER_ADDR, + CLIENT_PIDS, + CLIENT_CMDS, + ARCH, + THREADS, + RELEASED, + HASH, +}; + +enum { + NO_PID = -1, + NO_PTR = 0 +}; + struct TableEntry { - std::string interfaceName; - std::string transport; - int32_t serverPid; - uint32_t threadUsage; - uint32_t threadCount; - std::string serverCmdline; - uint64_t serverObjectAddress; - Pids clientPids; - std::vector<std::string> clientCmdlines; - Architecture arch; + std::string interfaceName{}; + std::string transport{}; + int32_t serverPid{NO_PID}; + uint32_t threadUsage{0}; + uint32_t threadCount{0}; + std::string serverCmdline{}; + uint64_t serverObjectAddress{NO_PTR}; + Pids clientPids{}; + std::vector<std::string> clientCmdlines{}; + Architecture arch{ARCH_UNKNOWN}; + // empty: unknown, all zeros: unreleased, otherwise: released + std::string hash{}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; @@ -69,36 +92,52 @@ struct TableEntry { return std::to_string(threadUsage) + "/" + std::to_string(threadCount); } + + std::string isReleased() const; + + std::string getField(TableColumnType type) const; + + bool operator==(const TableEntry& other) const; + std::string to_string() const; }; -struct Table { +using SelectedColumns = std::vector<TableColumnType>; + +class Table { +public: using Entries = std::vector<TableEntry>; - std::string description; - Entries entries; - Entries::iterator begin() { return entries.begin(); } - Entries::const_iterator begin() const { return entries.begin(); } - Entries::iterator end() { return entries.end(); } - Entries::const_iterator end() const { return entries.end(); } -}; + Entries::iterator begin() { return mEntries.begin(); } + Entries::const_iterator begin() const { return mEntries.begin(); } + Entries::iterator end() { return mEntries.end(); } + Entries::const_iterator end() const { return mEntries.end(); } + size_t size() const { return mEntries.size(); } -using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>; + void add(TableEntry&& entry) { mEntries.push_back(std::move(entry)); } -enum : unsigned int { - ENABLE_INTERFACE_NAME = 1 << 0, - ENABLE_TRANSPORT = 1 << 1, - ENABLE_SERVER_PID = 1 << 2, - ENABLE_SERVER_ADDR = 1 << 3, - ENABLE_CLIENT_PIDS = 1 << 4, - ENABLE_ARCH = 1 << 5, - ENABLE_THREADS = 1 << 6, + void setSelectedColumns(const SelectedColumns& s) { mSelectedColumns = s; } + const SelectedColumns& getSelectedColumns() const { return mSelectedColumns; } + + void setDescription(std::string&& d) { mDescription = std::move(d); } + + // Write table content. + TextTable createTextTable(bool neat = true, + const std::function<std::string(const std::string&)>& emitDebugInfo = nullptr) const; + +private: + std::string mDescription; + Entries mEntries; + SelectedColumns mSelectedColumns; }; -using TableEntrySelect = unsigned int; +using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>; -enum { - NO_PID = -1, - NO_PTR = 0 +class MergedTable { +public: + MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {} + TextTable createTextTable(); +private: + std::vector<const Table*> mTables; }; } // namespace lshal diff --git a/cmds/lshal/TextTable.cpp b/cmds/lshal/TextTable.cpp new file mode 100644 index 0000000000..eca9061034 --- /dev/null +++ b/cmds/lshal/TextTable.cpp @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#include <algorithm> +#include <iomanip> + +#include "TextTable.h" + +namespace android { +namespace lshal { + +void TextTable::computeWidth(const std::vector<std::string>& v) { + if (mWidths.size() < v.size()) { + mWidths.resize(v.size()); + } + for (size_t i = 0; i < v.size(); ++i) { + mWidths[i] = std::max(mWidths[i], v[i].length()); + } +} + +void TextTable::dump(std::ostream& out) const { + out << std::left; + for (const auto& row : mTable) { + if (!row.isRow()) { + out << row.line() << std::endl; + continue; + } + + for (size_t i = 0; i < row.fields().size(); ++i) { + if (i != 0) { + out << " "; + } + // last column does not std::setw to avoid printing unnecessary spaces. + if (i < row.fields().size() - 1) { + out << std::setw(mWidths[i]); + } + out << row.fields()[i]; + } + out << std::endl; + } +} + +void TextTable::addAll(TextTable&& other) { + for (auto&& row : other.mTable) { + if (row.isRow()) { + computeWidth(row.fields()); + } + + mTable.emplace_back(std::move(row)); + } +} + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h new file mode 100644 index 0000000000..91d522aef7 --- /dev/null +++ b/cmds/lshal/TextTable.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ + +#include <iostream> +#include <string> +#include <vector> + +namespace android { +namespace lshal { + +// An element in TextTable. This is either an actual row (an array of cells +// in this row), or a string of explanatory text. +// To see if this is an actual row, test fields().empty(). +class TextTableRow { +public: + // An empty line. + TextTableRow() {} + + // A row of cells. + TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {} + + // A single comment string. + TextTableRow(std::string&& s) : mLine(std::move(s)) {} + TextTableRow(const std::string& s) : mLine(s) {} + + // Whether this row is an actual row of cells. + bool isRow() const { return !fields().empty(); } + + // Get all cells. + const std::vector<std::string>& fields() const { return mFields; } + + // Get the single comment string. + const std::string& line() const { return mLine; } + +private: + std::vector<std::string> mFields; + std::string mLine; +}; + +// A TextTable is a 2D array of strings. +class TextTable { +public: + + // Add a TextTableRow. + void add() { mTable.emplace_back(); } + void add(std::vector<std::string>&& v) { + computeWidth(v); + mTable.emplace_back(std::move(v)); + } + void add(const std::string& s) { mTable.emplace_back(s); } + void add(std::string&& s) { mTable.emplace_back(std::move(s)); } + + void addAll(TextTable&& other); + + // Prints the table to out, with column widths adjusted appropriately according + // to the content. + void dump(std::ostream& out) const; + +private: + void computeWidth(const std::vector<std::string>& v); + std::vector<size_t> mWidths; + std::vector<TextTableRow> mTable; +}; + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 972d508768..9220fc09fb 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -26,21 +26,29 @@ #include <gmock/gmock.h> #include <android/hardware/tests/baz/1.0/IQuux.h> #include <hidl/HidlTransportSupport.h> +#include <vintf/parse_xml.h> +#include "ListCommand.h" #include "Lshal.h" #define NELEMS(array) static_cast<int>(sizeof(array) / sizeof(array[0])) using namespace testing; +using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::hidl::manager::V1_0::IServiceNotification; +using ::android::hardware::hidl_array; using ::android::hardware::hidl_death_recipient; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; +using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; + +using hidl_hash = hidl_array<uint8_t, 32>; + namespace android { namespace hardware { namespace tests { @@ -76,7 +84,6 @@ struct Quux : android::hardware::tests::baz::V1_0::IQuux { namespace lshal { - class MockServiceManager : public IServiceManager { public: template<typename T> @@ -107,7 +114,7 @@ public: }; -class LshalTest : public ::testing::Test { +class DebugTest : public ::testing::Test { public: void SetUp() override { using ::android::hardware::tests::baz::V1_0::IQuux; @@ -122,43 +129,545 @@ public: return new Quux(); return nullptr; })); + + lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager); } void TearDown() override {} std::stringstream err; std::stringstream out; sp<MockServiceManager> serviceManager; + + std::unique_ptr<Lshal> lshal; }; -TEST_F(LshalTest, Debug) { - const char *args[] = { +static Arg createArg(const std::vector<const char*>& args) { + return Arg{static_cast<int>(args.size()), const_cast<char**>(args.data())}; +} + +template<typename T> +static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const char*>& args) { + return lshal->main(createArg(args)); +} + +TEST_F(DebugTest, Debug) { + EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar" - }; - EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast<char **>(args)})); + })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar")); EXPECT_THAT(err.str(), IsEmpty()); } -TEST_F(LshalTest, Debug2) { - const char *args[] = { +TEST_F(DebugTest, Debug2) { + EXPECT_EQ(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux" - }; - EXPECT_EQ(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast<char **>(args)})); + })); EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux")); EXPECT_THAT(err.str(), IsEmpty()); } -TEST_F(LshalTest, Debug3) { - const char *args[] = { +TEST_F(DebugTest, Debug3) { + EXPECT_NE(0u, callMain(lshal, { "lshal", "debug", "android.hardware.tests.doesnotexist@1.0::IDoesNotExist", - }; - EXPECT_NE(0u, Lshal(out, err, serviceManager, serviceManager) - .main({NELEMS(args), const_cast<char **>(args)})); + })); EXPECT_THAT(err.str(), HasSubstr("does not exist")); } +class MockLshal : public Lshal { +public: + MockLshal() {} + ~MockLshal() = default; + MOCK_CONST_METHOD0(out, NullableOStream<std::ostream>()); + MOCK_CONST_METHOD0(err, NullableOStream<std::ostream>()); +}; + +// expose protected fields and methods for ListCommand +class MockListCommand : public ListCommand { +public: + MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} + + Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); } + Status main(const Arg& arg) { return ListCommand::main(arg); } + void forEachTable(const std::function<void(Table &)> &f) { + return ListCommand::forEachTable(f); + } + void forEachTable(const std::function<void(const Table &)> &f) const { + return ListCommand::forEachTable(f); + } + Status fetch() { return ListCommand::fetch(); } + void dumpVintf(const NullableOStream<std::ostream>& out) { + return ListCommand::dumpVintf(out); + } + void internalPostprocess() { ListCommand::postprocess(); } + const PidInfo* getPidInfoCached(pid_t serverPid) { + return ListCommand::getPidInfoCached(serverPid); + } + + MOCK_METHOD0(postprocess, void()); + MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); + MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); +}; + +class ListParseArgsTest : public ::testing::Test { +public: + void SetUp() override { + mockLshal = std::make_unique<NiceMock<MockLshal>>(); + mockList = std::make_unique<MockListCommand>(mockLshal.get()); + // ListCommand::parseArgs should parse arguments from the second element + optind = 1; + } + std::unique_ptr<MockLshal> mockLshal; + std::unique_ptr<MockListCommand> mockList; + std::stringstream output; +}; + +TEST_F(ListParseArgsTest, Args) { + EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"}))); + mockList->forEachTable([](const Table& table) { + EXPECT_EQ(SelectedColumns({TableColumnType::SERVER_PID, TableColumnType::INTERFACE_NAME, + TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}), + table.getSelectedColumns()); + }); +} + +TEST_F(ListParseArgsTest, Cmds) { + EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"}))); + mockList->forEachTable([](const Table& table) { + EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID))) + << "should not print server PID with -m"; + EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS))) + << "should not print client PIDs with -m"; + EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD)) + << "should print server cmd with -m"; + EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS)) + << "should print client cmds with -m"; + }); +} + +TEST_F(ListParseArgsTest, DebugAndNeat) { + ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output))); + EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"}))); + EXPECT_THAT(output.str(), StrNe("")); +} + +/// Fetch Test + +// A set of deterministic functions to generate fake debug infos. +static uint64_t getPtr(pid_t serverId) { return 10000 + serverId; } +static std::vector<pid_t> getClients(pid_t serverId) { + return {serverId + 1, serverId + 3}; +} +static PidInfo getPidInfoFromId(pid_t serverId) { + PidInfo info; + info.refPids[getPtr(serverId)] = getClients(serverId); + info.threadUsage = 10 + serverId; + info.threadCount = 20 + serverId; + return info; +} +static std::string getInterfaceName(pid_t serverId) { + return "a.h.foo" + std::to_string(serverId) + "@" + std::to_string(serverId) + ".0::IFoo"; +} +static std::string getInstanceName(pid_t serverId) { + return std::to_string(serverId); +} +static pid_t getIdFromInstanceName(const hidl_string& instance) { + return atoi(instance.c_str()); +} +static std::string getFqInstanceName(pid_t serverId) { + return getInterfaceName(serverId) + "/" + getInstanceName(serverId); +} +static std::string getCmdlineFromId(pid_t serverId) { + if (serverId == NO_PID) return ""; + return "command_line_" + std::to_string(serverId); +} +static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; } +static hidl_hash getHashFromId(pid_t serverId) { + hidl_hash hash; + bool isReleased = getIsReleasedFromId(serverId); + for (size_t i = 0; i < hash.size(); ++i) { + hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u; + } + return hash; +} + +// Fake service returned by mocked IServiceManager::get. +class TestService : public IBase { +public: + TestService(pid_t id) : mId(id) {} + hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override { + cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT }); + return hardware::Void(); + } + hardware::Return<void> interfaceChain(interfaceChain_cb cb) override { + cb({getInterfaceName(mId), IBase::descriptor}); + return hardware::Void(); + } + hardware::Return<void> getHashChain(getHashChain_cb cb) override { + cb({getHashFromId(mId), getHashFromId(0xff)}); + return hardware::Void(); + } +private: + pid_t mId; +}; + +class ListTest : public ::testing::Test { +public: + void SetUp() override { + initMockServiceManager(); + lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager); + initMockList(); + } + + void initMockList() { + mockList = std::make_unique<NiceMock<MockListCommand>>(lshal.get()); + ON_CALL(*mockList, getPidInfo(_,_)).WillByDefault(Invoke( + [](pid_t serverPid, PidInfo* info) { + *info = getPidInfoFromId(serverPid); + return true; + })); + ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId)); + ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() { + mockList->internalPostprocess(); + size_t i = 0; + mockList->forEachTable([&](Table& table) { + table.setDescription("[fake description " + std::to_string(i++) + "]"); + }); + })); + } + + void initMockServiceManager() { + serviceManager = new testing::NiceMock<MockServiceManager>(); + passthruManager = new testing::NiceMock<MockServiceManager>(); + using A = DebugInfo::Architecture; + ON_CALL(*serviceManager, list(_)).WillByDefault(Invoke( + [] (IServiceManager::list_cb cb) { + cb({ getFqInstanceName(1), getFqInstanceName(2) }); + return hardware::Void(); + })); + + ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( + [&](const hidl_string&, const hidl_string& instance) { + int id = getIdFromInstanceName(instance); + return sp<IBase>(new TestService(id)); + })); + + ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke( + [] (IServiceManager::debugDump_cb cb) { + cb({InstanceDebugInfo{getInterfaceName(3), getInstanceName(3), 3, + getClients(3), A::IS_32BIT}, + InstanceDebugInfo{getInterfaceName(4), getInstanceName(4), 4, + getClients(4), A::IS_32BIT}}); + return hardware::Void(); + })); + + ON_CALL(*passthruManager, debugDump(_)).WillByDefault(Invoke( + [] (IServiceManager::debugDump_cb cb) { + cb({InstanceDebugInfo{getInterfaceName(5), getInstanceName(5), 5, + getClients(5), A::IS_32BIT}, + InstanceDebugInfo{getInterfaceName(6), getInstanceName(6), 6, + getClients(6), A::IS_32BIT}}); + return hardware::Void(); + })); + } + + std::stringstream err; + std::stringstream out; + std::unique_ptr<Lshal> lshal; + std::unique_ptr<MockListCommand> mockList; + sp<MockServiceManager> serviceManager; + sp<MockServiceManager> passthruManager; +}; + +TEST_F(ListTest, GetPidInfoCached) { + EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1); + + EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); + EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); +} + +TEST_F(ListTest, Fetch) { + EXPECT_EQ(0u, mockList->fetch()); + std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough", + "passthrough", "passthrough", "passthrough"}}; + std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}}; + int id = 1; + mockList->forEachTable([&](const Table& table) { + ASSERT_EQ(2u, table.size()); + for (const auto& entry : table) { + const auto& transport = transports[id - 1]; + TableEntry expected{ + .interfaceName = getFqInstanceName(id), + .transport = transport, + .serverPid = transport == "hwbinder" ? id : NO_PID, + .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0, + .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0, + .serverCmdline = {}, + .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR, + .clientPids = getClients(id), + .clientCmdlines = {}, + .arch = archs[id - 1], + }; + EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string(); + + ++id; + } + }); + +} + +TEST_F(ListTest, DumpVintf) { + const std::string expected = + "<!-- \n" + " This is a skeleton device manifest. Notes: \n" + " 1. android.hidl.*, android.frameworks.*, android.system.* are not included.\n" + " 2. If a HAL is supported in both hwbinder and passthrough transport, \n" + " only hwbinder is shown.\n" + " 3. It is likely that HALs in passthrough transport does not have\n" + " <interface> declared; users will have to write them by hand.\n" + " 4. A HAL with lower minor version can be overridden by a HAL with\n" + " higher minor version if they have the same name and major version.\n" + " 5. sepolicy version is set to 0.0. It is recommended that the entry\n" + " is removed from the manifest file and written by assemble_vintf\n" + " at build time.\n" + "-->\n" + "<manifest version=\"1.0\" type=\"device\">\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <version>1.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>1</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo2</name>\n" + " <transport>hwbinder</transport>\n" + " <version>2.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>2</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo3</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <version>3.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>3</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo4</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <version>4.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>4</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo5</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <version>5.0</version>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo6</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <version>6.0</version>\n" + " </hal>\n" + " <sepolicy>\n" + " <version>0.0</version>\n" + " </sepolicy>\n" + "</manifest>\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); + + vintf::HalManifest m; + EXPECT_EQ(true, vintf::gHalManifestConverter(&m, out.str())) + << "--init-vintf does not emit valid HAL manifest: " + << vintf::gHalManifestConverter.lastError(); +} + +// test default columns +TEST_F(ListTest, DumpDefault) { + const std::string expected = + "[fake description 0]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" + "\n" + "[fake description 1]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + " a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "\n" + "[fake description 2]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + " a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpHash) { + const std::string expected = + "[fake description 0]\n" + "Interface R Hash\n" + "a.h.foo1@1.0::IFoo/1 0000000000000000000000000000000000000000000000000000000000000000\n" + "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n" + "\n" + "[fake description 1]\n" + "Interface R Hash\n" + "a.h.foo3@3.0::IFoo/3 \n" + "a.h.foo4@4.0::IFoo/4 \n" + "\n" + "[fake description 2]\n" + "Interface R Hash\n" + "a.h.foo5@5.0::IFoo/5 \n" + "a.h.foo6@6.0::IFoo/6 \n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, Dump) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" + "\n" + "[fake description 1]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" + "\n" + "[fake description 2]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpCmdline) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n" + "\n" + "[fake description 1]\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n" + "\n" + "[fake description 2]\n" + "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepacm"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpNeat) { + const std::string expected = + "a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" + "a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +class HelpTest : public ::testing::Test { +public: + void SetUp() override { + lshal = std::make_unique<Lshal>(out, err, new MockServiceManager() /* serviceManager */, + new MockServiceManager() /* passthruManager */); + } + + std::stringstream err; + std::stringstream out; + std::unique_ptr<Lshal> lshal; +}; + +TEST_F(HelpTest, GlobalUsage) { + (void)callMain(lshal, {"lshal", "--help"}); // ignore return + std::string errStr = err.str(); + EXPECT_THAT(errStr, ContainsRegex("(^|\n)commands:($|\n)")) + << "`lshal --help` does not contain global usage"; + EXPECT_THAT(errStr, ContainsRegex("(^|\n)list:($|\n)")) + << "`lshal --help` does not contain usage for 'list' command"; + EXPECT_THAT(errStr, ContainsRegex("(^|\n)debug:($|\n)")) + << "`lshal --help` does not contain usage for 'debug' command"; + EXPECT_THAT(errStr, ContainsRegex("(^|\n)help:($|\n)")) + << "`lshal --help` does not contain usage for 'help' command"; + + err.str(""); + (void)callMain(lshal, {"lshal", "help"}); // ignore return + EXPECT_EQ(errStr, err.str()) << "`lshal help` should have the same output as `lshal --help`"; + + err.str(""); + EXPECT_NE(0u, callMain(lshal, {"lshal", "--unknown-option"})); + EXPECT_THAT(err.str(), ContainsRegex("unrecognized option")); + EXPECT_THAT(err.str(), EndsWith(errStr)) + << "`lshal --unknown-option` should have the same output as `lshal --help`"; + EXPECT_EQ("", out.str()); +} + +TEST_F(HelpTest, UnknownOptionList1) { + (void)callMain(lshal, {"lshal", "help", "list"}); + EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)")) + << "`lshal help list` does not contain usage for 'list' command"; +} + +TEST_F(HelpTest, UnknownOptionList2) { + EXPECT_NE(0u, callMain(lshal, {"lshal", "list", "--unknown-option"})); + EXPECT_THAT(err.str(), ContainsRegex("unrecognized option")); + EXPECT_THAT(err.str(), ContainsRegex("(^|\n)list:($|\n)")) + << "`lshal list --unknown-option` does not contain usage for 'list' command"; + EXPECT_EQ("", out.str()); +} + +TEST_F(HelpTest, UnknownOptionHelp1) { + (void)callMain(lshal, {"lshal", "help", "help"}); + EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)")) + << "`lshal help help` does not contain usage for 'help' command"; +} + +TEST_F(HelpTest, UnknownOptionHelp2) { + (void)callMain(lshal, {"lshal", "help", "--unknown-option"}); + EXPECT_THAT(err.str(), ContainsRegex("(^|\n)help:($|\n)")) + << "`lshal help --unknown-option` does not contain usage for 'help' command"; + EXPECT_EQ("", out.str()); +} + } // namespace lshal } // namespace android diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h index 45b922cc03..c09e8b1666 100644 --- a/cmds/lshal/utils.h +++ b/cmds/lshal/utils.h @@ -29,15 +29,23 @@ namespace lshal { enum : unsigned int { OK = 0, + // Return to Lshal::main to print help info. USAGE = 1 << 0, + // no service managers NO_BINDERIZED_MANAGER = 1 << 1, NO_PASSTHROUGH_MANAGER = 1 << 2, + // general error in getting information from the three sources DUMP_BINDERIZED_ERROR = 1 << 3, DUMP_PASSTHROUGH_ERROR = 1 << 4, DUMP_ALL_LIBS_ERROR = 1 << 5, + // I/O error in reading files IO_ERROR = 1 << 6, + // Interface does not exist (IServiceManager::get fails) NO_INTERFACE = 1 << 7, + // Transaction error from hwbinder transactions TRANSACTION_ERROR = 1 << 8, + // No transaction error, but return value is unexpected. + BAD_IMPL = 1 << 9, }; using Status = unsigned int; diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp index b703ed4c4c..9513ec1c23 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -8,7 +8,11 @@ cc_binary { "libbinder", ], - cflags: ["-DXP_UNIX"], + cflags: [ + "-DXP_UNIX", + "-Wall", + "-Werror", + ], } cc_binary { @@ -22,5 +26,10 @@ cc_binary { "libbinder", ], - cflags: ["-DXP_UNIX", "-DVENDORSERVICES"], + cflags: [ + "-DXP_UNIX", + "-DVENDORSERVICES", + "-Wall", + "-Werror", + ], } diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index d3d396f025..428561bc8a 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -46,6 +46,6 @@ cc_binary { cflags: [ "-DVENDORSERVICEMANAGER=1", ], - shared_libs: ["libcutils", "libselinux_vendor"], + shared_libs: ["libcutils", "libselinux"], init_rc: ["vndservicemanager.rc"], } diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c index 93a18fc0ac..fade8cfb72 100644 --- a/cmds/servicemanager/binder.c +++ b/cmds/servicemanager/binder.c @@ -514,7 +514,7 @@ void bio_put_obj(struct binder_io *bio, void *ptr) return; obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj->type = BINDER_TYPE_BINDER; + obj->hdr.type = BINDER_TYPE_BINDER; obj->binder = (uintptr_t)ptr; obj->cookie = 0; } @@ -532,7 +532,7 @@ void bio_put_ref(struct binder_io *bio, uint32_t handle) return; obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj->type = BINDER_TYPE_HANDLE; + obj->hdr.type = BINDER_TYPE_HANDLE; obj->handle = handle; obj->cookie = 0; } @@ -649,7 +649,7 @@ uint32_t bio_get_ref(struct binder_io *bio) if (!obj) return 0; - if (obj->type == BINDER_TYPE_HANDLE) + if (obj->hdr.type == BINDER_TYPE_HANDLE) return obj->handle; return 0; diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 35b63ec5c3..2b5389b8dc 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -505,7 +505,7 @@ status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegion ALOGV("Setting Transparent Region Hint"); Region re = Region(); - for (auto r : trhc.region()) { + for (const auto& r : trhc.region()) { Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom()); re.merge(rect); } diff --git a/include/android/sensor.h b/include/android/sensor.h index 7f460873b5..a88733cac7 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -388,13 +388,13 @@ ASensorManager* ASensorManager_getInstance(); #endif #if __ANDROID_API__ >= __ANDROID_API_O__ -/* +/** * Get a reference to the sensor manager. ASensorManager is a singleton * per package as different packages may have access to different sensors. * * Example: * - * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); + * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); * */ ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName); @@ -503,14 +503,12 @@ void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId) * {@link ASensor_isDirectChannelTypeSupported}, respectively. * * Example: - * \code{.cpp} - * ASensorManager *manager = ...; - * ASensor *sensor = ...; - * int channelId = ...; * - * ASensorManager_configureDirectReport( - * manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); - * \endcode + * ASensorManager *manager = ...; + * ASensor *sensor = ...; + * int channelId = ...; + * + * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. @@ -530,50 +528,86 @@ int ASensorManager_configureDirectReport( /*****************************************************************************/ /** - * Enable the selected sensor with a specified sampling period and max batch report latency. - * Returns a negative error code on failure. - * Note: To disable the selected sensor, use ASensorEventQueue_disableSensor() same as before. + * Enable the selected sensor with sampling and report parameters + * + * Enable the selected sensor at a specified sampling period and max batch report latency. + * To disable sensor, use {@link ASensorEventQueue_disableSensor}. + * + * \param queue {@link ASensorEventQueue} for sensor event to be report to. + * \param sensor {@link ASensor} to be enabled. + * \param samplingPeriodUs sampling period of sensor in microseconds. + * \param maxBatchReportLatencyus maximum time interval between two batch of sensor events are + * delievered in microseconds. For sensor streaming, set to 0. + * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor, int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs); /** - * Enable the selected sensor. Returns a negative error code on failure. + * Enable the selected sensor at default sampling rate. + * + * Start event reports of a sensor to specified sensor event queue at a default rate. + * + * \param queue {@link ASensorEventQueue} for sensor event to be report to. + * \param sensor {@link ASensor} to be enabled. + * + * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor); /** - * Disable the selected sensor. Returns a negative error code on failure. + * Disable the selected sensor. + * + * Stop event reports from the sensor to specified sensor event queue. + * + * \param queue {@link ASensorEventQueue} to be changed + * \param sensor {@link ASensor} to be disabled + * \return 0 on success or a negative error code on failure. */ int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor); /** * Sets the delivery rate of events in microseconds for the given sensor. + * + * This function has to be called after {@link ASensorEventQueue_enableSensor}. * Note that this is a hint only, generally event will arrive at a higher * rate. It is an error to set a rate inferior to the value returned by * ASensor_getMinDelay(). - * Returns a negative error code on failure. + * + * \param queue {@link ASensorEventQueue} to which sensor event is delivered. + * \param sensor {@link ASensor} of which sampling rate to be updated. + * \param usec sensor sampling period (1/sampling rate) in microseconds + * \return 0 on sucess or a negative error code on failure. */ int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec); /** - * Returns true if there are one or more events available in the - * sensor queue. Returns 1 if the queue has events; 0 if - * it does not have events; and a negative value if there is an error. + * Determine if a sensor event queue has pending event to be processed. + * + * \param queue {@link ASensorEventQueue} to be queried + * \return 1 if the queue has events; 0 if it does not have events; + * or a negative value if there is an error. */ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); /** - * Returns the next available events from the queue. Returns a negative - * value if no events are available or an error has occurred, otherwise - * the number of events returned. + * Retrieve pending events in sensor event queue + * + * Retrieve next available events from the queue to a specified event array. + * + * \param queue {@link ASensorEventQueue} to get events from + * \param events pointer to an array of {@link ASensorEvents}. + * \param count max number of event that can be filled into array event. + * \return number of events returned on success; negative error code when + * no events are pending or an error has occurred. * * Examples: - * ASensorEvent event; - * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1); * - * ASensorEvent eventBuffer[8]; - * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8); + * ASensorEvent event; + * ssize_t numEvent = ASensorEventQueue_getEvents(queue, &event, 1); + * + * ASensorEvent eventBuffer[8]; + * ssize_t numEvent = ASensorEventQueue_getEvents(queue, eventBuffer, 8); * */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h index 380e7454ad..0ec3e9474e 100644 --- a/include/private/ui/RegionHelper.h +++ b/include/private/ui/RegionHelper.h @@ -81,7 +81,6 @@ public: int inside = spanner.next(current.top, current.bottom); spannerInner.prepare(inside); do { - TYPE left, right; int inner_inside = spannerInner.next(current.left, current.right); if ((op_mask >> inner_inside) & 1) { if (current.left < current.right && diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index 890ef30ec7..a81f44ef8e 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -249,6 +249,8 @@ status_t BBinder::onTransact( if (resultReceiver != NULL) { resultReceiver->send(INVALID_OPERATION); } + + return NO_ERROR; } case SYSPROPS_TRANSACTION: { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index e8329613ab..1c3fab4057 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -470,22 +470,33 @@ status_t IPCThreadState::getAndExecuteCommand() void IPCThreadState::processPendingDerefs() { if (mIn.dataPosition() >= mIn.dataSize()) { - size_t numPending = mPendingWeakDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + /* + * The decWeak()/decStrong() calls may cause a destructor to run, + * which in turn could have initiated an outgoing transaction, + * which in turn could cause us to add to the pending refs + * vectors; so instead of simply iterating, loop until they're empty. + * + * We do this in an outer loop, because calling decStrong() + * may result in something being added to mPendingWeakDerefs, + * which could be delayed until the next incoming command + * from the driver if we don't process it now. + */ + while (mPendingWeakDerefs.size() > 0 || mPendingStrongDerefs.size() > 0) { + while (mPendingWeakDerefs.size() > 0) { + RefBase::weakref_type* refs = mPendingWeakDerefs[0]; + mPendingWeakDerefs.removeAt(0); refs->decWeak(mProcess.get()); } - mPendingWeakDerefs.clear(); - } - numPending = mPendingStrongDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - BBinder* obj = mPendingStrongDerefs[i]; + if (mPendingStrongDerefs.size() > 0) { + // We don't use while() here because we don't want to re-order + // strong and weak decs at all; if this decStrong() causes both a + // decWeak() and a decStrong() to be queued, we want to process + // the decWeak() first. + BBinder* obj = mPendingStrongDerefs[0]; + mPendingStrongDerefs.removeAt(0); obj->decStrong(mProcess.get()); } - mPendingStrongDerefs.clear(); } } } @@ -675,7 +686,7 @@ void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) #if LOG_REFCOUNTS ALOGV("IPCThreadState::expungeHandle(%ld)\n", handle); #endif - self()->mProcess->expungeHandle(handle, binder); + self()->mProcess->expungeHandle(handle, binder); // NOLINT } status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index 2a15773aa3..1cfe02a3fe 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -289,7 +289,15 @@ SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) SimpleBestFitAllocator::~SimpleBestFitAllocator() { while(!mList.isEmpty()) { - delete mList.remove(mList.head()); + chunk_t* removed = mList.remove(mList.head()); +#ifdef __clang_analyzer__ + // Clang static analyzer gets confused in this loop + // and generates a false positive warning about accessing + // memory that is already freed. + // Add an "assert" to avoid the confusion. + LOG_ALWAYS_FATAL_IF(mList.head() == removed); +#endif + delete removed; } } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index e22179b15d..84ca3c0781 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -104,7 +104,7 @@ enum { void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { - switch (obj.type) { + switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); @@ -140,7 +140,7 @@ void acquire_object(const sp<ProcessState>& proc, } } - ALOGD("Invalid object type 0x%08x", obj.type); + ALOGD("Invalid object type 0x%08x", obj.hdr.type); } void acquire_object(const sp<ProcessState>& proc, @@ -152,7 +152,7 @@ void acquire_object(const sp<ProcessState>& proc, static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { - switch (obj.type) { + switch (obj.hdr.type) { case BINDER_TYPE_BINDER: if (obj.binder) { LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); @@ -191,7 +191,7 @@ static void release_object(const sp<ProcessState>& proc, } } - ALOGE("Invalid object type 0x%08x", obj.type); + ALOGE("Invalid object type 0x%08x", obj.hdr.type); } void release_object(const sp<ProcessState>& proc, @@ -227,17 +227,17 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/, ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_HANDLE; + obj.hdr.type = BINDER_TYPE_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { - obj.type = BINDER_TYPE_BINDER; + obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); obj.cookie = reinterpret_cast<uintptr_t>(local); } } else { - obj.type = BINDER_TYPE_BINDER; + obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; } @@ -261,12 +261,12 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/, ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_WEAK_HANDLE; + obj.hdr.type = BINDER_TYPE_WEAK_HANDLE; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = handle; obj.cookie = 0; } else { - obj.type = BINDER_TYPE_WEAK_BINDER; + obj.hdr.type = BINDER_TYPE_WEAK_BINDER; obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs()); obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get()); } @@ -281,13 +281,13 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/, // but we can't do that with the different reference counting // implementation we are using. ALOGE("Unable to unflatten Binder weak reference!"); - obj.type = BINDER_TYPE_BINDER; + obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, obj, out); } else { - obj.type = BINDER_TYPE_BINDER; + obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; return finish_flatten_binder(NULL, obj, out); @@ -307,7 +307,7 @@ status_t unflatten_binder(const sp<ProcessState>& proc, const flat_binder_object* flat = in.readObject(false); if (flat) { - switch (flat->type) { + switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); @@ -326,7 +326,7 @@ status_t unflatten_binder(const sp<ProcessState>& proc, const flat_binder_object* flat = in.readObject(false); if (flat) { - switch (flat->type) { + switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); return finish_unflatten_binder(NULL, *flat, in); @@ -543,7 +543,7 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) = reinterpret_cast<flat_binder_object*>(mData + off); acquire_object(proc, *flat, this, &mOpenAshmemSize); - if (flat->type == BINDER_TYPE_FD) { + if (flat->hdr.type == BINDER_TYPE_FD) { // If this is a file descriptor, we need to dup it so the // new Parcel now owns its own fd, and can declare that we // officially know we have fds. @@ -1152,7 +1152,7 @@ status_t Parcel::writeNativeHandle(const native_handle* handle) status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) { flat_binder_object obj; - obj.type = BINDER_TYPE_FD; + obj.hdr.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ obj.handle = fd; @@ -1310,7 +1310,7 @@ restart_write: *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val; // remember if it's a file descriptor - if (val.type == BINDER_TYPE_FD) { + if (val.hdr.type == BINDER_TYPE_FD) { if (!mAllowFds) { // fail before modifying our object index return FDS_NOT_ALLOWED; @@ -2132,7 +2132,7 @@ int Parcel::readFileDescriptor() const { const flat_binder_object* flat = readObject(true); - if (flat && flat->type == BINDER_TYPE_FD) { + if (flat && flat->hdr.type == BINDER_TYPE_FD) { return flat->handle; } @@ -2325,7 +2325,7 @@ void Parcel::closeFileDescriptors() i--; const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { + if (flat->hdr.type == BINDER_TYPE_FD) { //ALOGI("Closing fd: %ld", flat->handle); close(flat->handle); } @@ -2397,7 +2397,7 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]); to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " - << TypeCode(flat->type & 0x7f7f7f00) + << TypeCode(flat->hdr.type & 0x7f7f7f00) << " = " << flat->binder; } } else { @@ -2618,7 +2618,7 @@ status_t Parcel::continueWrite(size_t desired) for (size_t i=objectsSize; i<mObjectsSize; i++) { const flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { + if (flat->hdr.type == BINDER_TYPE_FD) { // will need to rescan because we may have lopped off the only FDs mFdsKnown = false; } @@ -2728,7 +2728,7 @@ void Parcel::scanForFds() const for (size_t i=0; i<mObjectsSize; i++) { const flat_binder_object* flat = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { + if (flat->hdr.type == BINDER_TYPE_FD) { hasFds = true; break; } diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp index fd1dfd5ada..85cd739411 100644 --- a/libs/binder/Value.cpp +++ b/libs/binder/Value.cpp @@ -182,10 +182,12 @@ Value& Value::swap(Value &rhs) Value& Value::operator=(const Value& rhs) { - delete mContent; - mContent = rhs.mContent - ? rhs.mContent->clone() - : NULL; + if (this != &rhs) { + delete mContent; + mContent = rhs.mContent + ? rhs.mContent->clone() + : NULL; + } return *this; } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 3071408540..c451780dd7 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -14,9 +14,20 @@ // limitations under the License. // +cc_defaults { + name: "binder_test_defaults", + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-private-field", + "-Wno-unused-variable", + ], +} + cc_test { name: "binderDriverInterfaceTest_IPC_32", srcs: ["binderDriverInterfaceTest.cpp"], + defaults: ["binder_test_defaults"], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } @@ -30,11 +41,13 @@ cc_test { name: "binderDriverInterfaceTest", srcs: ["binderDriverInterfaceTest.cpp"], + defaults: ["binder_test_defaults"], } cc_test { name: "binderValueTypeTest", srcs: ["binderValueTypeTest.cpp"], + defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", @@ -44,6 +57,7 @@ cc_test { cc_test { name: "binderLibTest_IPC_32", srcs: ["binderLibTest.cpp"], + defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", @@ -59,6 +73,7 @@ cc_test { }, }, + defaults: ["binder_test_defaults"], name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ @@ -70,6 +85,7 @@ cc_test { cc_test { name: "binderThroughputTest", srcs: ["binderThroughputTest.cpp"], + defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", @@ -77,8 +93,6 @@ cc_test { clang: true, cflags: [ "-g", - "-Wall", - "-Werror", "-Wno-missing-field-initializers", "-Wno-sign-compare", "-O3", @@ -88,6 +102,7 @@ cc_test { cc_test { name: "binderTextOutputTest", srcs: ["binderTextOutputTest.cpp"], + defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", @@ -98,6 +113,7 @@ cc_test { cc_test { name: "schd-dbg", srcs: ["schd-dbg.cpp"], + defaults: ["binder_test_defaults"], shared_libs: [ "libbinder", "libutils", @@ -108,9 +124,9 @@ cc_test { cc_test { name: "binderSafeInterfaceTest", srcs: ["binderSafeInterfaceTest.cpp"], + defaults: ["binder_test_defaults"], cppflags: [ - "-Werror", "-Weverything", "-Wno-c++98-compat", "-Wno-c++98-compat-pedantic", diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index ff5912fbe1..b14631dbda 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -139,6 +139,12 @@ TEST_F(BinderDriverInterfaceTest, Version) { ASSERT_EQ(BINDER_CURRENT_PROTOCOL_VERSION, version.protocol_version); } +TEST_F(BinderDriverInterfaceTest, OpenNoMmap) { + int binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); + ASSERT_GE(binderFd, 0); + close(binderFd); +} + TEST_F(BinderDriverInterfaceTest, WriteReadNull) { binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT); } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index a04869ae62..1611e11209 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -28,10 +28,19 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <sys/epoll.h> + #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; +static ::testing::AssertionResult IsPageAligned(void *buf) { + if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0) + return ::testing::AssertionSuccess(); + else + return ::testing::AssertionFailure() << buf << " is not page aligned"; +} + static testing::Environment* binder_env; static char *binderservername; static char *binderserversuffix; @@ -43,7 +52,10 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, BINDER_LIB_TEST_REGISTER_SERVER, BINDER_LIB_TEST_ADD_SERVER, + BINDER_LIB_TEST_ADD_POLL_SERVER, BINDER_LIB_TEST_CALL_BACK, + BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF, + BINDER_LIB_TEST_DELAYED_CALL_BACK, BINDER_LIB_TEST_NOP_CALL_BACK, BINDER_LIB_TEST_GET_SELF_TRANSACTION, BINDER_LIB_TEST_GET_ID_TRANSACTION, @@ -60,7 +72,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, }; -pid_t start_server_process(int arg2) +pid_t start_server_process(int arg2, bool usePoll = false) { int ret; pid_t pid; @@ -68,11 +80,13 @@ pid_t start_server_process(int arg2) int pipefd[2]; char stri[16]; char strpipefd1[16]; + char usepoll[2]; char *childargv[] = { binderservername, binderserverarg, stri, strpipefd1, + usepoll, binderserversuffix, NULL }; @@ -83,6 +97,7 @@ pid_t start_server_process(int arg2) snprintf(stri, sizeof(stri), "%d", arg2); snprintf(strpipefd1, sizeof(strpipefd1), "%d", pipefd[1]); + snprintf(usepoll, sizeof(usepoll), "%d", usePoll ? 1 : 0); pid = fork(); if (pid == -1) @@ -167,14 +182,14 @@ class BinderLibTest : public ::testing::Test { virtual void TearDown() { } protected: - sp<IBinder> addServer(int32_t *idPtr = NULL) + sp<IBinder> addServerEtc(int32_t *idPtr, int code) { int ret; int32_t id; Parcel data, reply; sp<IBinder> binder; - ret = m_server->transact(BINDER_LIB_TEST_ADD_SERVER, data, &reply); + ret = m_server->transact(code, data, &reply); EXPECT_EQ(NO_ERROR, ret); EXPECT_FALSE(binder != NULL); @@ -186,6 +201,17 @@ class BinderLibTest : public ::testing::Test { *idPtr = id; return binder; } + + sp<IBinder> addServer(int32_t *idPtr = NULL) + { + return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER); + } + + sp<IBinder> addPollServer(int32_t *idPtr = NULL) + { + return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER); + } + void waitForReadData(int fd, int timeout_ms) { int ret; pollfd pfd = pollfd(); @@ -265,17 +291,23 @@ class BinderLibTestEvent pthread_mutex_unlock(&m_waitMutex); return ret; } + pthread_t getTriggeringThread() + { + return m_triggeringThread; + } protected: void triggerEvent(void) { pthread_mutex_lock(&m_waitMutex); pthread_cond_signal(&m_waitCond); m_eventTriggered = true; + m_triggeringThread = pthread_self(); pthread_mutex_unlock(&m_waitMutex); }; private: pthread_mutex_t m_waitMutex; pthread_cond_t m_waitCond; bool m_eventTriggered; + pthread_t m_triggeringThread; }; class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent @@ -283,6 +315,7 @@ class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent public: BinderLibTestCallBack() : m_result(NOT_ENOUGH_DATA) + , m_prev_end(NULL) { } status_t getResult(void) @@ -298,16 +331,43 @@ class BinderLibTestCallBack : public BBinder, public BinderLibTestEvent (void)reply; (void)flags; switch(code) { - case BINDER_LIB_TEST_CALL_BACK: - m_result = data.readInt32(); + case BINDER_LIB_TEST_CALL_BACK: { + status_t status = data.readInt32(&m_result); + if (status != NO_ERROR) { + m_result = status; + } triggerEvent(); return NO_ERROR; + } + case BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF: { + sp<IBinder> server; + int ret; + const uint8_t *buf = data.data(); + size_t size = data.dataSize(); + if (m_prev_end) { + /* 64-bit kernel needs at most 8 bytes to align buffer end */ + EXPECT_LE((size_t)(buf - m_prev_end), (size_t)8); + } else { + EXPECT_TRUE(IsPageAligned((void *)buf)); + } + + m_prev_end = buf + size + data.objectsCount() * sizeof(binder_size_t); + + if (size > 0) { + server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); + ret = server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, + data, reply); + EXPECT_EQ(NO_ERROR, ret); + } + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; } } status_t m_result; + const uint8_t *m_prev_end; }; class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestEvent @@ -606,6 +666,65 @@ TEST_F(BinderLibTest, DeathNotificationMultiple) } } +TEST_F(BinderLibTest, DeathNotificationThread) +{ + status_t ret; + sp<BinderLibTestCallBack> callback; + sp<IBinder> target = addServer(); + ASSERT_TRUE(target != NULL); + sp<IBinder> client = addServer(); + ASSERT_TRUE(client != NULL); + + sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); + + ret = target->linkToDeath(testDeathRecipient); + EXPECT_EQ(NO_ERROR, ret); + + { + Parcel data, reply; + ret = target->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); + EXPECT_EQ(0, ret); + } + + /* Make sure it's dead */ + testDeathRecipient->waitEvent(5); + + /* Now, pass the ref to another process and ask that process to + * call linkToDeath() on it, and wait for a response. This tests + * two things: + * 1) You still get death notifications when calling linkToDeath() + * on a ref that is already dead when it was passed to you. + * 2) That death notifications are not directly pushed to the thread + * registering them, but to the threadpool (proc workqueue) instead. + * + * 2) is tested because the thread handling BINDER_LIB_TEST_DEATH_TRANSACTION + * is blocked on a condition variable waiting for the death notification to be + * called; therefore, that thread is not available for handling proc work. + * So, if the death notification was pushed to the thread workqueue, the callback + * would never be called, and the test would timeout and fail. + * + * Note that we can't do this part of the test from this thread itself, because + * the binder driver would only push death notifications to the thread if + * it is a looper thread, which this thread is not. + * + * See b/23525545 for details. + */ + { + Parcel data, reply; + + callback = new BinderLibTestCallBack(); + data.writeStrongBinder(target); + data.writeStrongBinder(callback); + ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); + EXPECT_EQ(NO_ERROR, ret); + } + + ret = callback->waitEvent(5); + EXPECT_EQ(NO_ERROR, ret); + ret = callback->getResult(); + EXPECT_EQ(NO_ERROR, ret); +} + TEST_F(BinderLibTest, PassFile) { int ret; int pipefd[2]; @@ -681,7 +800,7 @@ TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { const flat_binder_object *fb = reply.readObject(false); ASSERT_TRUE(fb != NULL); - EXPECT_EQ(BINDER_TYPE_HANDLE, fb->type); + EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type); EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle)); EXPECT_EQ((binder_uintptr_t)0, fb->cookie); EXPECT_EQ((uint64_t)0, (uint64_t)fb->binder >> 32); @@ -728,6 +847,61 @@ TEST_F(BinderLibTest, FreedBinder) { } } +TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) { + status_t ret; + Parcel data, reply; + sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack(); + for (int i = 0; i < 2; i++) { + BinderLibTestBundle datai; + datai.appendFrom(&data, 0, data.dataSize()); + + data.freeData(); + data.writeInt32(1); + data.writeStrongBinder(callBack); + data.writeInt32(BINDER_LIB_TEST_CALL_BACK_VERIFY_BUF); + + datai.appendTo(&data); + } + ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, OnewayQueueing) +{ + status_t ret; + Parcel data, data2; + + sp<IBinder> pollServer = addPollServer(); + + sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack(); + data.writeStrongBinder(callBack); + data.writeInt32(500000); // delay in us before calling back + + sp<BinderLibTestCallBack> callBack2 = new BinderLibTestCallBack(); + data2.writeStrongBinder(callBack2); + data2.writeInt32(0); // delay in us + + ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, NULL, TF_ONE_WAY); + EXPECT_EQ(NO_ERROR, ret); + + // The delay ensures that this second transaction will end up on the async_todo list + // (for a single-threaded server) + ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, NULL, TF_ONE_WAY); + EXPECT_EQ(NO_ERROR, ret); + + // The server will ensure that the two transactions are handled in the expected order; + // If the ordering is not as expected, an error will be returned through the callbacks. + ret = callBack->waitEvent(2); + EXPECT_EQ(NO_ERROR, ret); + ret = callBack->getResult(); + EXPECT_EQ(NO_ERROR, ret); + + ret = callBack2->waitEvent(2); + EXPECT_EQ(NO_ERROR, ret); + ret = callBack2->getResult(); + EXPECT_EQ(NO_ERROR, ret); +} + class BinderLibTestService : public BBinder { public: @@ -735,6 +909,7 @@ class BinderLibTestService : public BBinder : m_id(id) , m_nextServerId(id + 1) , m_serverStartRequested(false) + , m_callback(NULL) { pthread_mutex_init(&m_serverWaitMutex, NULL); pthread_cond_init(&m_serverWaitCond, NULL); @@ -743,6 +918,16 @@ class BinderLibTestService : public BBinder { exit(EXIT_SUCCESS); } + + void processPendingCall() { + if (m_callback != NULL) { + Parcel data; + data.writeInt32(NO_ERROR); + m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY); + m_callback = NULL; + } + } + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { @@ -774,6 +959,7 @@ class BinderLibTestService : public BBinder pthread_mutex_unlock(&m_serverWaitMutex); return NO_ERROR; } + case BINDER_LIB_TEST_ADD_POLL_SERVER: case BINDER_LIB_TEST_ADD_SERVER: { int ret; uint8_t buf[1] = { 0 }; @@ -788,9 +974,10 @@ class BinderLibTestService : public BBinder } else { serverid = m_nextServerId++; m_serverStartRequested = true; + bool usePoll = code == BINDER_LIB_TEST_ADD_POLL_SERVER; pthread_mutex_unlock(&m_serverWaitMutex); - ret = start_server_process(serverid); + ret = start_server_process(serverid, usePoll); pthread_mutex_lock(&m_serverWaitMutex); } if (ret > 0) { @@ -818,6 +1005,42 @@ class BinderLibTestService : public BBinder } case BINDER_LIB_TEST_NOP_TRANSACTION: return NO_ERROR; + case BINDER_LIB_TEST_DELAYED_CALL_BACK: { + // Note: this transaction is only designed for use with a + // poll() server. See comments around epoll_wait(). + if (m_callback != NULL) { + // A callback was already pending; this means that + // we received a second call while still processing + // the first one. Fail the test. + sp<IBinder> callback = data.readStrongBinder(); + Parcel data2; + data2.writeInt32(UNKNOWN_ERROR); + + callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, NULL, TF_ONE_WAY); + } else { + m_callback = data.readStrongBinder(); + int32_t delayUs = data.readInt32(); + /* + * It's necessary that we sleep here, so the next + * transaction the caller makes will be queued to + * the async queue. + */ + usleep(delayUs); + + /* + * Now when we return, libbinder will tell the kernel + * we are done with this transaction, and the kernel + * can move the queued transaction to either the + * thread todo worklist (for kernels without the fix), + * or the proc todo worklist. In case of the former, + * the next outbound call will pick up the pending + * transaction, which leads to undesired reentrant + * behavior. This is caught in the if() branch above. + */ + } + + return NO_ERROR; + } case BINDER_LIB_TEST_NOP_CALL_BACK: { Parcel data2, reply2; sp<IBinder> binder; @@ -825,7 +1048,7 @@ class BinderLibTestService : public BBinder if (binder == NULL) { return BAD_VALUE; } - reply2.writeInt32(NO_ERROR); + data2.writeInt32(NO_ERROR); binder->transact(BINDER_LIB_TEST_CALL_BACK, data2, &reply2); return NO_ERROR; } @@ -967,16 +1190,26 @@ class BinderLibTestService : public BBinder bool m_serverStartRequested; sp<IBinder> m_serverStarted; sp<IBinder> m_strongRef; + bool m_callbackPending; + sp<IBinder> m_callback; }; -int run_server(int index, int readypipefd) +int run_server(int index, int readypipefd, bool usePoll) { binderLibTestServiceName += String16(binderserversuffix); status_t ret; sp<IServiceManager> sm = defaultServiceManager(); + BinderLibTestService* testServicePtr; { sp<BinderLibTestService> testService = new BinderLibTestService(index); + /* + * We need this below, but can't hold a sp<> because it prevents the + * node from being cleaned up automatically. It's safe in this case + * because of how the tests are written. + */ + testServicePtr = testService.get(); + if (index == 0) { ret = sm->addService(binderLibTestServiceName, testService); } else { @@ -994,8 +1227,53 @@ int run_server(int index, int readypipefd) if (ret) return 1; //printf("%s: joinThreadPool\n", __func__); - ProcessState::self()->startThreadPool(); - IPCThreadState::self()->joinThreadPool(); + if (usePoll) { + int fd; + struct epoll_event ev; + int epoll_fd; + IPCThreadState::self()->setupPolling(&fd); + if (fd < 0) { + return 1; + } + IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER + + epoll_fd = epoll_create1(0); + if (epoll_fd == -1) { + return 1; + } + + ev.events = EPOLLIN; + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) { + return 1; + } + + while (1) { + /* + * We simulate a single-threaded process using the binder poll + * interface; besides handling binder commands, it can also + * issue outgoing transactions, by storing a callback in + * m_callback and setting m_callbackPending. + * + * processPendingCall() will then issue that transaction. + */ + struct epoll_event events[1]; + int numEvents = epoll_wait(epoll_fd, events, 1, 1000); + if (numEvents < 0) { + if (errno == EINTR) { + continue; + } + return 1; + } + if (numEvents > 0) { + IPCThreadState::self()->handlePolledCommands(); + IPCThreadState::self()->flushCommands(); // flush BC_FREE_BUFFER + testServicePtr->processPendingCall(); + } + } + } else { + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + } //printf("%s: joinThreadPool returned\n", __func__); return 1; /* joinThreadPool should not return */ } @@ -1009,9 +1287,9 @@ int main(int argc, char **argv) { binderservername = argv[0]; } - if (argc == 5 && !strcmp(argv[1], binderserverarg)) { - binderserversuffix = argv[4]; - return run_server(atoi(argv[2]), atoi(argv[3])); + if (argc == 6 && !strcmp(argv[1], binderserverarg)) { + binderserversuffix = argv[5]; + return run_server(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]) == 1); } binderserversuffix = new char[16]; snprintf(binderserversuffix, 16, "%d", getpid()); diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp index 156ddff2c6..a8263069de 100644 --- a/libs/diskusage/Android.bp +++ b/libs/diskusage/Android.bp @@ -15,4 +15,5 @@ cc_library_static { name: "libdiskusage", srcs: ["dirsize.c"], + cflags: ["-Wall", "-Werror"], } diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index f2686d5748..9f995380bd 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -19,6 +19,8 @@ cc_library_shared { "GraphicsEnv.cpp", ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ "libnativeloader", "liblog", diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 3996305cd0..cf72d55894 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -25,9 +25,12 @@ cc_library_shared { }, clang: true, + cflags: [ + "-Wall", + "-Werror", + ], cppflags: [ "-Weverything", - "-Werror", // The static constructors and destructors in this library have not been noted to // introduce significant overheads @@ -96,6 +99,7 @@ cc_library_shared { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", + "LayerDebugInfo.cpp", "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 625dc5bcee..c5cab2d730 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -764,7 +764,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &acquireFence, &stickyTransform, &getFrameTimestamps); - Region surfaceDamage = input.getSurfaceDamage(); + const Region& surfaceDamage = input.getSurfaceDamage(); if (acquireFence == NULL) { BQ_LOGE("queueBuffer: fence is NULL"); diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 0a0d112af6..8e7f814313 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -28,6 +28,7 @@ #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/LayerDebugInfo.h> #include <private/gui/LayerState.h> @@ -469,6 +470,36 @@ public: return result; } + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const + { + if (!outLayers) { + return UNEXPECTED_NULL; + } + + Parcel data, reply; + + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + return err; + } + + err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply); + if (err != NO_ERROR) { + return err; + } + + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + return err; + } + if (result != NO_ERROR) { + return result; + } + + outLayers->clear(); + return reply.readParcelableVector(outLayers); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -763,6 +794,17 @@ status_t BnSurfaceComposer::onTransact( } return injectVSync(when); } + case GET_LAYER_DEBUG_INFO: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + std::vector<LayerDebugInfo> outLayers; + status_t result = getLayerDebugInfo(&outLayers); + reply->writeInt32(result); + if (result == NO_ERROR) + { + result = reply->writeParcelableVector(outLayers); + } + return result; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp new file mode 100644 index 0000000000..57ddde075a --- /dev/null +++ b/libs/gui/LayerDebugInfo.cpp @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#include <gui/LayerDebugInfo.h> + +#include <ui/DebugUtils.h> + +#include <binder/Parcel.h> + +#include <utils/String8.h> + +using namespace android; + +#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) + +namespace android { + +status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { + RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mType.c_str())); + RETURN_ON_ERROR(parcel->write(mTransparentRegion)); + RETURN_ON_ERROR(parcel->write(mVisibleRegion)); + RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->writeUint32(mLayerStack)); + RETURN_ON_ERROR(parcel->writeFloat(mX)); + RETURN_ON_ERROR(parcel->writeFloat(mY)); + RETURN_ON_ERROR(parcel->writeUint32(mZ)); + 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(mAlpha)); + RETURN_ON_ERROR(parcel->writeUint32(mFlags)); + RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat)); + RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace))); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); + RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); + RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); + return NO_ERROR; +} + +status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { + mName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mParentName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mType = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + RETURN_ON_ERROR(parcel->read(mTransparentRegion)); + RETURN_ON_ERROR(parcel->read(mVisibleRegion)); + RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->readUint32(&mLayerStack)); + RETURN_ON_ERROR(parcel->readFloat(&mX)); + RETURN_ON_ERROR(parcel->readFloat(&mY)); + RETURN_ON_ERROR(parcel->readUint32(&mZ)); + RETURN_ON_ERROR(parcel->readInt32(&mWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mHeight)); + RETURN_ON_ERROR(parcel->read(mCrop)); + RETURN_ON_ERROR(parcel->read(mFinalCrop)); + RETURN_ON_ERROR(parcel->readFloat(&mAlpha)); + RETURN_ON_ERROR(parcel->readUint32(&mFlags)); + RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat)); + // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways? + mDataSpace = static_cast<android_dataspace>(parcel->readUint32()); + RETURN_ON_ERROR(parcel->errorCheck()); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); + RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); + RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); + return NO_ERROR; +} + +std::string to_string(const LayerDebugInfo& info) { + String8 result; + + result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); + info.mTransparentRegion.dump(result, "TransparentRegion"); + info.mVisibleRegion.dump(result, "VisibleRegion"); + info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); + + result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", + 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("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()); + result.appendFormat("alpha=%.3f, flags=0x%08x, ", + static_cast<double>(info.mAlpha), info.mFlags); + result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]", + static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]), + static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1])); + result.append("\n"); + result.appendFormat(" parent=%s\n", info.mParentName.c_str()); + result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],", + info.mActiveBufferWidth, info.mActiveBufferHeight, + info.mActiveBufferStride, + decodePixelFormat(info.mActiveBufferFormat).c_str()); + result.appendFormat(" queued-frames=%d, mRefreshPending=%d", + info.mNumQueuedFrames, info.mRefreshPending); + result.append("\n"); + return std::string(result.c_str()); +} + +} // android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 5b1c599a13..d9d945dff1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -153,7 +153,10 @@ status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); DisplayStatInfo stats; - status_t err = composerService()->getDisplayStats(NULL, &stats); + status_t result = composerService()->getDisplayStats(NULL, &stats); + if (result != NO_ERROR) { + return result; + } *outRefreshDuration = stats.vsyncPeriod; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index f80ba000b4..b2267426a8 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -39,6 +39,7 @@ struct ComposerState; struct DisplayState; struct DisplayInfo; struct DisplayStatInfo; +class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; class IGraphicBufferProducer; @@ -195,6 +196,12 @@ public: virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; + + /* Gets the list of active layers in Z order for debugging purposes + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; }; // ---------------------------------------------------------------------------- @@ -229,6 +236,7 @@ public: SET_ACTIVE_COLOR_MODE, ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, + GET_LAYER_DEBUG_INFO, CREATE_SCOPED_CONNECTION }; diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h new file mode 100644 index 0000000000..8453e043ef --- /dev/null +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#pragma once + +#include <binder/Parcelable.h> + +#include <ui/PixelFormat.h> +#include <ui/Region.h> + +#include <string> + +namespace android { + +/* Class for transporting debug info from SurfaceFlinger to authorized + * recipients. The class is intended to be a data container. There are + * no getters or setters. + */ +class LayerDebugInfo : public Parcelable { +public: + LayerDebugInfo() = default; + LayerDebugInfo(const LayerDebugInfo&) = default; + virtual ~LayerDebugInfo() = default; + + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + + std::string mName = std::string("NOT FILLED"); + std::string mParentName = std::string("NOT FILLED"); + std::string mType = std::string("NOT FILLED"); + Region mTransparentRegion = Region::INVALID_REGION; + Region mVisibleRegion = Region::INVALID_REGION; + Region mSurfaceDamageRegion = Region::INVALID_REGION; + uint32_t mLayerStack = 0; + float mX = 0.f; + float mY = 0.f; + uint32_t mZ = 0 ; + int32_t mWidth = -1; + int32_t mHeight = -1; + Rect mCrop = Rect::INVALID_RECT; + Rect mFinalCrop = Rect::INVALID_RECT; + float mAlpha = 0.f; + uint32_t mFlags = 0; + PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; + android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN; + // Row-major transform matrix (SurfaceControl::setMatrix()) + float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}}; + int32_t mActiveBufferWidth = -1; + int32_t mActiveBufferHeight = -1; + int32_t mActiveBufferStride = 0; + PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE; + int32_t mNumQueuedFrames = -1; + bool mRefreshPending = false; + bool mIsOpaque = false; + bool mContentDirty = false; +}; + +std::string to_string(const LayerDebugInfo& info); + +} // namespace android diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 8bb705cf77..c15209d32c 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -90,6 +90,16 @@ public: status_t setFlags(uint32_t flags, uint32_t mask); status_t setTransparentRegionHint(const Region& transparent); status_t setAlpha(float alpha=1.0f); + + // Experimentarily it appears that the matrix transforms the + // on-screen rectangle and it's contents before the position is + // applied. + // + // TODO: Test with other combinations to find approximate transformation rules. + // + // For example: + // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives + // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y)) status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy); status_t setCrop(const Rect& crop); status_t setFinalCrop(const Rect& crop); diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index fa87f29aa1..908959ce1a 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -7,6 +7,10 @@ cc_test { test_suites: ["device-tests"], clang: true, + cflags: [ + "-Wall", + "-Werror", + ], srcs: [ "BufferItemConsumer_test.cpp", diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 0982d7e9f4..588e54142f 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -310,8 +310,6 @@ void checkGreyscaleBuffer(const CpuConsumer::LockedBuffer &buf) { uint32_t h = buf.height; const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; - const int blockRows = h / blockHeight; - const int blockCols = w / blockWidth; // Top-left square is bright checkPixel(buf, 0, 0, 191); @@ -349,8 +347,6 @@ void checkRgba8888Buffer(const CpuConsumer::LockedBuffer &buf) { uint32_t h = buf.height; const int blockWidth = w > 16 ? w / 16 : 1; const int blockHeight = h > 16 ? h / 16 : 1; - const int blockRows = h / blockHeight; - const int blockCols = w / blockWidth; // Top-left square is bright red checkPixel(buf, 0, 0, 191, 63, 63); @@ -392,8 +388,6 @@ void checkBayerRawBuffer(const CpuConsumer::LockedBuffer &buf) { uint32_t h = buf.height; const int blockWidth = (w > 16 ? w / 8 : 2) & ~0x1; const int blockHeight = (h > 16 ? h / 8 : 2) & ~0x1; - const int blockRows = h / blockHeight; - const int blockCols = w / blockWidth; // Top-left square is red checkPixel(buf, 0, 0, 1000, 200, 200); diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index c6745d034d..56392867ea 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -323,7 +323,6 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { for (int j = 0; j < numTestPixels; j++) { int x = testPixels[j].x; int y = testPixels[j].y; - uint8_t value = 0; if (j == (i % numTestPixels)) { // We must y-invert the texture coords EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255)); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index e18af17bde..ca43c68f92 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -539,6 +539,9 @@ public: return NO_ERROR; } status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } + status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { + return NO_ERROR; + } protected: IBinder* onAsBinder() override { return nullptr; } @@ -1075,7 +1078,6 @@ TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); - const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); @@ -1089,7 +1091,6 @@ TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { EXPECT_EQ(initialCompositorTiming.presentLatency, compositeToPresentLatency); - const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); addFrameEvents(true, 0, 1); @@ -1162,7 +1163,6 @@ TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { nsecs_t expectedDeadline = initialCompositorTiming.deadline; EXPECT_EQ(expectedDeadline, compositeDeadline); - const uint64_t fId1 = getNextFrameId(); dequeueAndQueue(0); addFrameEvents(true, NO_FRAME_INDEX, 0); @@ -1175,7 +1175,6 @@ TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { initialCompositorTiming.deadline +initialCompositorTiming.interval; EXPECT_EQ(expectedDeadline, compositeDeadline); - const uint64_t fId2 = getNextFrameId(); dequeueAndQueue(1); addFrameEvents(true, 0, 1); diff --git a/libs/hwc2on1adapter/Android.bp b/libs/hwc2on1adapter/Android.bp index ec9cbf8429..420a1f6066 100644 --- a/libs/hwc2on1adapter/Android.bp +++ b/libs/hwc2on1adapter/Android.bp @@ -17,9 +17,13 @@ cc_library_shared { vendor: true, clang: true, + cflags: [ + "-Wall", + "-Werror", + "-Wno-user-defined-warnings", + ], cppflags: [ "-Weverything", - "-Wall", "-Wunused", "-Wunreachable-code", diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d8dc9575ba..9abd04ca04 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -450,7 +450,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, break; } - case AINPUT_EVENT_TYPE_MOTION: { + case InputMessage::TYPE_MOTION: { ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); if (batchIndex >= 0) { Batch& batch = mBatches.editItemAt(batchIndex); diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index b174fa8bbb..62acea360e 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -556,6 +556,46 @@ static bool solveLeastSquares(const float* x, const float* y, return true; } +/* + * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to + * the default implementation + */ +static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) { + float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; + + for (size_t i = 0; i < count; i++) { + float xi = x[i]; + float yi = y[i]; + float xi2 = xi*xi; + float xi3 = xi2*xi; + float xi4 = xi3*xi; + float xi2yi = xi2*yi; + float xiyi = xi*yi; + + sxi += xi; + sxi2 += xi2; + sxiyi += xiyi; + sxi2yi += xi2yi; + syi += yi; + sxi3 += xi3; + sxi4 += xi4; + } + + float Sxx = sxi2 - sxi*sxi / count; + float Sxy = sxiyi - sxi*syi / count; + float Sxx2 = sxi3 - sxi*sxi2 / count; + float Sx2y = sxi2yi - sxi2*syi / count; + float Sx2x2 = sxi4 - sxi2*sxi2 / count; + + float numerator = Sxy*Sx2x2 - Sx2y*Sxx2; + float denominator = Sxx*Sx2x2 - Sxx2*Sxx2; + if (denominator == 0) { + ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2); + return 0; + } + return numerator/denominator; +} + bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const { outEstimator->clear(); @@ -597,6 +637,19 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, degree = m - 1; } if (degree >= 1) { + if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = 2; + outEstimator->confidence = 1; + outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0 + outEstimator->yCoeff[0] = 0; + outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m); + outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m); + outEstimator->xCoeff[2] = 0; + outEstimator->yCoeff[2] = 0; + return true; + } + float xdet, ydet; uint32_t n = degree + 1; if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 029a42091e..0028655075 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -7,6 +7,12 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-error=sign-compare", // to fix later + "-Wno-unused-variable", + ], shared_libs: [ "libinput", "libcutils", @@ -24,5 +30,7 @@ cc_library_static { srcs: ["StructLayout_test.cpp"], cflags: [ "-O0", + "-Wall", + "-Werror", ], } diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp index 0ed24a2a1e..0184f56dc4 100644 --- a/libs/math/tests/Android.bp +++ b/libs/math/tests/Android.bp @@ -18,22 +18,26 @@ cc_test { name: "vec_test", srcs: ["vec_test.cpp"], static_libs: ["libmath"], + cflags: ["-Wall", "-Werror"], } cc_test { name: "mat_test", srcs: ["mat_test.cpp"], static_libs: ["libmath"], + cflags: ["-Wall", "-Werror"], } cc_test { name: "half_test", srcs: ["half_test.cpp"], static_libs: ["libmath"], + cflags: ["-Wall", "-Werror"], } cc_test { name: "quat_test", srcs: ["quat_test.cpp"], static_libs: ["libmath"], + cflags: ["-Wall", "-Werror"], } diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index e61fbd6e8b..64eb110198 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -34,6 +34,12 @@ cc_library { clang: true, + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-function", + ], + cppflags: [ "-std=c++1z" ], diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index b89c35ac64..20071be668 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -23,4 +23,5 @@ cc_test { srcs: [ "AHardwareBufferTest.cpp", "c_compatibility.c"], + cflags: ["-Wall", "-Werror"], } diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 171a627c7e..940ff5afbc 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -16,9 +16,12 @@ cc_library_shared { name: "libsensor", clang: true, + cflags: [ + "-Wall", + "-Werror", + ], cppflags: [ "-Weverything", - "-Werror", // The static constructors and destructors in this library have not been noted to // introduce significant overheads diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 9d530fc5fa..9fd84bcb5a 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -17,6 +17,8 @@ cc_test { clang: true, + cflags: ["-Wall", "-Werror"], + srcs: [ "Sensor_test.cpp", ], diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 59173cbf3d..07aba321a8 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -20,9 +20,12 @@ cc_library_shared { }, clang: true, + cflags: [ + "-Wall", + "-Werror", + ], cppflags: [ "-Weverything", - "-Werror", // The static constructors and destructors in this library have not been noted to // introduce significant overheads @@ -114,4 +117,7 @@ cc_library_headers { vendor_available: true, } -subdirs = ["tests"] +subdirs = [ + "tests", + "tools", +] diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index d5676cc2b8..2d72944665 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -16,10 +16,13 @@ #include <ui/DebugUtils.h> #include <ui/PixelFormat.h> +#include <ui/Rect.h> #include <android-base/stringprintf.h> #include <string> +using android::base::StringPrintf; + std::string decodeStandard(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); switch (dataspaceSelect) { @@ -187,7 +190,7 @@ std::string decodeRange(android_dataspace dataspace) { std::string dataspaceDetails(android_dataspace dataspace) { if (dataspace == 0) { - return "Default (0)"; + return "Default"; } return android::base::StringPrintf("%s %s %s", decodeStandard(dataspace).c_str(), decodeTransfer(dataspace).c_str(), @@ -262,3 +265,7 @@ std::string decodePixelFormat(android::PixelFormat format) { return android::base::StringPrintf("Unknown %#08x", format); } } + +std::string to_string(const android::Rect& rect) { + return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); +} diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index d52c508003..d85448968a 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -99,7 +99,7 @@ status_t GraphicBufferMapper::unlock(buffer_handle_t handle) { int32_t fenceFd = -1; status_t error = unlockAsync(handle, &fenceFd); - if (error == NO_ERROR) { + if (error == NO_ERROR && fenceFd >= 0) { sync_wait(fenceFd, -1); close(fenceFd); } @@ -129,29 +129,6 @@ status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, return static_cast<status_t>(error); } -static inline bool isValidYCbCrPlane(const android_flex_plane_t& plane) { - if (plane.bits_per_component != 8) { - ALOGV("Invalid number of bits per component: %d", - plane.bits_per_component); - return false; - } - if (plane.bits_used != 8) { - ALOGV("Invalid number of bits used: %d", plane.bits_used); - return false; - } - - bool hasValidIncrement = plane.h_increment == 1 || - (plane.component != FLEX_COMPONENT_Y && plane.h_increment == 2); - hasValidIncrement = hasValidIncrement && plane.v_increment > 0; - if (!hasValidIncrement) { - ALOGV("Invalid increment: h %d v %d", plane.h_increment, - plane.v_increment); - return false; - } - - return true; -} - status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, int fenceFd) { diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h index 30f4a59fe0..dad9446b3a 100644 --- a/libs/ui/include/ui/DebugUtils.h +++ b/libs/ui/include/ui/DebugUtils.h @@ -21,9 +21,14 @@ #include <string> +namespace android { +class Rect; +} + std::string decodeStandard(android_dataspace dataspace); std::string decodeTransfer(android_dataspace dataspace); std::string decodeRange(android_dataspace dataspace); std::string dataspaceDetails(android_dataspace dataspace); std::string decodeColorMode(android_color_mode colormode); std::string decodePixelFormat(android::PixelFormat format); +std::string to_string(const android::Rect& rect); diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 6733505090..08067fcf5d 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -18,10 +18,12 @@ cc_test { name: "Region_test", shared_libs: ["libui"], srcs: ["Region_test.cpp"], + cflags: ["-Wall", "-Werror"], } cc_test { name: "colorspace_test", shared_libs: ["libui"], srcs: ["colorspace_test.cpp"], + cflags: ["-Wall", "-Werror"], } diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index b3da120c74..fb69d5cf59 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -230,7 +230,7 @@ void HardwareComposer::OnPostThreadResumed() { // Standalones only create the composer client once and then use SetPowerMode // to control the screen on pause/resume. if (!is_standalone_device_ || !composer_) { - composer_.reset(new Hwc2::Composer(false)); + composer_.reset(new Hwc2::Composer("default")); composer_callback_ = new ComposerCallback; composer_->registerCallback(composer_callback_); Layer::SetComposer(composer_.get()); diff --git a/opengl/include/GLES/gl.h b/opengl/include/GLES/gl.h index 36acff937f..25033f2a39 100644 --- a/opengl/include/GLES/gl.h +++ b/opengl/include/GLES/gl.h @@ -51,6 +51,7 @@ extern "C" { #ifndef GL_VERSION_ES_CM_1_0 #define GL_VERSION_ES_CM_1_0 1 typedef void GLvoid; +typedef char GLchar; typedef unsigned int GLenum; #include <KHR/khrplatform.h> typedef khronos_float_t GLfloat; diff --git a/opengl/include/GLES/glext.h b/opengl/include/GLES/glext.h index b6fe620827..1a150e34e8 100644 --- a/opengl/include/GLES/glext.h +++ b/opengl/include/GLES/glext.h @@ -104,7 +104,6 @@ GL_API void GL_APIENTRY glBlendEquationOES (GLenum mode); #ifndef GL_OES_byte_coordinates #define GL_OES_byte_coordinates 1 -typedef khronos_int8_t GLbyte; #endif /* GL_OES_byte_coordinates */ #ifndef GL_OES_compressed_ETC1_RGB8_sub_texture @@ -128,7 +127,6 @@ typedef khronos_int8_t GLbyte; #ifndef GL_OES_draw_texture #define GL_OES_draw_texture 1 -typedef short GLshort; #define GL_TEXTURE_CROP_RECT_OES 0x8B9D typedef void (GL_APIENTRYP PFNGLDRAWTEXSOESPROC) (GLshort x, GLshort y, GLshort z, GLshort width, GLshort height); typedef void (GL_APIENTRYP PFNGLDRAWTEXIOESPROC) (GLint x, GLint y, GLint z, GLint width, GLint height); @@ -409,7 +407,6 @@ GL_API GLbitfield GL_APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *expon #ifndef GL_OES_single_precision #define GL_OES_single_precision 1 -typedef khronos_float_t GLclampf; typedef void (GL_APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); typedef void (GL_APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); typedef void (GL_APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 802b3b46e0..32c2d7e0d5 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -3,6 +3,7 @@ cc_library { name: "libETC1", srcs: ["ETC1/etc1.cpp"], host_supported: true, + cflags: ["-Wall", "-Werror"], target: { android: { @@ -56,6 +57,9 @@ cc_defaults { "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", "-fvisibility=hidden", + "-Wall", + "-Werror", + "-Wno-unused-variable", ], shared_libs: [ // ***** DO NOT ADD NEW DEPENDENCIES HERE ***** @@ -87,6 +91,11 @@ cc_defaults { defaults: ["gl_libs_defaults"], cflags: [ "-DLOG_TAG=\"libEGL\"", + "-Wall", + "-Werror", + "-Wno-error=deprecated-register", + "-Wno-error=unknown-attributes", + "-Wno-unused-variable", ], shared_libs: [ // ***** DO NOT ADD NEW DEPENDENCIES HERE ***** diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index f1b30c7957..0624b609ce 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -18,6 +18,7 @@ #include "BlobCache.h" +#include <errno.h> #include <inttypes.h> #include <cutils/properties.h> diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 371239ddc0..399affc509 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -389,7 +389,7 @@ static void* load_system_driver(const char* kind) { static bool find(std::string& result, const std::string& pattern, const char* const search, bool exact) { if (exact) { - std::string absolutePath = std::string(search) + "/" + pattern; + std::string absolutePath = std::string(search) + "/" + pattern + ".so"; if (!access(absolutePath.c_str(), R_OK)) { result = absolutePath; return true; diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index 837cfa92ca..72b4823ea0 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -116,17 +116,23 @@ void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { if (gl_extensions.empty()) { // call the implementation's glGetString(GL_EXTENSIONS) const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS); - gl_extensions = exts; - if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) { - gl_extensions.insert(0, "GL_EXT_debug_marker "); - } - // tokenize the supported extensions for the glGetStringi() wrapper - std::stringstream ss; - std::string str; - ss << gl_extensions; - while (ss >> str) { - tokenized_gl_extensions.push_back(str); + // If this context is sharing with another context, and the other context was reset + // e.g. due to robustness failure, this context might also be reset and glGetString can + // return NULL. + if (exts) { + gl_extensions = exts; + if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) { + gl_extensions.insert(0, "GL_EXT_debug_marker "); + } + + // tokenize the supported extensions for the glGetStringi() wrapper + std::stringstream ss; + std::string str; + ss << gl_extensions; + while (ss >> str) { + tokenized_gl_extensions.push_back(str); + } } } } diff --git a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt index 772b21a132..285bba46de 100644 --- a/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt +++ b/opengl/specs/EGL_ANDROID_get_native_client_buffer.txt @@ -16,15 +16,15 @@ Contact Status - Draft + Complete Version - Version 1.0, January 27, 2017 + Version 3, October 11, 2017 Number - EGL Extension #XXX + EGL Extension #123 Dependencies @@ -43,11 +43,11 @@ Overview New Types -struct AHardwareBuffer + struct AHardwareBuffer New Procedures and Functions -EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) + EGLClientBuffer eglGetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer) New Tokens @@ -62,7 +62,7 @@ Changes to Chapter 3 of the EGL 1.2 Specification (EGL Functions and Errors) "The command EGLClientBuffer eglGetNativeClientBufferANDROID( - AHardwareBuffer *buffer) + const struct AHardwareBuffer *buffer) may be used to create an EGLClientBuffer from an AHardwareBuffer object. EGL implementations must guarantee that the lifetime of the returned @@ -92,6 +92,9 @@ Issues Revision History +#3 (Jesse Hall, October 11, 2017) + - Assigned extension number, fixed minor issues for publication + #2 (Craig Donner, February 17, 2017) - Fix typographical errors. diff --git a/opengl/tests/hwc/hwcRects.cpp b/opengl/tests/hwc/hwcRects.cpp index 69e56ff59b..5956366809 100644 --- a/opengl/tests/hwc/hwcRects.cpp +++ b/opengl/tests/hwc/hwcRects.cpp @@ -170,7 +170,7 @@ static EGLSurface surface; static EGLint width, height; // Function prototypes -static Rectangle parseRect(string rectStr); +static Rectangle parseRect(const string& rectStr); void init(void); void printSyntax(const char *cmd); @@ -358,7 +358,7 @@ main(int argc, char *argv[]) // Parse string description of rectangle and add it to list of rectangles // to be rendered. -static Rectangle parseRect(string rectStr) +static Rectangle parseRect(const string& rectStr) { int rv; string str; diff --git a/services/Android.bp b/services/Android.bp deleted file mode 100644 index 7a8ee5d010..0000000000 --- a/services/Android.bp +++ /dev/null @@ -1 +0,0 @@ -subdirs = [ "*" ] diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 4fd98e2a7e..238cba362c 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -37,6 +37,9 @@ cc_library_shared { ], cflags: [ + "-Wall", + "-Wextra", + "-Werror", "-Wno-unused-parameter", // TODO: Move inputflinger to its own process and mark it hidden //-fvisibility=hidden diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index 50589b484a..99fe0f5d55 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -69,12 +69,6 @@ namespace android { static const char *WAKE_LOCK_ID = "KeyEvents"; static const char *DEVICE_PATH = "/dev/input"; -/* return the larger integer */ -static inline int max(int v1, int v2) -{ - return (v1 > v2) ? v1 : v2; -} - static inline const char* toString(bool value) { return value ? "true" : "false"; } diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 69067d225b..42f9ba9047 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -1132,8 +1132,6 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, INJECTION_PERMISSION_DENIED }; - nsecs_t startTime = now(); - // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. int32_t displayId = entry->displayId; diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index b8e9bce4bd..775dbdc658 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -32,6 +32,8 @@ cc_library_shared { ], cflags: [ + "-Wall", + "-Werror", "-Wno-unused-parameter", // TODO: Move inputflinger to its own process and mark it hidden //-fvisibility=hidden @@ -47,6 +49,8 @@ cc_binary { srcs: ["main.cpp"], + cflags: ["-Wall", "-Werror"], + shared_libs: [ "libbinder", "libinputflingerhost", diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 29d93f034b..a49c8c84c7 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -7,7 +7,11 @@ cc_test { "InputDispatcher_test.cpp", ], test_per_src: true, - cflags: ["-Wno-unused-parameter"], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + ], shared_libs = [ "libcutils", "liblog", diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp index ca91b8e85f..0227164ef5 100644 --- a/services/schedulerservice/Android.bp +++ b/services/schedulerservice/Android.bp @@ -3,6 +3,7 @@ cc_library_shared { srcs: [ "SchedulingPolicyService.cpp", ], + cflags: ["-Wall", "-Werror"], shared_libs: [ "libhidlbase", "libhidltransport", diff --git a/services/schedulerservice/SchedulingPolicyService.cpp b/services/schedulerservice/SchedulingPolicyService.cpp index 1f6ed57913..a1106cf49b 100644 --- a/services/schedulerservice/SchedulingPolicyService.cpp +++ b/services/schedulerservice/SchedulingPolicyService.cpp @@ -17,6 +17,8 @@ #include "SchedulingPolicyService.h" +#include <private/android_filesystem_config.h> // for AID_CAMERASERVER + #include <log/log.h> #include <hwbinder/IPCThreadState.h> #include <mediautils/SchedulingPolicyService.h> @@ -28,8 +30,9 @@ namespace V1_0 { namespace implementation { bool SchedulingPolicyService::isAllowed() { - // TODO(b/37291237) - return true; + using ::android::hardware::IPCThreadState; + + return IPCThreadState::self()->getCallingUid() == AID_CAMERASERVER; } Return<bool> SchedulingPolicyService::requestPriority(int32_t pid, int32_t tid, int32_t priority) { diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 8d381b1c31..a7f3a5235e 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -14,6 +14,7 @@ cc_library_shared { "RecentEventLogger.cpp", "RotationVectorSensor.cpp", "SensorDevice.cpp", + "SensorDeviceUtils.cpp", "SensorDirectConnection.cpp", "SensorEventConnection.cpp", "SensorFusion.cpp", diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 7d9b0b730a..535d0db2d2 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -26,11 +26,10 @@ #include <cinttypes> #include <thread> -using android::hardware::hidl_vec; - using namespace android::hardware::sensors::V1_0; using namespace android::hardware::sensors::V1_0::implementation; - +using android::hardware::hidl_vec; +using android::SensorDeviceUtils::HidlServiceRegistrationWaiter; namespace android { // --------------------------------------------------------------------------- @@ -52,7 +51,8 @@ static status_t StatusFromResult(Result result) { } } -SensorDevice::SensorDevice() : mHidlTransportErrors(20) { +SensorDevice::SensorDevice() + : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) { if (!connectHidlService()) { return; } @@ -87,35 +87,29 @@ SensorDevice::SensorDevice() : mHidlTransportErrors(20) { } bool SensorDevice::connectHidlService() { - // SensorDevice may wait upto 100ms * 10 = 1s for hidl service. - constexpr auto RETRY_DELAY = std::chrono::milliseconds(100); + // SensorDevice will wait for HAL service to start if HAL is declared in device manifest. size_t retry = 10; - while (true) { - int initStep = 0; + while (retry-- > 0) { mSensors = ISensors::getService(); - if (mSensors != nullptr) { - ++initStep; - // 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 - break; - } - // hidl service is restarting, pointer is invalid. - mSensors = nullptr; + if (mSensors == nullptr) { + // no sensor hidl service found + break; } - if (--retry <= 0) { - ALOGE("Cannot connect to ISensors hidl service!"); + 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 break; } - // Delay 100ms before retry, hidl service is expected to come up in short time after - // crash. - ALOGI("%s unsuccessful, try again soon (remaining retry %zu).", - (initStep == 0) ? "getService()" : "poll() check", retry); - std::this_thread::sleep_for(RETRY_DELAY); + + // hidl service is restarting, pointer is invalid. + mSensors = nullptr; + ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry); + mRestartWaiter->wait(); } return (mSensors != nullptr); } @@ -173,7 +167,7 @@ ssize_t SensorDevice::getSensorList(sensor_t const** list) { } status_t SensorDevice::initCheck() const { - return mSensors != NULL ? NO_ERROR : NO_INIT; + return mSensors != nullptr ? NO_ERROR : NO_INIT; } ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index fd6cee67ea..6d7505158d 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SENSOR_DEVICE_H #define ANDROID_SENSOR_DEVICE_H +#include "SensorDeviceUtils.h" #include "SensorServiceUtils.h" #include <sensor/Sensor.h> @@ -39,12 +40,9 @@ namespace android { // --------------------------------------------------------------------------- -using SensorServiceUtil::Dumpable; -using hardware::Return; -class SensorDevice : public Singleton<SensorDevice>, public Dumpable { +class SensorDevice : public Singleton<SensorDevice>, public SensorServiceUtil::Dumpable { public: - class HidlTransportErrorLog { public: @@ -105,7 +103,7 @@ public: private: friend class Singleton<SensorDevice>; - sp<android::hardware::sensors::V1_0::ISensors> mSensors; + sp<hardware::sensors::V1_0::ISensors> mSensors; Vector<sensor_t> mSensorList; std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; @@ -174,6 +172,8 @@ private: } return std::move(ret); } + //TODO(b/67425500): remove waiter after bug is resolved. + sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter; bool isClientDisabled(void* ident); bool isClientDisabledLocked(void* ident); diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp new file mode 100644 index 0000000000..b1344becd7 --- /dev/null +++ b/services/sensorservice/SensorDeviceUtils.cpp @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#include "SensorDeviceUtils.h" + +#include <android/hardware/sensors/1.0/ISensors.h> +#include <utils/Log.h> + +#include <chrono> +#include <thread> + +using ::android::hardware::Void; +using namespace android::hardware::sensors::V1_0; + +namespace android { +namespace SensorDeviceUtils { + +HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() + : mRegistered(ISensors::registerForNotifications("default", this)) { +} + +Return<void> HidlServiceRegistrationWaiter::onRegistration( + const hidl_string &fqName, const hidl_string &name, bool preexisting) { + ALOGV("onRegistration fqName %s, name %s, preexisting %d", + fqName.c_str(), name.c_str(), preexisting); + + { + std::lock_guard<std::mutex> lk(mLock); + mRestartObserved = true; + } + mCondition.notify_all(); + return Void(); +} + +void HidlServiceRegistrationWaiter::reset() { + std::lock_guard<std::mutex> lk(mLock); + mRestartObserved = false; +} + +bool HidlServiceRegistrationWaiter::wait() { + constexpr int DEFAULT_WAIT_MS = 100; + constexpr int TIMEOUT_MS = 1000; + + if (!mRegistered) { + ALOGW("Cannot register service notification, use default wait(%d ms)", DEFAULT_WAIT_MS); + std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_WAIT_MS)); + // not sure if service is actually restarted + return false; + } + + std::unique_lock<std::mutex> lk(mLock); + return mCondition.wait_for(lk, std::chrono::milliseconds(TIMEOUT_MS), + [this]{return mRestartObserved;}); +} + +} // namespace SensorDeviceUtils +} // namespace android diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h new file mode 100644 index 0000000000..da36928105 --- /dev/null +++ b/services/sensorservice/SensorDeviceUtils.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef ANDROID_SENSOR_DEVICE_UTIL +#define ANDROID_SENSOR_DEVICE_UTIL + +#include <android/hidl/manager/1.0/IServiceNotification.h> + +#include <condition_variable> +#include <thread> + +using ::android::hardware::hidl_string; +using ::android::hardware::Return; +using ::android::hidl::manager::V1_0::IServiceNotification; + +namespace android { +namespace SensorDeviceUtils { + +class HidlServiceRegistrationWaiter : public IServiceNotification { +public: + + HidlServiceRegistrationWaiter(); + + Return<void> onRegistration(const hidl_string &fqName, + const hidl_string &name, + bool preexisting) override; + + void reset(); + + /** + * Wait for service restart + * + * @return true if service is restart since last reset(); false otherwise. + */ + bool wait(); +private: + const bool mRegistered; + + std::mutex mLock; + std::condition_variable mCondition; + bool mRestartObserved; +}; + +} // namespace SensorDeviceUtils +} // namespace android; + +#endif // ANDROID_SENSOR_SERVICE_UTIL diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index cc93105543..4775e4ef54 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -2,3 +2,5 @@ cc_library_static { name: "libsurfaceflingerincludes", export_include_dirs: ["."], } + +subdirs = ["tests/fakehwc"]
\ No newline at end of file diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index b28c9ba4cc..248ef53f55 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -65,7 +65,7 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; static bool useTripleFramebuffer = getInt64< ISurfaceFlingerConfigs, - &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) == 3; + &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2) >= 3; #if !defined(EGL_EGLEXT_PROTOTYPES) || !defined(EGL_ANDROID_swap_rectangle) // Dummy implementation in case it is missing. diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index cf01ad0d62..7d6d9886f6 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -157,15 +157,11 @@ void Composer::CommandWriter::writeBufferMetadata( write64(metadata.usage); } -Composer::Composer(bool useVrComposer) +Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize), - mIsUsingVrComposer(useVrComposer) + mIsUsingVrComposer(serviceName == std::string("vr")) { - if (mIsUsingVrComposer) { - mComposer = IComposer::getService("vr"); - } else { - mComposer = IComposer::getService(); // use default name - } + mComposer = IComposer::getService(serviceName); if (mComposer == nullptr) { LOG_ALWAYS_FATAL("failed to get hwcomposer service"); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 533509be35..31a3c1d785 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -136,7 +136,7 @@ private: // Composer is a wrapper to IComposer, a proxy to server-side composer. class Composer { public: - Composer(bool useVrComposer); + Composer(const std::string& serviceName); std::vector<IComposer::Capability> getCapabilities(); std::string dumpDebugInfo(); diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 1ac21c6210..93c6d5486f 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -34,6 +34,7 @@ #include <gui/BufferQueue.h> #include <gui/Surface.h> +#include <ui/DebugUtils.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> @@ -103,6 +104,7 @@ status_t FramebufferSurface::advanceFrame() { sp<Fence> acquireFence(Fence::NO_FENCE); android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN; status_t result = nextBuffer(slot, buf, acquireFence, dataspace); + mDataSpace = dataspace; if (result != NO_ERROR) { ALOGE("error latching next FramebufferSurface buffer: %s (%d)", strerror(-result), result); @@ -249,7 +251,10 @@ status_t FramebufferSurface::compositionComplete() #endif void FramebufferSurface::dumpAsString(String8& result) const { - ConsumerBase::dumpState(result); + Mutex::Autolock lock(mMutex); + result.appendFormat("FramebufferSurface: dataspace: %s(%d)\n", + dataspaceDetails(mDataSpace).c_str(), mDataSpace); + ConsumerBase::dumpLocked(result, ""); } void FramebufferSurface::dumpLocked(String8& result, const char* prefix) const diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index 69a72d7ede..a1756ca3c2 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -83,6 +83,13 @@ private: // or the buffer is not associated with a slot. int mCurrentBufferSlot; + // mDataSpace is the dataspace of the current composition buffer for + // this FramebufferSurface. It will be 0 when HWC is doing the + // compositing. Otherwise it will display the dataspace of the buffer + // use for compositing which can change as wide-color content is + // on/off. + android_dataspace mDataSpace; + // mCurrentBuffer is the current buffer or NULL to indicate that there is // no current buffer. sp<GraphicBuffer> mCurrentBuffer; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index ae44ae0e83..78c0c8567a 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -98,8 +98,8 @@ private: // Device methods -Device::Device(bool useVrComposer) - : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)), +Device::Device(const std::string& serviceName) + : mComposer(std::make_unique<Hwc2::Composer>(serviceName)), mCapabilities(), mDisplays(), mRegisteredCallback(false) diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 949f0e3f82..fbe4c7ebed 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -79,10 +79,9 @@ class ComposerCallback { class Device { public: - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - Device(bool useVrComposer); + // Service name is expected to be 'default' or 'vr' for normal use. + // 'vr' will slightly modify the behavior of the mComposer. + Device(const std::string& serviceName); void registerCallback(ComposerCallback* callback, int32_t sequenceId); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3f3c67b1e6..b096a3ae57 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -59,7 +59,7 @@ namespace android { // --------------------------------------------------------------------------- -HWComposer::HWComposer(bool useVrComposer) +HWComposer::HWComposer(const std::string& serviceName) : mHwcDevice(), mDisplayData(2), mFreeDisplaySlots(), @@ -73,7 +73,7 @@ HWComposer::HWComposer(bool useVrComposer) mVSyncCounts[i] = 0; } - mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer); + mHwcDevice = std::make_unique<HWC2::Device>(serviceName); mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index e25dee1de3..3640bb5a98 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -65,10 +65,9 @@ class String8; class HWComposer { public: - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - HWComposer(bool useVrComposer); + // Uses the named composer service. Valid choices for normal use + // are 'default' and 'vr'. + HWComposer(const std::string& serviceName); ~HWComposer(); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 038ece2e05..e92565fd9c 100755 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -40,6 +40,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueue.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include "clz.h" @@ -2420,69 +2421,51 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- -void Layer::dump(String8& result, Colorizer& colorizer) const -{ - const Layer::State& s(getDrawingState()); - - colorizer.colorize(result, Colorizer::GREEN); - result.appendFormat( - "+ %s %p (%s)\n", - getTypeId(), this, getName().string()); - colorizer.reset(result); - - s.activeTransparentRegion.dump(result, "transparentRegion"); - visibleRegion.dump(result, "visibleRegion"); - surfaceDamageRegion.dump(result, "surfaceDamageRegion"); - sp<Client> client(mClientRef.promote()); - PixelFormat pf = PIXEL_FORMAT_UNKNOWN; - const sp<GraphicBuffer>& buffer(getActiveBuffer()); - if (buffer != NULL) { - pf = buffer->getPixelFormat(); - } - - result.appendFormat( " " - "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), " - "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), " - "isOpaque=%1d, invalidate=%1d, " - "dataspace=%s, pixelformat=%s " -#ifdef USE_HWC2 - "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#else - "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#endif - " client=%p\n", - getLayerStack(), s.z, - s.active.transform.tx(), s.active.transform.ty(), - s.active.w, s.active.h, - s.crop.left, s.crop.top, - s.crop.right, s.crop.bottom, - s.finalCrop.left, s.finalCrop.top, - s.finalCrop.right, s.finalCrop.bottom, - isOpaque(s), contentDirty, - dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(), - s.alpha, s.flags, - s.active.transform[0][0], s.active.transform[0][1], - s.active.transform[1][0], s.active.transform[1][1], - client.get()); - - sp<const GraphicBuffer> buf0(mActiveBuffer); - uint32_t w0=0, h0=0, s0=0, f0=0; - if (buf0 != 0) { - w0 = buf0->getWidth(); - h0 = buf0->getHeight(); - s0 = buf0->getStride(); - f0 = buf0->format; - } - result.appendFormat( - " " - "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " queued-frames=%d, mRefreshPending=%d\n", - mFormat, w0, h0, s0,f0, - mQueuedFrames, mRefreshPending); - - if (mSurfaceFlingerConsumer != 0) { - mSurfaceFlingerConsumer->dumpState(result, " "); +LayerDebugInfo Layer::getLayerDebugInfo() const { + LayerDebugInfo info; + const Layer::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.mVisibleRegion = visibleRegion; + info.mSurfaceDamageRegion = surfaceDamageRegion; + info.mLayerStack = getLayerStack(); + info.mX = ds.active.transform.tx(); + info.mY = ds.active.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.mAlpha = ds.alpha; + info.mFlags = ds.flags; + info.mPixelFormat = getPixelFormat(); + info.mDataSpace = getDataSpace(); + 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]; + { + sp<const GraphicBuffer> activeBuffer = getActiveBuffer(); + if (activeBuffer != 0) { + info.mActiveBufferWidth = activeBuffer->getWidth(); + info.mActiveBufferHeight = activeBuffer->getHeight(); + info.mActiveBufferStride = activeBuffer->getStride(); + info.mActiveBufferFormat = activeBuffer->format; + } else { + info.mActiveBufferWidth = 0; + info.mActiveBufferHeight = 0; + info.mActiveBufferStride = 0; + info.mActiveBufferFormat = 0; + } } + info.mNumQueuedFrames = getQueuedFrameCount(); + info.mRefreshPending = isBufferLatched(); + info.mIsOpaque = isOpaque(ds); + info.mContentDirty = contentDirty; + return info; } #ifdef USE_HWC2 diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index c34d8a0930..f7b82e4fb7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -60,6 +60,7 @@ class Colorizer; class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; +class LayerDebugInfo; // --------------------------------------------------------------------------- @@ -447,6 +448,8 @@ public: bool hasQueuedFrame() const { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } + int32_t getQueuedFrameCount() const { return mQueuedFrames; } + #ifdef USE_HWC2 // ----------------------------------------------------------------------- @@ -479,9 +482,9 @@ public: inline const State& getCurrentState() const { return mCurrentState; } inline State& getCurrentState() { return mCurrentState; } + LayerDebugInfo getLayerDebugInfo() const; /* always call base class first */ - void dump(String8& result, Colorizer& colorizer) const; #ifdef USE_HWC2 static void miniDumpHeader(String8& result); void miniDump(String8& result, int32_t hwcId) const; @@ -679,6 +682,9 @@ public: sp<IGraphicBufferProducer> getProducer() const; const String8& getName() const; void notifyAvailableFrames(); + + PixelFormat getPixelFormat() const { return mFormat; } + private: // ----------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 4055693e45..4157144e3f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -46,6 +46,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -138,6 +139,21 @@ bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; bool SurfaceFlinger::hasWideColorDisplay; + +std::string getHwcServiceName() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.hwc_service_name", value, "default"); + ALOGI("Using HWComposer service: '%s'", value); + return std::string(value); +} + +bool useTrebleTestingOverride() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.treble_testing_override", value, "false"); + ALOGI("Treble testing override: '%s'", value); + return std::string(value) == "true"; +} + SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), mTransactionFlags(0), @@ -146,6 +162,7 @@ SurfaceFlinger::SurfaceFlinger() mLayersRemoved(false), mLayersAdded(false), mRepaintEverything(0), + mHwcServiceName(getHwcServiceName()), mRenderEngine(nullptr), mBootTime(systemTime()), mBuiltinDisplays(), @@ -247,6 +264,15 @@ SurfaceFlinger::SurfaceFlinger() // but since /data may be encrypted, we need to wait until after vold // comes online to attempt to read the property. The property is // instead read after the boot animation + + if (useTrebleTestingOverride()) { + // Without the override SurfaceFlinger cannot connect to HIDL + // services that are not listed in the manifests. Considered + // deriving the setting from the set service name, but it + // would be brittle if the name that's not 'default' is used + // for production purposes later on. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + } } void SurfaceFlinger::onFirstRef() @@ -601,7 +627,7 @@ void SurfaceFlinger::init() { LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, "Starting with vr flinger active is not currently supported."); - mHwc.reset(new HWComposer(false)); + mHwc.reset(new HWComposer(mHwcServiceName)); mHwc->registerCallback(this, mComposerSequenceId); if (useVrFlinger) { @@ -1092,6 +1118,33 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + 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); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -1382,7 +1435,8 @@ void SurfaceFlinger::updateVrFlinger() { resetDisplayState(); mHwc.reset(); // Delete the current instance before creating the new one - mHwc.reset(new HWComposer(vrFlingerRequestsDisplay)); + mHwc.reset(new HWComposer( + vrFlingerRequestsDisplay ? "vr" : mHwcServiceName)); mHwc->registerCallback(this, ++mComposerSequenceId); LOG_ALWAYS_FATAL_IF(!mHwc->getComposer()->isRemote(), @@ -2255,7 +2309,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) sp<const DisplayDevice> hw(mDisplays[dpy]); if (layer->belongsToDisplay(hw->getLayerStack(), hw->isPrimary())) { if (disp == NULL) { - disp = hw; + disp = std::move(hw); } else { disp = NULL; break; @@ -3029,7 +3083,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } if (what & layer_state_t::eRelativeLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); flags |= eTransactionNeeded|eTraversalNeeded; } } @@ -3755,7 +3812,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 99d4a1a636..231709d408 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -313,6 +313,7 @@ private: 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; /* ------------------------------------------------------------------------ @@ -672,6 +673,10 @@ private: // acquiring mStateLock. std::unique_ptr<HWComposer> mHwc; +#ifdef USE_HWC2 + const std::string mHwcServiceName; // "default" for real use, something else for testing. +#endif + // constant members (no synchronization needed for access) RenderEngine* mRenderEngine; nsecs_t mBootTime; diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index b28fe68224..1d6fbaf19b 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -41,6 +41,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -178,6 +179,8 @@ SurfaceFlinger::SurfaceFlinger() maxFrameBufferAcquiredBuffers = getInt64< ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::maxFrameBufferAcquiredBuffers>(2); + mPrimaryDispSync.init(hasSyncFramework, dispSyncPresentTimeOffset); + char value[PROPERTY_VALUE_MAX]; property_get("ro.bq.gpu_to_cpu_unsupported", value, "0"); @@ -931,6 +934,34 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + 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 + status_t err = mStateLock.timedLock(s2ns(1)); + bool locked = (err == NO_ERROR); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -2577,6 +2608,14 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } } + if (what & layer_state_t::eRelativeLayerChanged) { + ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); + if (layer->setRelativeLayer(s.relativeLayerHandle, s.z)) { + mCurrentState.layersSortedByZ.removeAt(idx); + mCurrentState.layersSortedByZ.add(layer); + flags |= eTransactionNeeded|eTraversalNeeded; + } + } if (what & layer_state_t::eSizeChanged) { if (layer->setSize(s.w, s.h)) { flags |= eTraversalNeeded; @@ -3254,7 +3293,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp new file mode 100644 index 0000000000..94f3f2561a --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -0,0 +1,35 @@ +cc_test { + name: "sffakehwc_test", + srcs: [ + "FakeComposerClient.cpp", + "FakeComposerService.cpp", + "FakeComposerUtils.cpp", + "SFFakeHwc_test.cpp" + ], + shared_libs: [ + "libcutils", + "libutils", + "libbinder", + "libui", + "libgui", + "liblog", + "libnativewindow", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.mapper@2.0", + "libhwbinder", + "libhardware", + "libhidlbase", + "libsync", + "libfmq", + "libbase", + "libhidltransport" + ], + static_libs: [ + "libhwcomposer-client", + "libsurfaceflingerincludes", + "libtrace_proto", + "libgmock" + ], + tags: ["tests"], + test_suites: ["device-tests"] +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp new file mode 100644 index 0000000000..60916f3ab9 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp @@ -0,0 +1,613 @@ +/* + * 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 "FakeComposer" + +#include "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +#include <inttypes.h> +#include <time.h> +#include <algorithm> +#include <condition_variable> +#include <iostream> +#include <mutex> +#include <set> +#include <thread> + +constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0); +constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1); + +using namespace sftest; + +using android::Condition; +using android::Mutex; + +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point<Clock>; + +namespace { + +// Internal state of a layer in the HWC API. +class LayerImpl { +public: + LayerImpl() = default; + + bool mValid = true; + RenderState mRenderState; + uint32_t mZ = 0; +}; + +// Struct for storing per frame rectangle state. Contains the render +// state shared to the test case. Basically a snapshot and a subset of +// LayerImpl sufficient to re-create the pixels of a layer for the +// frame. +struct FrameRect { +public: + FrameRect(Layer layer_, const RenderState& state, uint32_t z_) + : layer(layer_), renderState(state), z(z_) {} + + const Layer layer; + const RenderState renderState; + const uint32_t z; +}; + +// Collection of FrameRects forming one rendered frame. Could store +// related fences and other data in the future. +class Frame { +public: + Frame() = default; + std::vector<std::unique_ptr<FrameRect>> rectangles; +}; + +class DelayedEventGenerator { +public: + DelayedEventGenerator(std::function<void()> onTimerExpired) + : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {} + + ~DelayedEventGenerator() { + ALOGI("DelayedEventGenerator exiting."); + { + std::unique_lock<std::mutex> lock(mMutex); + mRunning = false; + mWakeups.clear(); + mCondition.notify_one(); + } + mThread.join(); + ALOGI("DelayedEventGenerator exited."); + } + + void wakeAfter(std::chrono::nanoseconds waitTime) { + std::unique_lock<std::mutex> lock(mMutex); + mWakeups.insert(Clock::now() + waitTime); + mCondition.notify_one(); + } + +private: + void loop() { + while (true) { + // Lock scope + { + std::unique_lock<std::mutex> lock(mMutex); + mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); }); + if (!mRunning && mWakeups.empty()) { + // This thread should only exit once the destructor has been called and all + // wakeups have been processed + return; + } + + // At this point, mWakeups will not be empty + + TimePoint target = *(mWakeups.begin()); + auto status = mCondition.wait_until(lock, target); + while (status == std::cv_status::no_timeout) { + // This was either a spurious wakeup or another wakeup was added, so grab the + // oldest point and wait again + target = *(mWakeups.begin()); + status = mCondition.wait_until(lock, target); + } + + // status must have been timeout, so we can finally clear this point + mWakeups.erase(target); + } + // Callback *without* locks! + mOnTimerExpired(); + } + } + + std::function<void()> mOnTimerExpired; + std::thread mThread; + std::mutex mMutex; + std::condition_variable mCondition; + bool mRunning = true; + std::set<TimePoint> mWakeups; +}; + +} // namespace + +FakeComposerClient::FakeComposerClient() + : mCallbacksOn(false), + mClient(nullptr), + mCurrentConfig(NULL_DISPLAY_CONFIG), + mVsyncEnabled(false), + mLayers(), + mDelayedEventGenerator( + std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })), + mSurfaceComposer(nullptr) {} + +FakeComposerClient::~FakeComposerClient() {} + +void FakeComposerClient::removeClient() { + ALOGV("removeClient"); + // TODO: Ahooga! Only thing current lifetime management choices in + // APIs make possible. Sad. + delete this; +} + +void FakeComposerClient::enableCallback(bool enable) { + ALOGV("enableCallback"); + mCallbacksOn = enable; + if (mCallbacksOn) { + mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED); + } +} + +void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) { + if (mCallbacksOn) { + mClient->onHotplug(display, state); + } +} + +uint32_t FakeComposerClient::getMaxVirtualDisplayCount() { + ALOGV("getMaxVirtualDisplayCount"); + return 1; +} + +Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, + PixelFormat* /*format*/, Display* /*outDisplay*/) { + ALOGV("createVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) { + ALOGV("destroyVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) { + ALOGV("createLayer"); + *outLayer = mLayers.size(); + auto newLayer = std::make_unique<LayerImpl>(); + mLayers.push_back(std::move(newLayer)); + return Error::NONE; +} + +Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) { + ALOGV("destroyLayer"); + mLayers[layer]->mValid = false; + return Error::NONE; +} + +Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) { + ALOGV("getActiveConfig"); + + // TODO Assert outConfig != nullptr + + // TODO This is my reading of the + // IComposerClient::getActiveConfig, but returning BAD_CONFIG + // seems to not fit SurfaceFlinger plans. See version 2 below. + // if (mCurrentConfig == NULL_DISPLAY_CONFIG) { + // return Error::BAD_CONFIG; + // } + //*outConfig = mCurrentConfig; + *outConfig = 1; // Very special config for you my friend + return Error::NONE; +} + +Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/, + uint32_t /*height*/, PixelFormat /*format*/, + Dataspace /*dataspace*/) { + ALOGV("getClientTargetSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) { + ALOGV("getColorModes"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayAttribute(Display display, Config config, + IComposerClient::Attribute attribute, + int32_t* outValue) { + ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display), + static_cast<int>(config), static_cast<int>(attribute), outValue); + + // TODO: SOOO much fun to be had with these alone + switch (attribute) { + case IComposerClient::Attribute::WIDTH: + *outValue = 1920; + break; + case IComposerClient::Attribute::HEIGHT: + *outValue = 1080; + break; + case IComposerClient::Attribute::VSYNC_PERIOD: + *outValue = 1666666666; + break; // TOOD: Tests break down if lowered to 16ms? + case IComposerClient::Attribute::DPI_X: + *outValue = 240; + break; + case IComposerClient::Attribute::DPI_Y: + *outValue = 240; + break; + default: + LOG_ALWAYS_FATAL("Say what!?! New attribute"); + } + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) { + ALOGV("getDisplayConfigs"); + // TODO assert display == 1, outConfigs != nullptr + + outConfigs->resize(1); + (*outConfigs)[0] = 1; + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) { + ALOGV("getDisplayName"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayType(Display /*display*/, + IComposerClient::DisplayType* outType) { + ALOGV("getDisplayType"); + // TODO: This setting nothing on the output had no effect on initial trials. Is first display + // assumed to be physical? + *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL); + return Error::NONE; +} + +Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) { + ALOGV("getDozeSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/, + float* /*outMaxLuminance*/, + float* /*outMaxAverageLuminance*/, + float* /*outMinLuminance*/) { + ALOGV("getHdrCapabilities"); + return Error::NONE; +} + +Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) { + ALOGV("setActiveConfig"); + mCurrentConfig = config; + return Error::NONE; +} + +Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) { + ALOGV("setColorMode"); + return Error::NONE; +} + +Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) { + ALOGV("setPowerMode"); + return Error::NONE; +} + +Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) { + mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE); + ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE"); + return Error::NONE; +} + +Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/, + int32_t /*hint*/) { + ALOGV("setColorTransform"); + return Error::NONE; +} + +Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/, + int32_t /*acquireFence*/, int32_t /*dataspace*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setClientTarget"); + return Error::NONE; +} + +Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/, + int32_t /*releaseFence*/) { + ALOGV("setOutputBuffer"); + return Error::NONE; +} + +Error FakeComposerClient::validateDisplay( + Display /*display*/, std::vector<Layer>* /*outChangedLayers*/, + std::vector<IComposerClient::Composition>* /*outCompositionTypes*/, + uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/, + std::vector<uint32_t>* /*outRequestMasks*/) { + ALOGV("validateDisplay"); + // TODO: Assume touching nothing means All Korrekt! + return Error::NONE; +} + +Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) { + ALOGV("acceptDisplayChanges"); + // Didn't ask for changes because software is omnipotent. + return Error::NONE; +} + +bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) { + return a->z <= b->z; +} + +Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/, + std::vector<Layer>* /*outLayers*/, + std::vector<int32_t>* /*outReleaseFences*/) { + ALOGV("presentDisplay"); + // TODO Leaving layers and their fences out for now. Doing so + // means that we've already processed everything. Important to + // test that the fences are respected, though. (How?) + + std::unique_ptr<Frame> newFrame(new Frame); + for (uint64_t layer = 0; layer < mLayers.size(); layer++) { + const LayerImpl& layerImpl = *mLayers[layer]; + + if (!layerImpl.mValid) continue; + + auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ); + newFrame->rectangles.push_back(std::move(rect)); + } + std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering); + { + Mutex::Autolock _l(mStateMutex); + mFrames.push_back(std::move(newFrame)); + mFramesAvailable.broadcast(); + } + return Error::NONE; +} + +Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/, + int32_t /*x*/, int32_t /*y*/) { + ALOGV("setLayerCursorPosition"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) { + ALOGV("setLayerBuffer"); + LayerImpl& l = getLayerImpl(layer); + if (buffer != l.mRenderState.mBuffer) { + l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not? + } + l.mRenderState.mBuffer = buffer; + l.mRenderState.mAcquireFence = acquireFence; + + return Error::NONE; +} + +Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setLayerSurfaceDamage"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) { + ALOGV("setLayerBlendMode"); + getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode); + return Error::NONE; +} + +Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer, + IComposerClient::Color color) { + ALOGV("setLayerColor"); + getLayerImpl(layer).mRenderState.mLayerColor.r = color.r; + getLayerImpl(layer).mRenderState.mLayerColor.g = color.g; + getLayerImpl(layer).mRenderState.mLayerColor.b = color.b; + getLayerImpl(layer).mRenderState.mLayerColor.a = color.a; + return Error::NONE; +} + +Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/, + int32_t /*type*/) { + ALOGV("setLayerCompositionType"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/, + int32_t /*dataspace*/) { + ALOGV("setLayerDataspace"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer, + const hwc_rect_t& frame) { + ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right, + frame.bottom); + getLayerImpl(layer).mRenderState.mDisplayFrame = frame; + return Error::NONE; +} + +Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) { + ALOGV("setLayerPlaneAlpha"); + getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha; + return Error::NONE; +} + +Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/, + buffer_handle_t /*stream*/) { + ALOGV("setLayerSidebandStream"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer, + const hwc_frect_t& crop) { + ALOGV("setLayerSourceCrop"); + getLayerImpl(layer).mRenderState.mSourceCrop = crop; + return Error::NONE; +} + +Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) { + ALOGV("setLayerTransform"); + getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform); + return Error::NONE; +} + +Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer, + const std::vector<hwc_rect_t>& visible) { + ALOGV("setLayerVisibleRegion"); + getLayerImpl(layer).mRenderState.mVisibleRegion = visible; + return Error::NONE; +} + +Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) { + ALOGV("setLayerZOrder"); + getLayerImpl(layer).mZ = z; + return Error::NONE; +} + +////////////////////////////////////////////////////////////////// + +void FakeComposerClient::setClient(ComposerClient* client) { + mClient = client; +} + +void FakeComposerClient::requestVSync(uint64_t vsyncTime) { + if (mCallbacksOn) { + uint64_t timestamp = vsyncTime; + ALOGV("Vsync"); + if (timestamp == 0) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; + } + if (mSurfaceComposer != nullptr) { + mSurfaceComposer->injectVSync(timestamp); + } else { + mClient->onVsync(DEFAULT_DISPLAY, timestamp); + } + } +} + +void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) { + mDelayedEventGenerator->wakeAfter(wait); +} + +LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) { + // TODO Change these to an internal state check that can be + // invoked from the gtest? GTest macros do not seem all that safe + // when used outside the test class + EXPECT_GE(handle, static_cast<Layer>(0)); + EXPECT_LT(handle, mLayers.size()); + return *(mLayers[handle]); +} + +int FakeComposerClient::getFrameCount() const { + return mFrames.size(); +} + +static std::vector<RenderState> extractRenderState( + const std::vector<std::unique_ptr<FrameRect>>& internalRects) { + std::vector<RenderState> result; + result.reserve(internalRects.size()); + for (const std::unique_ptr<FrameRect>& rect : internalRects) { + result.push_back(rect->renderState); + } + return result; +} + +std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[frame]->rectangles); +} + +std::vector<RenderState> FakeComposerClient::getLatestFrame() const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[mFrames.size() - 1]->rectangles); +} + +void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) { + int currentFrame = 0; + { + Mutex::Autolock _l(mStateMutex); // I hope this is ok... + currentFrame = static_cast<int>(mFrames.size()); + requestVSync(); + } + waitUntilFrame(currentFrame + 1, maxWait); +} + +void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const { + Mutex::Autolock _l(mStateMutex); + while (mFrames.size() < static_cast<size_t>(targetFrame)) { + android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count()); + if (result == android::TIMED_OUT) { + ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame, + mFrames.size(), maxWait.count()); + return; + } + } +} + +void FakeComposerClient::clearFrames() { + Mutex::Autolock _l(mStateMutex); + mFrames.clear(); + for (const std::unique_ptr<LayerImpl>& layer : mLayers) { + if (layer->mValid) { + layer->mRenderState.mSwapCount = 0; + } + } +} + +void FakeComposerClient::onSurfaceFlingerStart() { + mSurfaceComposer == nullptr; + do { + mSurfaceComposer = new android::SurfaceComposerClient; + android::status_t initResult = mSurfaceComposer->initCheck(); + if (initResult != android::NO_ERROR) { + ALOGD("Init result: %d", initResult); + mSurfaceComposer = nullptr; + std::this_thread::sleep_for(10ms); + } + } while (mSurfaceComposer == nullptr); + ALOGD("SurfaceComposerClient created"); + mSurfaceComposer->enableVSyncInjections(true); +} + +void FakeComposerClient::onSurfaceFlingerStop() { + mSurfaceComposer->dispose(); + mSurfaceComposer.clear(); +} + +// Includes destroyed layers, stored in order of creation. +int FakeComposerClient::getLayerCount() const { + return mLayers.size(); +} + +Layer FakeComposerClient::getLayer(size_t index) const { + // NOTE: If/when passing calls through to actual implementation, + // this might get more involving. + return static_cast<Layer>(index); +} diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h new file mode 100644 index 0000000000..294abb2c59 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h @@ -0,0 +1,146 @@ +/* + * 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 "ComposerClient.h" +#include "RenderState.h" + +#include <utils/Condition.h> + +#include <chrono> + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using namespace android::hardware; +using namespace std::chrono_literals; + +namespace { +class LayerImpl; +class Frame; +class DelayedEventGenerator; +} // namespace + +namespace android { +class SurfaceComposerClient; +} // namespace android + +namespace sftest { + +class FakeComposerClient : public ComposerBase { +public: + FakeComposerClient(); + virtual ~FakeComposerClient(); + + void removeClient() override; + void enableCallback(bool enable) override; + uint32_t getMaxVirtualDisplayCount() override; + Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, + Display* outDisplay) override; + Error destroyVirtualDisplay(Display display) override; + Error createLayer(Display display, Layer* outLayer) override; + Error destroyLayer(Display display, Layer layer) override; + + Error getActiveConfig(Display display, Config* outConfig) override; + Error getClientTargetSupport(Display display, uint32_t width, uint32_t height, + PixelFormat format, Dataspace dataspace) override; + Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override; + Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue) override; + Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; + Error getDisplayName(Display display, hidl_string* outName) override; + Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override; + Error getDozeSupport(Display display, bool* outSupport) override; + Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance, + float* outMaxAverageLuminance, float* outMinLuminance) override; + + Error setActiveConfig(Display display, Config config) override; + Error setColorMode(Display display, ColorMode mode) override; + Error setPowerMode(Display display, IComposerClient::PowerMode mode) override; + Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; + + Error setColorTransform(Display display, const float* matrix, int32_t hint) override; + Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence, + int32_t dataspace, const std::vector<hwc_rect_t>& damage) override; + Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override; + Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers, + std::vector<IComposerClient::Composition>* outCompositionTypes, + uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, + std::vector<uint32_t>* outRequestMasks) override; + Error acceptDisplayChanges(Display display) override; + Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers, + std::vector<int32_t>* outReleaseFences) override; + + Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override; + Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) override; + Error setLayerSurfaceDamage(Display display, Layer layer, + const std::vector<hwc_rect_t>& damage) override; + Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; + Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override; + Error setLayerCompositionType(Display display, Layer layer, int32_t type) override; + Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override; + Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override; + Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; + Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override; + Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override; + Error setLayerTransform(Display display, Layer layer, int32_t transform) override; + Error setLayerVisibleRegion(Display display, Layer layer, + const std::vector<hwc_rect_t>& visible) override; + Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; + + void setClient(ComposerClient* client); + + void requestVSync(uint64_t vsyncTime = 0); + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + // Wait until next frame is rendered after requesting vsync. + void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms); + void runVSyncAfter(std::chrono::nanoseconds wait); + + int getFrameCount() const; + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const; + std::vector<RenderState> getFrameRects(int frame) const; + std::vector<RenderState> getLatestFrame() const; + void clearFrames(); + + void onSurfaceFlingerStart(); + void onSurfaceFlingerStop(); + + int getLayerCount() const; + Layer getLayer(size_t index) const; + + void hotplugDisplay(Display display, IComposerCallback::Connection state); + +private: + LayerImpl& getLayerImpl(Layer handle); + + bool mCallbacksOn; + ComposerClient* mClient; + Config mCurrentConfig; + bool mVsyncEnabled; + std::vector<std::unique_ptr<LayerImpl>> mLayers; + std::vector<std::unique_ptr<Frame>> mFrames; + // Using a pointer to hide the implementation into the CPP file. + std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator; + android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections + mutable android::Mutex mStateMutex; + mutable android::Condition mFramesAvailable; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp new file mode 100644 index 0000000000..c411604587 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp @@ -0,0 +1,54 @@ +/* + * 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 "FakeHwcService" +#include <log/log.h> + +#include "FakeComposerService.h" + +using namespace android::hardware; + +namespace sftest { + +FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {} + +FakeComposerService::~FakeComposerService() { + ALOGI("Maybe killing client %p", mClient.get()); + // Rely on sp to kill the client. +} + +Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) { + ALOGI("FakeComposerService::getCapabilities"); + hidl_cb(hidl_vec<Capability>()); + return Void(); +} + +Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { + ALOGI("FakeComposerService::dumpDebugInfo"); + hidl_cb(hidl_string()); + return Void(); +} + +Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) { + ALOGI("FakeComposerService::createClient %p", mClient.get()); + mClient->initialize(); + hidl_cb(Error::NONE, mClient); + return Void(); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h new file mode 100644 index 0000000000..520408496f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#pragma once + +#include "ComposerClient.h" + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using android::hardware::Return; + +namespace sftest { + +class FakeComposerService : public IComposer { +public: + FakeComposerService(android::sp<ComposerClient>& client); + virtual ~FakeComposerService(); + + Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; + Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; + Return<void> createClient(createClient_cb hidl_cb) override; + +private: + android::sp<ComposerClient> mClient; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp new file mode 100644 index 0000000000..51956ec970 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcUtil" +#include <log/log.h> + +#include "FakeComposerUtils.h" +#include "RenderState.h" + +#include "SurfaceFlinger.h" // Get the name of the service... + +#include <binder/IServiceManager.h> + +#include <cutils/properties.h> + +#include <iomanip> +#include <thread> + +using android::String16; +using android::sp; +using namespace std::chrono_literals; +using namespace sftest; +using std::setw; + +namespace sftest { + +// clang-format off +inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) { + os << std::fixed << std::setprecision(1) << "(" + << setw(align) << sourceRect.left << setw(0) << "," + << setw(align) << sourceRect.top << setw(0) << "," + << setw(align) << sourceRect.right << setw(0) << "," + << setw(align) << sourceRect.bottom << setw(0) << ")"; +} + +inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) { + os << "(" + << setw(align) << displayRect.left << setw(0) << "," + << setw(align) << displayRect.top << setw(0) << "," + << setw(align) << displayRect.right << setw(0) << "," + << setw(align) << displayRect.bottom << setw(0) << ")"; +} +// clang-format on + +inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) { + printSourceRectAligned(os, state.mSourceCrop, 7); + os << "->"; + printDisplayRectAligned(os, state.mDisplayFrame, 5); + return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3) + << state.mPlaneAlpha << " Xform:" << state.mTransform; +} + +// Helper for verifying the parts of the RenderState +template <typename T> +bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val, + const char* name) { + if (ref != val) { + message = message << "Expected " << name << ":" << ref << ", got:" << val << "."; + return false; + } + return true; +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) { + // TODO: Message could start as success and be assigned as failure. + // Only problem is that utility assumes it to be failure and just adds stuff. Would + // need still special case the initial failure in the utility? + // TODO: ... or would it be possible to break this back to gtest primitives? + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passes = true; + + // The work here is mostly about providing good log strings for differences + passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame"); + passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha"); + passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count"); + passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop"); + // ... add more + if (passes) { + return ::testing::AssertionSuccess(); + } + return message; +} + +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val) { + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passed = true; + if (ref.size() != val.size()) { + message << "Expected " << ref.size() << " rects, got " << val.size() << "."; + passed = false; + } + for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) { + ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]); + if (rectResult == false) { + message << "First different rect at " << rectIndex << ": " << rectResult.message(); + passed = false; + break; + } + } + + if (passed) { + return ::testing::AssertionSuccess(); + } else { + message << "\nReference:"; + for (auto state = ref.begin(); state != ref.end(); ++state) { + message << "\n" << *state; + } + message << "\nActual:"; + for (auto state = val.begin(); state != val.end(); ++state) { + message << "\n" << *state; + } + } + return message; +} + +void startSurfaceFlinger() { + ALOGI("Start SurfaceFlinger"); + system("start surfaceflinger"); + + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf == nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger running"); +} + +void stopSurfaceFlinger() { + ALOGI("Stop SurfaceFlinger"); + system("stop surfaceflinger"); + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf != nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger stopped"); +} + +//////////////////////////////////////////////// + +void FakeHwcEnvironment::SetUp() { + ALOGI("Test env setup"); + system("setenforce 0"); + system("stop"); + property_set("debug.sf.nobootanimation", "1"); + { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.nobootanimation", value, "0"); + LOG_FATAL_IF(atoi(value) != 1, "boot skip not set"); + } + // TODO: Try registering the mock as the default service instead. + property_set("debug.sf.hwc_service_name", "mock"); + // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files. + property_set("debug.sf.treble_testing_override", "true"); +} + +void FakeHwcEnvironment::TearDown() { + ALOGI("Test env tear down"); + system("stop"); + // Wait for mock call signaling teardown? + property_set("debug.sf.nobootanimation", "0"); + property_set("debug.sf.hwc_service_name", "default"); + ALOGI("Test env tear down - done"); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h new file mode 100644 index 0000000000..74dc0e51bb --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h @@ -0,0 +1,119 @@ +/* + * 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 "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <hardware/hwcomposer_defs.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +// clang-format off +// Note: This needs to reside in the global namespace for the GTest to use it +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} + +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} +// clang-format on + +namespace sftest { + +class RenderState; + +// clang-format off +inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} + +inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} +// clang-format on + +inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) { + return !(a == b); +} + +inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) { + return !(a == b); +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val); +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val); + +void startSurfaceFlinger(); +void stopSurfaceFlinger(); + +class FakeHwcEnvironment : public ::testing::Environment { +public: + virtual ~FakeHwcEnvironment() {} + void SetUp() override; + void TearDown() override; +}; + +/* + * All surface state changes are supposed to happen inside a global + * transaction. GlobalTransactionScope object at the beginning of + * scope automates the process. The resulting scope gives a visual cue + * on the span of the transaction as well. + * + * Closing the transaction is synchronous, i.e., it waits for + * SurfaceFlinger to composite one frame. Now, the FakeComposerClient + * is built to explicitly request vsyncs one at the time. A delayed + * request must be made before closing the transaction or the test + * thread stalls until SurfaceFlinger does an emergency vsync by + * itself. GlobalTransactionScope encapsulates this vsync magic. + */ +class GlobalTransactionScope { +public: + GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) { + android::SurfaceComposerClient::openGlobalTransaction(); + } + ~GlobalTransactionScope() { + int frameCount = mComposer.getFrameCount(); + mComposer.runVSyncAfter(1ms); + android::SurfaceComposerClient::closeGlobalTransaction(true); + // Make sure that exactly one frame has been rendered. + mComposer.waitUntilFrame(frameCount + 1); + LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(), + "Unexpected frame advance. Delta: %d", + mComposer.getFrameCount() - frameCount); + } + FakeComposerClient& mComposer; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h new file mode 100644 index 0000000000..0059289d4f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/RenderState.h @@ -0,0 +1,44 @@ +/* + * 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 <hardware/hwcomposer2.h> + +#include <vector> + +namespace sftest { +// Description of a rendered rectangle. Should only contain +// instructions necessary to rasterize the rectangle. The full scene +// is given as a sorted list of rectangles, bottom layer at index 0. +class RenderState { +public: + RenderState() = default; + // Default copy-ctor + + hwc_rect_t mDisplayFrame = {0, 0, 0, 0}; + hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f}; + std::vector<hwc_rect_t> mVisibleRegion; + hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE; + buffer_handle_t mBuffer = 0; + uint32_t mSwapCount = 0; // How many set buffer calls to the layer. + int32_t mAcquireFence = 0; // Probably should not be here. + float mPlaneAlpha = 0.f; + hwc_color_t mLayerColor = {0, 0, 0, 0}; + hwc_transform_t mTransform = static_cast<hwc_transform_t>(0); +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp new file mode 100644 index 0000000000..8902ede301 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -0,0 +1,1306 @@ +/* + * 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 "FakeHwcTest" + +#include "FakeComposerClient.h" +#include "FakeComposerService.h" +#include "FakeComposerUtils.h" + +#include <gui/ISurfaceComposer.h> +#include <gui/LayerDebugInfo.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/gui/ComposerService.h> +#include <private/gui/LayerState.h> + +#include <ui/DisplayInfo.h> + +#include <android/native_window.h> + +#include <android/hidl/manager/1.0/IServiceManager.h> + +#include <hwbinder/ProcessState.h> + +#include <binder/ProcessState.h> + +#include <log/log.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <limits> + +using namespace std::chrono_literals; + +using namespace android; +using namespace android::hardware; + +using namespace sftest; + +namespace { + +// Mock test helpers +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::_; + +/////////////////////////////////////////////// + +struct TestColor { +public: + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +constexpr static TestColor RED = {195, 63, 63, 255}; +constexpr static TestColor LIGHT_RED = {255, 177, 177, 255}; +constexpr static TestColor GREEN = {63, 195, 63, 255}; +constexpr static TestColor BLUE = {63, 63, 195, 255}; +constexpr static TestColor DARK_GRAY = {63, 63, 63, 255}; +constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255}; + +// Fill an RGBA_8888 formatted surface with a single color. +static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color, + bool unlock = true) { + ANativeWindow_Buffer outBuffer; + sp<Surface> s = sc->getSurface(); + ASSERT_TRUE(s != nullptr); + ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); + uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; y++) { + for (int x = 0; x < outBuffer.width; x++) { + uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); + pixel[0] = color.r; + pixel[1] = color.g; + pixel[2] = color.b; + pixel[3] = color.a; + } + } + if (unlock) { + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + } +} + +inline RenderState makeSimpleRect(int left, int top, int right, int bottom) { + RenderState res; + res.mDisplayFrame = hwc_rect_t{left, top, right, bottom}; + res.mPlaneAlpha = 1.0f; + res.mSwapCount = 0; + res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left), + static_cast<float>(bottom - top)}; + return res; +} + +inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right, + unsigned int bottom) { + EXPECT_LE(left, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(top, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(right, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX)); + return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right), + static_cast<int>(bottom)); +} + +//////////////////////////////////////////////// + +class DisplayTest : public ::testing::Test { +public: + class MockComposerClient : public FakeComposerClient { + public: + MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType)); + MOCK_METHOD4(getDisplayAttribute, + Error(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue)); + + // Re-routing to basic fake implementation + Error getDisplayAttributeFake(Display display, Config config, + IComposerClient::Attribute attribute, int32_t* outValue) { + return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue); + } + }; + +protected: + void SetUp() override; + void TearDown() override; + + sp<IComposer> mFakeService; + sp<SurfaceComposerClient> mComposerClient; + + MockComposerClient* mMockComposer; +}; + +void DisplayTest::SetUp() { + // TODO: The mMockComposer should be a unique_ptr, but it needs to + // outlive the test class. Currently ComposerClient only dies + // when the service is replaced. The Mock deletes itself when + // removeClient is called on it, which is ugly. This can be + // changed if HIDL ServiceManager allows removing services or + // ComposerClient starts taking the ownership of the contained + // implementation class. Moving the fake class to the HWC2 + // interface instead of the current Composer interface might also + // change the situation. + mMockComposer = new MockComposerClient; + sp<ComposerClient> client = new ComposerClient(*mMockComposer); + mMockComposer->setClient(client.get()); + mFakeService = new FakeComposerService(client); + mFakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + EXPECT_CALL(*mMockComposer, getDisplayType(1, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy + // but encoding that here exactly. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // TODO: Find out what code is generating the ID 0. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + mMockComposer->onSurfaceFlingerStart(); + + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); +} + +void DisplayTest::TearDown() { + mComposerClient->dispose(); + mComposerClient = nullptr; + + // Fake composer needs to release SurfaceComposerClient before the stop. + mMockComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + + mFakeService = nullptr; + // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime + // management. + mMockComposer = nullptr; +} + +TEST_F(DisplayTest, Hotplug) { + ALOGD("DisplayTest::Hotplug"); + + EXPECT_CALL(*mMockComposer, getDisplayType(2, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // The attribute queries will get done twice. This is for defaults + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _)) + .Times(2 * 3) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // ... and then special handling for dimensions. Specifying this + // rules later means that gmock will try them first, i.e., + // ordering of width/height vs. the default implementation for + // other queries is significant. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE))); + + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE))); + + // TODO: Width and height queries are not actually called. Display + // info returns dimensions 0x0 in display info. Why? + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); + + mMockComposer->clearFrames(); + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); +} + +//////////////////////////////////////////////// + +class TransactionTest : public ::testing::Test { +protected: + // Layer array indexing constants. + constexpr static int BG_LAYER = 0; + constexpr static int FG_LAYER = 1; + + static void SetUpTestCase(); + static void TearDownTestCase(); + + void SetUp() override; + void TearDown() override; + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + std::vector<RenderState> mBaseFrame; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + + static FakeComposerClient* sFakeComposer; +}; + +FakeComposerClient* TransactionTest::sFakeComposer; + +void TransactionTest::SetUpTestCase() { + // TODO: See TODO comment at DisplayTest::SetUp for background on + // the lifetime of the FakeComposerClient. + sFakeComposer = new FakeComposerClient; + sp<ComposerClient> client = new ComposerClient(*sFakeComposer); + sFakeComposer->setClient(client.get()); + sp<IComposer> fakeService = new FakeComposerService(client); + fakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + sFakeComposer->onSurfaceFlingerStart(); +} + +void TransactionTest::TearDownTestCase() { + // Fake composer needs to release SurfaceComposerClient before the stop. + sFakeComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + // TODO: This is deleted when the ComposerClient calls + // removeClient. Devise better lifetime control. + sFakeComposer = nullptr; +} + +void TransactionTest::SetUp() { + ALOGI("TransactionTest::SetUp"); + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + ALOGI("TransactionTest::SetUp - display"); + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + + mDisplayWidth = info.w; + mDisplayHeight = info.h; + + // Background surface + mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth, + mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + fillSurfaceRGBA8(mBGSurfaceControl, BLUE); + + // Foreground surface + mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + fillSurfaceRGBA8(mFGSurfaceControl, RED); + + SurfaceComposerClient::openGlobalTransaction(); + + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show()); + + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + + // Synchronous transaction will stop this thread, so we set up a + // delayed, off-thread vsync request before closing the + // transaction. In the test code this is usually done with + // GlobalTransactionScope. Leaving here in the 'vanilla' form for + // reference. + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + sFakeComposer->runVSyncAfter(1ms); + SurfaceComposerClient::closeGlobalTransaction(true); + sFakeComposer->waitUntilFrame(1); + + // Reference data. This is what the HWC should see. + static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing"); + mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight)); + mBaseFrame[BG_LAYER].mSwapCount = 1; + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + mBaseFrame[FG_LAYER].mSwapCount = 1; + + auto frame = sFakeComposer->getFrameRects(0); + ASSERT_TRUE(framesAreSame(mBaseFrame, frame)); +} + +void TransactionTest::TearDown() { + ALOGD("TransactionTest::TearDown"); + + mComposerClient->dispose(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + mComposerClient = 0; + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.clear(); + sFakeComposer->clearFrames(); + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + std::vector<LayerDebugInfo> layers; + status_t result = sf->getLayerDebugInfo(&layers); + if (result != NO_ERROR) { + ALOGE("Failed to get layers %s %d", strerror(-result), result); + } else { + // If this fails, the test being torn down leaked layers. + EXPECT_EQ(0u, layers.size()); + if (layers.size() > 0) { + for (auto layer = layers.begin(); layer != layers.end(); ++layer) { + std::cout << to_string(*layer).c_str(); + } + // To ensure the next test has clean slate, will run the class + // tear down and setup here. + TearDownTestCase(); + SetUpTestCase(); + } + } + ALOGD("TransactionTest::TearDown - complete"); +} + +TEST_F(TransactionTest, LayerMove) { + ALOGD("TransactionTest::LayerMove"); + + // The scope opens and closes a global transaction and, at the + // same time, makes sure the SurfaceFlinger progresses one frame + // after the transaction closes. The results of the transaction + // should be available in the latest frame stored by the fake + // composer. + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls. + // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?) + // + // sFakeComposer->runVSyncAndWait(); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + // NOTE: Frame 0 is produced in the SetUp. + auto frame1Ref = mBaseFrame; + frame1Ref[FG_LAYER].mDisplayFrame = + hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerResize) { + ALOGD("TransactionTest::LayerResize"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128)); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + auto frame1Ref = mBaseFrame; + // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128}; + frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f}; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerCrop) { + // TODO: Add scaling to confirm that crop happens in buffer space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f}; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerFinalCrop) { + // TODO: Add scaling to confirm that crop happens in display space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(32, 32, 32 + 64, 32 + 64); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(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? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(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) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The layers will switch order, but both are rendered because the background layer is + // transparent (RGBA8888). + std::vector<RenderState> referenceFrame(2); + referenceFrame[0] = mBaseFrame[FG_LAYER]; + referenceFrame[1] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetLayerOpaque) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + ASSERT_EQ(NO_ERROR, + mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, + layer_state_t::eLayerOpaque)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The former foreground layer is now covered with opaque layer - it should have disappeared + std::vector<RenderState> referenceFrame(1); + referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetLayerStack) { + ALOGD("TransactionTest::SetLayerStack"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerShowHide) { + ALOGD("TransactionTest::LayerShowHide"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide()); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + } + + // Foreground layer should be back + ASSERT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f)); + } + + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetFlags) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden, + layer_state_t::eLayerHidden)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetMatrix) { + struct matrixTestData { + float matrix[4]; + hwc_transform_t expectedTransform; + hwc_rect_t expectedDisplayFrame; + }; + + // The matrix operates on the display frame and is applied before + // the position is added. So, the foreground layer rect is (0, 0, + // 64, 64) is first transformed, potentially yielding negative + // coordinates and then the position (64, 64) is added yielding + // the final on-screen rectangles given. + + const matrixTestData MATRIX_TESTS[7] = // clang-format off + {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}}, + {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}}, + {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}}, + {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}}, + {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}}; + // clang-format on + constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData); + + for (int i = 0; i < TEST_COUNT; i++) { + // TODO: How to leverage the HWC2 stringifiers? + const matrixTestData& xform = MATRIX_TESTS[i]; + SCOPED_TRACE(i); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1], + xform.matrix[2], xform.matrix[3])); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mTransform = xform.expectedTransform; + referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + } +} + +#if 0 +TEST_F(TransactionTest, LayerSetMatrix2) { + { + GlobalTransactionScope gts(*sFakeComposer); + // TODO: PLEASE SPEC THE FUNCTION! + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f, + -2.33f, 0.22f)); + } + auto referenceFrame = mBaseFrame; + // TODO: Is this correct for sure? + //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} +#endif + +TEST_F(TransactionTest, DeferredTransaction) { + // Synchronization surface + constexpr static int SYNC_LAYER = 2; + auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(syncSurfaceControl != nullptr); + ASSERT_TRUE(syncSurfaceControl->isValid()); + + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->show()); + } + auto referenceFrame = mBaseFrame; + referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2, + mDisplayWidth - 1, mDisplayHeight - 1)); + referenceFrame[SYNC_LAYER].mSwapCount = 1; + EXPECT_EQ(2, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // set up two deferred transactions on different frames - these should not yield composited + // frames + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber()); + } + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + } + EXPECT_EQ(4, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should trigger the first deferred transaction, but not the second one + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + EXPECT_EQ(5, sFakeComposer->getFrameCount()); + + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should show up immediately since it's not deferred + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0)); + } + referenceFrame[FG_LAYER].mPlaneAlpha = 1.f; + EXPECT_EQ(6, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // trigger the second deferred transaction + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + // TODO: Compute from layer size? + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64}; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_EQ(7, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetRelativeLayer) { + constexpr int RELATIVE_LAYER = 2; + auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED); + + // Now we stack the surface above the foreground surface and make sure it is visible. + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setPosition(64, 64); + relativeSurfaceControl->show(); + relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1); + } + auto referenceFrame = mBaseFrame; + // NOTE: All three layers will be visible as the surfaces are + // transparent because of the RGBA format. + referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + referenceFrame[RELATIVE_LAYER].mSwapCount = 1; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // A call to setLayer will override a call to setRelativeLayer + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setLayer(0); + } + + // Previous top layer will now appear at the bottom. + auto referenceFrame2 = mBaseFrame; + referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]); + EXPECT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +class ChildLayerTest : public TransactionTest { +protected: + constexpr static int CHILD_LAYER = 2; + + void SetUp() override { + TransactionTest::SetUp(); + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + fillSurfaceRGBA8(mChild, LIGHT_GRAY); + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); + mBaseFrame[CHILD_LAYER].mSwapCount = 1; + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + } + void TearDown() override { + mChild = 0; + TransactionTest::TearDown(); + } + + sp<SurfaceControl> mChild; +}; + +TEST_F(ChildLayerTest, Positioning) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + // Move to the same position as in the original setup. + mFGSurfaceControl->setPosition(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())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0)); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Cropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setCrop(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 + // transparent. + 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, FinalCropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setFinalCrop(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) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mFGSurfaceControl->setPosition(0, 0); + mChild->setPosition(63, 63); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Scaling) { + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setPosition(0, 0); + } + 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}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, LayerAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + ASSERT_EQ(NO_ERROR, mChild->setAlpha(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())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(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(ChildLayerTest, ReparentChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(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())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle()); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, DetachChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(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())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->detachChildren(); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->hide(); + } + + // Nothing should have changed. The child control becomes a no-op + // zombie on detach. See comments for detachChildren in the + // SurfaceControl.h file. + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // We cause scaling by 2. + mFGSurfaceControl->setSize(128, 128); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +// Regression test for b/37673612 +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + // We set things up as in b/37673612 so that there is a mismatch between the buffer size and + // the WM specified state size. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 64); + } + + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + + // The child should still be in the same place and not have any strange scaling as in + // b/37673612. + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f}; + referenceFrame[FG_LAYER].mSwapCount++; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Bug36858924) { + // Destroy the child layer + mChild.clear(); + + // Now recreate it as hidden + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, + mFGSurfaceControl.get()); + + // Show the child layer in a deferred transaction + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); + mChild->show(); + } + + // Render the foreground surface a few times + // + // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third + // frame because SurfaceFlinger would never process the deferred transaction and would therefore + // never acquire/release the first buffer + ALOGI("Filling 1"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 2"); + fillSurfaceRGBA8(mFGSurfaceControl, BLUE); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 3"); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 4"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); +} + +class LatchingTest : public TransactionTest { +protected: + void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); } + + void unlockFGBuffer() { + sp<Surface> s = mFGSurfaceControl->getSurface(); + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + sFakeComposer->runVSyncAndWait(); + } + + void completeFGResize() { + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + } + void restoreInitialState() { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(64, 64); + mFGSurfaceControl->setPosition(64, 64); + mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64)); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); + } +}; + +TEST_F(LatchingTest, SurfacePositionLatching) { + // By default position can be updated even while + // a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + + // The size should not have updated as we have not provided a new buffer. + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + // Now we repeat with setGeometryAppliesWithResize + // and verify the position DOESN'T latch. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, CropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, FinalCropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(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(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(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. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(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(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(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. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setFinalCrop(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) { + ::testing::InitGoogleTest(&argc, argv); + + sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment; + ::testing::AddGlobalTestEnvironment(fakeEnvironment); + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp index 062485ea52..4055527b13 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp @@ -382,7 +382,9 @@ public: if (outErr) { *outErr = err; } else { - ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set cursor position"; + ASSERT_TRUE((err == HWC2_ERROR_NONE) || + (err == HWC2_ERROR_BAD_LAYER)) << + "failed to set cursor position"; } } @@ -652,7 +654,7 @@ public: hwc2_layer_request_t request = requests.at(i); EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer), - 0) << "get display requests returned an unknown layer"; + 1) << "get display requests returned an unknown layer"; EXPECT_NE(request, 0) << "returned empty request for layer " << requestedLayer; @@ -1603,9 +1605,10 @@ protected: EXPECT_EQ(layers.size(), fences.size()); for (int32_t fence : fences) { - EXPECT_GE(sync_wait(fence, msWait), 0); - if (fence >= 0) + if (fence >= 0) { + EXPECT_GE(sync_wait(fence, msWait), 0); close(fence); + } } } @@ -1643,8 +1646,9 @@ protected: testLayers->getBlendMode(layer))); EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer, testLayers->getColor(layer))); - EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, cursor.left, - cursor.top)); + if (composition == HWC2_COMPOSITION_CURSOR) + EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer, + cursor.left, cursor.top)); EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer, testLayers->getDataspace(layer))); EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer, @@ -2895,7 +2899,6 @@ TEST_F(Hwc2Test, SET_CURSOR_POSITION_composition_type_unset) ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete, [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer, Hwc2TestLayer* testLayer, hwc2_error_t* outErr) { - const hwc_rect_t cursorPosition = testLayer->getCursorPosition(); EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer, cursorPosition.left, cursorPosition.top, outErr)); @@ -4406,11 +4409,11 @@ TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_display) /* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */ TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter) { - hwc2_display_t display = HWC_DISPLAY_PRIMARY; hwc2_error_t err = HWC2_ERROR_NONE; - - ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err)); - EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code"; + for (auto display : mDisplays) { + ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err)); + EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code"; + } } /* TESTCASE: Tests that the HWC2 can get the max virtual display count. */ diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp index 5e32af0bea..fd271d0fe2 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ b/services/vr/hardware_composer/impl/vr_hwc.cpp @@ -232,7 +232,7 @@ VrHwc::VrHwc() {} VrHwc::~VrHwc() {} -bool VrHwc::hasCapability(Capability capability) const { return false; } +bool VrHwc::hasCapability(Capability /* capability */) const { return false; } void VrHwc::removeClient() { std::lock_guard<std::mutex> guard(mutex_); @@ -306,13 +306,15 @@ Error VrHwc::getActiveConfig(Display display, Config* outConfig) { return Error::NONE; } -Error VrHwc::getClientTargetSupport(Display display, uint32_t width, - uint32_t height, PixelFormat format, - Dataspace dataspace) { +Error VrHwc::getClientTargetSupport(Display /* display */, uint32_t /* width */, + uint32_t /* height */, + PixelFormat /* format */, + Dataspace /* dataspace */) { return Error::NONE; } -Error VrHwc::getColorModes(Display display, hidl_vec<ColorMode>* outModes) { +Error VrHwc::getColorModes(Display /* display */, + hidl_vec<ColorMode>* outModes) { std::vector<ColorMode> color_modes(1, ColorMode::NATIVE); *outModes = hidl_vec<ColorMode>(color_modes); return Error::NONE; @@ -379,7 +381,7 @@ Error VrHwc::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) { return Error::NONE; } -Error VrHwc::getDisplayName(Display display, hidl_string* outName) { +Error VrHwc::getDisplayName(Display /* display */, hidl_string* outName) { *outName = hidl_string(); return Error::NONE; } @@ -409,7 +411,8 @@ Error VrHwc::getDozeSupport(Display display, bool* outSupport) { return Error::NONE; } -Error VrHwc::getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, +Error VrHwc::getHdrCapabilities(Display /* display */, + hidl_vec<Hdr>* /* outTypes */, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) { @@ -473,8 +476,8 @@ Error VrHwc::setColorTransform(Display display, const float* matrix, } Error VrHwc::setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t dataspace, - const std::vector<hwc_rect_t>& damage) { + int32_t acquireFence, int32_t /* dataspace */, + const std::vector<hwc_rect_t>& /* damage */) { base::unique_fd fence(acquireFence); std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); @@ -490,7 +493,7 @@ Error VrHwc::setClientTarget(Display display, buffer_handle_t target, return Error::NONE; } -Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer, +Error VrHwc::setOutputBuffer(Display display, buffer_handle_t /* buffer */, int32_t releaseFence) { base::unique_fd fence(releaseFence); std::lock_guard<std::mutex> guard(mutex_); @@ -505,8 +508,9 @@ Error VrHwc::setOutputBuffer(Display display, buffer_handle_t buffer, Error VrHwc::validateDisplay( Display display, std::vector<Layer>* outChangedLayers, std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) { + uint32_t* /* outDisplayRequestMask */, + std::vector<Layer>* /* outRequestedLayers */, + std::vector<uint32_t>* /* outRequestMasks */) { std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); if (!display_ptr) @@ -517,7 +521,7 @@ Error VrHwc::validateDisplay( return Error::NONE; } -Error VrHwc::acceptDisplayChanges(Display display) { return Error::NONE; } +Error VrHwc::acceptDisplayChanges(Display /* display */) { return Error::NONE; } Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers, @@ -709,8 +713,8 @@ Error VrHwc::setLayerPlaneAlpha(Display display, Layer layer, float alpha) { return Error::NONE; } -Error VrHwc::setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) { +Error VrHwc::setLayerSidebandStream(Display display, Layer /* layer */, + buffer_handle_t /* stream */) { std::lock_guard<std::mutex> guard(mutex_); if (!FindDisplay(display)) return Error::BAD_DISPLAY; diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api index 3a2b1efb0b..2981a95196 100644 --- a/vulkan/api/vulkan.api +++ b/vulkan/api/vulkan.api @@ -28,7 +28,7 @@ import platform "platform.api" // API version (major.minor.patch) define VERSION_MAJOR 1 define VERSION_MINOR 0 -define VERSION_PATCH 54 +define VERSION_PATCH 61 // API limits define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 @@ -102,6 +102,10 @@ define NULL_HANDLE 0 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION 1 @extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME "VK_NV_glsl_shader" +// 14 +@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1 +@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_NAME "VK_EXT_depth_range_unrestricted" + // 15 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 @extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME "VK_KHR_sampler_mirror_clamp_to_edge" @@ -183,7 +187,7 @@ define NULL_HANDLE 0 @extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" // 61 -@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1 +@extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_SPEC_VERSION 2 @extension("VK_KHX_device_group") define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group" // 62 @@ -267,7 +271,7 @@ define NULL_HANDLE 0 @extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" // 87 -@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1 +@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 @extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" // 88 @@ -319,7 +323,7 @@ define NULL_HANDLE 0 @extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles" // 105 -@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 2 +@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 3 @extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" // 106 @@ -346,6 +350,10 @@ define NULL_HANDLE 0 @extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1 @extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd" +// 118 +@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 +@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" + // 120 @extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 @extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" @@ -363,7 +371,7 @@ define NULL_HANDLE 0 @extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" // 128 -@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1 +@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 @extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" // 131 @@ -378,10 +386,34 @@ define NULL_HANDLE 0 @extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1 @extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16" +// 137 +@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1 +@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples" + +// 138 +@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1 +@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask" + +// 141 +@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1 +@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export" + +// 144 +@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1 +@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations" + +// 145 +@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1 +@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout" + // 147 @extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_SPEC_VERSION 1 @extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" +// 148 +@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1 +@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list" + // 149 @extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2 @extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced" @@ -398,6 +430,26 @@ define NULL_HANDLE 0 @extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1 @extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle" +// 156 +@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1 +@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage" + +// 157 +@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1 +@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" + +// 158 +@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_SPEC_VERSION 1 +@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_EXTENSION_NAME "VK_KHR_bind_memory2" + +// 161 +@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1 +@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache" + +// 165 +@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1 +@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer" + ///////////// // Types // ///////////// @@ -456,6 +508,11 @@ type u32 VkSampleMask @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX @extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX +// 157 +@extension("VK_KHR_sampler_ycbcr_conversion") @nonDispatchHandle type u64 VkSamplerYcbcrConversionKHR + +// 161 +@extension("VK_EXT_validation_cache") @nonDispatchHandle type u64 VkValidationCacheEXT ///////////// // Enums // @@ -477,6 +534,10 @@ enum VkImageLayout { //@extension("VK_KHR_shared_presentable_image") // 112 VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, + + //@extension("VK_KHR_maintenance2") // 118 + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001, } enum VkAttachmentLoadOp { @@ -960,6 +1021,42 @@ enum VkFormat { VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000, + VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006, + VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016, + VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026, + VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033, } /// Structure type enumerant @@ -1096,8 +1193,6 @@ enum VkStructureType { //@extension("VK_KHX_device_group") // 61 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000, - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002, VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003, VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004, VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005, @@ -1108,6 +1203,8 @@ enum VkStructureType { VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011, VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060013, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060014, //@extension("VK_EXT_validation_flags") // 62 VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, @@ -1183,7 +1280,7 @@ enum VkStructureType { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, //@extension("VK_EXT_display_surface_counter") // 91 - VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000, //@extension("VK_EXT_display_control") // 92 VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, @@ -1226,6 +1323,12 @@ enum VkStructureType { VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000, VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001, + //@extension("VK_KHR_maintenance2") // 118 + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000, + VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001, + VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003, + //@extension("VK_KHR_get_surface_capabilities2") // 120 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, @@ -1248,6 +1351,13 @@ enum VkStructureType { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000, VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001, + //@extension("VK_EXT_sample_locations") // 144 + VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, + VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, + VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, + VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004, + //@extension("VK_KHR_get_memory_requirements2") // 147 VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000, VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001, @@ -1255,6 +1365,9 @@ enum VkStructureType { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003, VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004, + //@extension("VK_KHR_image_format_list") // 148 + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000, + //@extension("VK_EXT_blend_operation_advanced") // 149 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, @@ -1263,8 +1376,24 @@ enum VkStructureType { //@extension("VK_NV_fragment_coverage_to_color") // 150 VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, - //@structure("VK_NV_framebuffer_mixed_samples") // 153 + //@extension("VK_NV_framebuffer_mixed_samples") // 153 VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001, + VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002, + VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005, + + //@extension("VK_KHR_bind_memory2") // 158 + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001, + + //@extension("VK_EXT_validation_cache") // 161 + VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, + VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, } enum VkSubpassContents { @@ -1343,6 +1472,9 @@ enum VkDynamicState { //@extension("VK_EXT_discard_rectangles") // 100 VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, + + //@extension("VK_EXT_sample_locations") // 144 + VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, } enum VkObjectType { @@ -1392,6 +1524,12 @@ enum VkObjectType { //@extension("VK_NVX_device_generated_commands") // 87 VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000, + + //@extension("VK_EXT_validation_cache") // 161 + VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, } @extension("VK_KHR_surface") // 1 @@ -1463,8 +1601,14 @@ enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, + //extension("VK_EXT_validation_cache") // 161 + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = 33, + //extension("VK_KHR_descriptor_update_template") // 86 VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000, } @extension("VK_AMD_rasterization_order") // 19 @@ -1541,6 +1685,18 @@ enum VkDiscardRectangleModeEXT { VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1, } +@extension("VK_KHR_maintenance2") // 118 +enum VkPointClippingBehaviorKHR { + VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0, + VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1, +} + +@extension("VK_KHR_maintenance2") // 118 +enum VkTessellationDomainOriginKHR { + VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0, + VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1, +} + @extension("VK_EXT_sampler_filter_minmax") // 131 enum VkSamplerReductionModeEXT { VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = 0, @@ -1563,6 +1719,32 @@ enum VkCoverageModulationModeNV { VK_COVERAGE_MODULATION_MODE_RGBA_NV = 3, } +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +enum VkSamplerYcbcrModelConversionKHR { + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4, +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +enum VkSamplerYcbcrRangeKHR { + VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0, + VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1, +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +enum VkChromaLocationKHR { + VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0, + VK_CHROMA_LOCATION_MIDPOINT_KHR = 1, +} + +@extension("VK_EXT_validation_cache") // 161 +enum VkValidationCacheHeaderVersionEXT { + VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1, +} + ///////////////// // Bitfields // ///////////////// @@ -1698,6 +1880,19 @@ bitfield VkImageCreateFlagBits { //@extension("VK_KHX_device_group") // 61 VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040, + + //@extension("VK_EXT_sample_locations") // 144 + VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, + + //@extension("VK_KHR_maintenance2") // 118 + VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080, + VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200, + + //@extension("VK_KHR_bind_memory2") // 158 + VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400, } /// Image view creation flags @@ -1763,6 +1958,15 @@ bitfield VkFormatFeatureFlagBits { //@extension("VK_EXT_sampler_filter_minmax") // 131 VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000, + VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000, + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000, } /// Query control flags @@ -1826,6 +2030,11 @@ bitfield VkImageAspectFlagBits { VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + + //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 + VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010, + VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020, + VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040, } /// Sparse memory bind flags @@ -2353,6 +2562,12 @@ type VkFlags VkPipelineCoverageModulationStateCreateFlagsNV //bitfield VkPipelineCoverageModulationStateCreateFlagBitsNV { //} +@extension("VK_EXT_validation_cache") // 161 +type VkFlags VkValidationCacheCreateFlagsEXT +@extension("VK_EXT_validation_cache") // 161 +//bitfield VkValidationCacheCreateFlagBitsEXT { +//} + ////////////////// // Structures // ////////////////// @@ -3901,23 +4116,17 @@ class VkMemoryAllocateFlagsInfoKHX { } @extension("VK_KHX_device_group") // 61 -class VkBindBufferMemoryInfoKHX { +class VkBindBufferMemoryDeviceGroupInfoKHX { VkStructureType sType const void* pNext - VkBuffer buffer - VkDeviceMemory memory - VkDeviceSize memoryOffset u32 deviceIndexCount const u32* pDeviceIndices } @extension("VK_KHX_device_group") // 61 -class VkBindImageMemoryInfoKHX { +class VkBindImageMemoryDeviceGroupInfoKHX { VkStructureType sType const void* pNext - VkImage image - VkDeviceMemory memory - VkDeviceSize memoryOffset u32 deviceIndexCount const u32* pDeviceIndices u32 SFRRectCount @@ -4674,6 +4883,42 @@ class VkFenceGetFdInfoKHR { VkExternalFenceHandleTypeFlagBitsKHR handleType } +@extension("VK_KHR_maintenance2") // 118 +class VkPhysicalDevicePointClippingPropertiesKHR { + VkStructureType sType + void* pNext + VkPointClippingBehaviorKHR pointClippingBehavior +} + +@extension("VK_KHR_maintenance2") // 118 +class VkInputAttachmentAspectReferenceKHR { + u32 subpass + u32 inputAttachmentIndex + VkImageAspectFlags aspectMask +} + +@extension("VK_KHR_maintenance2") // 118 +class VkRenderPassInputAttachmentAspectCreateInfoKHR { + VkStructureType sType + const void* pNext + u32 aspectReferenceCount + const VkInputAttachmentAspectReferenceKHR* pAspectReferences +} + +@extension("VK_KHR_maintenance2") // 118 +class VkImageViewUsageCreateInfoKHR { + VkStructureType sType + const void* pNext + VkImageUsageFlags usage +} + +@extension("VK_KHR_maintenance2") // 118 +class VkPipelineTessellationDomainOriginStateCreateInfoKHR { + VkStructureType sType + const void* pNext + VkTessellationDomainOriginKHR domainOrigin +} + @extension("VK_KHR_get_surface_capabilities2") // 120 class VkPhysicalDeviceSurfaceInfo2KHR { VkStructureType sType @@ -4750,6 +4995,70 @@ class VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { VkBool32 filterMinmaxImageComponentMapping } +@extension("VK_EXT_sample_locations") // 144 +class VkSampleLocationEXT { + f32 x + f32 y +} + +@extension("VK_EXT_sample_locations") // 144 +class VkSampleLocationsInfoEXT { + VkStructureType sType + const void* pNext + VkSampleCountFlagBits sampleLocationsPerPixel + VkExtent2D sampleLocationGridSize + u32 sampleLocationsCount + const VkSampleLocationEXT* pSampleLocations +} + +@extension("VK_EXT_sample_locations") // 144 +class VkAttachmentSampleLocationsEXT { + u32 attachmentIndex + VkSampleLocationsInfoEXT sampleLocationsInfo +} + +@extension("VK_EXT_sample_locations") // 144 +class VkSubpassSampleLocationsEXT { + u32 subpassIndex + VkSampleLocationsInfoEXT sampleLocationsInfo +} + +@extension("VK_EXT_sample_locations") // 144 +class VkRenderPassSampleLocationsBeginInfoEXT { + VkStructureType sType + const void* pNext + u32 attachmentInitialSampleLocationsCount + const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations + u32 postSubpassSampleLocationsCount + const VkSubpassSampleLocationsEXT* pSubpassSampleLocations +} + +@extension("VK_EXT_sample_locations") // 144 +class VkPipelineSampleLocationsStateCreateInfoEXT { + VkStructureType sType + const void* pNext + VkBool32 sampleLocationsEnable + VkSampleLocationsInfoEXT sampleLocationsInfo +} + +@extension("VK_EXT_sample_locations") // 144 +class VkPhysicalDeviceSampleLocationsPropertiesEXT { + VkStructureType sType + void* pNext + VkSampleCountFlags sampleLocationSampleCounts + VkExtent2D maxSampleLocationGridSize + f32[2] sampleLocationCoordinateRange + u32 sampleLocationSubPixelBits + VkBool32 variableSampleLocations +} + +@extension("VK_EXT_sample_locations") // 144 +class VkMultisamplePropertiesEXT { + VkStructureType sType + void* pNext + VkExtent2D maxSampleLocationGridSize +} + @extension("VK_KHR_get_memory_requirements2") // 147 class VkBufferMemoryRequirementsInfo2KHR { VkStructureType sType @@ -4785,6 +5094,14 @@ class VkSparseImageMemoryRequirements2KHR { VkSparseImageMemoryRequirements memoryRequirements } +@extension("VK_KHR_image_format_list") // 148 +class VkImageFormatListCreateInfoKHR { + VkStructureType sType + const void* pNext + u32 viewFormatCount + const VkFormat* pViewFormats +} + @extension("VK_EXT_blend_operation_advanced") // 149 class VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT { VkStructureType sType @@ -4833,6 +5150,89 @@ class VkPipelineCoverageModulationStateCreateInfoNV { const f32* pCoverageModulationTable } +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkSamplerYcbcrConversionCreateInfoKHR { + VkStructureType sType + const void* pNext + VkFormat format + VkSamplerYcbcrModelConversionKHR ycbcrModel + VkSamplerYcbcrRangeKHR ycbcrRange + VkComponentMapping components + VkChromaLocationKHR xChromaOffset + VkChromaLocationKHR yChromaOffset + VkFilter chromaFilter + VkBool32 forceExplicitReconstruction +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkSamplerYcbcrConversionInfoKHR { + VkStructureType sType + const void* pNext + VkSamplerYcbcrConversionKHR conversion +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkBindImagePlaneMemoryInfoKHR { + VkStructureType sType + const void* pNext + VkImageAspectFlagBits planeAspect +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkImagePlaneMemoryRequirementsInfoKHR { + VkStructureType sType + const void* pNext + VkImageAspectFlagBits planeAspect +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR { + VkStructureType sType + void* pNext + VkBool32 samplerYcbcrConversion +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +class VkSamplerYcbcrConversionImageFormatPropertiesKHR { + VkStructureType sType + void* pNext + u32 combinedImageSamplerDescriptorCount +} + +@extension("VK_KHR_bind_memory2") // 158 +class VkBindBufferMemoryInfoKHR { + VkStructureType sType + const void* pNext + VkBuffer buffer + VkDeviceMemory memory + VkDeviceSize memoryOffset +} + +@extension("VK_KHR_bind_memory2") // 158 +class VkBindImageMemoryInfoKHR { + VkStructureType sType + const void* pNext + VkImage image + VkDeviceMemory memory + VkDeviceSize memoryOffset +} + +@extension("VK_EXT_validation_cache") // 161 +class VkValidationCacheCreateInfoEXT { + VkStructureType sType + const void* pNext + VkValidationCacheCreateFlagsEXT flags + platform.size_t initialDataSize + const void* pInitialData +} + +@extension("VK_EXT_validation_cache") // 161 +class VkShaderModuleValidationCacheCreateInfoEXT { + VkStructureType sType + const void* pNext + VkValidationCacheEXT validationCache +} + //////////////// // Commands // //////////////// @@ -7454,21 +7854,21 @@ cmd void vkDebugReportMessageEXT( @extension("VK_EXT_debug_marker") // 23 cmd VkResult vkDebugMarkerSetObjectTagEXT( VkDevice device, - VkDebugMarkerObjectTagInfoEXT* pTagInfo) { + const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { return ? } @extension("VK_EXT_debug_marker") // 23 cmd VkResult vkDebugMarkerSetObjectNameEXT( VkDevice device, - VkDebugMarkerObjectNameInfoEXT* pNameInfo) { + const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { return ? } @extension("VK_EXT_debug_marker") // 23 cmd void vkCmdDebugMarkerBeginEXT( VkCommandBuffer commandBuffer, - VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { } @extension("VK_EXT_debug_marker") // 23 @@ -7479,7 +7879,7 @@ cmd void vkCmdDebugMarkerEndEXT( @extension("VK_EXT_debug_marker") // 23 cmd void vkCmdDebugMarkerInsertEXT( VkCommandBuffer commandBuffer, - VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { } @extension("VK_AMD_draw_indirect_count") // 34 @@ -7584,22 +7984,6 @@ cmd void vkGetDeviceGroupPeerMemoryFeaturesKHX( } @extension("VK_KHX_device_group") // 61 -cmd VkResult vkBindBufferMemory2KHX( - VkDevice device, - u32 bindInfoCount, - const VkBindBufferMemoryInfoKHX* pBindInfos) { - return ? -} - -@extension("VK_KHX_device_group") // 61 -cmd VkResult vkBindImageMemory2KHX( - VkDevice device, - u32 bindInfoCount, - const VkBindImageMemoryInfoKHX* pBindInfos) { - return ? -} - -@extension("VK_KHX_device_group") // 61 cmd void vkCmdSetDeviceMaskKHX( VkCommandBuffer commandBuffer, u32 deviceMask) { @@ -8058,6 +8442,19 @@ cmd VkResult vkCreateMacOSSurfaceMVK( return ? } +@extension("VK_EXT_sample_locations") // 144 +cmd void vkCmdSetSampleLocationsEXT( + VkCommandBuffer commandBuffer, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo) { +} + +@extension("VK_EXT_sample_locations") // 144 +cmd void vkGetPhysicalDeviceMultisamplePropertiesEXT( + VkPhysicalDevice physicalDevice, + VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties) { +} + @extension("VK_KHR_get_memory_requirements2") // 147 cmd void vkGetImageMemoryRequirements2KHR( VkDevice device, @@ -8080,6 +8477,72 @@ cmd void vkGetImageSparseMemoryRequirements2KHR( VkSparseImageMemoryRequirements2KHR* pSparseMemoryRequirements) { } +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +cmd VkResult vkCreateSamplerYcbcrConversionKHR( + VkDevice device, + const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSamplerYcbcrConversionKHR* pYcbcrConversion) { + return ? +} + +@extension("VK_KHR_sampler_ycbcr_conversion") // 157 +cmd void vkDestroySamplerYcbcrConversionKHR( + VkDevice device, + VkSamplerYcbcrConversionKHR ycbcrConversion, + const VkAllocationCallbacks* pAllocator) { +} + +@extension("VK_KHR_bind_memory2") // 158 +cmd VkResult vkBindBufferMemory2KHR( + VkDevice device, + u32 bindInfoCount, + const VkBindBufferMemoryInfoKHR* pBindInfos) { + return ? +} + +@extension("VK_KHR_bind_memory2") // 158 +cmd VkResult vkBindImageMemory2KHR( + VkDevice device, + u32 bindInfoCount, + const VkBindImageMemoryInfoKHR* pBindInfos) { + return ? +} + +@extension("VK_EXT_validation_cache") // 161 +cmd VkResult vkCreateValidationCacheEXT( + VkDevice device, + const VkValidationCacheCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkValidationCacheEXT* pValidationCache) { + return ? +} + +@extension("VK_EXT_validation_cache") // 161 +cmd void vkDestroyValidationCacheEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + const VkAllocationCallbacks* pAllocator) { +} + +@extension("VK_EXT_validation_cache") // 161 +cmd VkResult vkMergeValidationCachesEXT( + VkDevice device, + VkValidationCacheEXT dstCache, + u32 srcCacheCount, + const VkValidationCacheEXT* pSrcCaches) { + return ? +} + +@extension("VK_EXT_validation_cache") // 161 +cmd VkResult vkGetValidationCacheDataEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + platform.size_t* pDataSize, + void* pData) { + return ? +} + //////////////// // Validation // //////////////// diff --git a/vulkan/include/vulkan/vulkan.h b/vulkan/include/vulkan/vulkan.h index 0271d38415..7813e4b394 100644 --- a/vulkan/include/vulkan/vulkan.h +++ b/vulkan/include/vulkan/vulkan.h @@ -34,16 +34,16 @@ extern "C" { (((major) << 22) | ((minor) << 12) | (patch)) // DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. -//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) +//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 // Vulkan 1.0 version number -#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0) +#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)// Patch version should always be set to 0 #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) // Version of this file -#define VK_HEADER_VERSION 54 +#define VK_HEADER_VERSION 61 #define VK_NULL_HANDLE 0 @@ -241,16 +241,16 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008, VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHX = 1000060000, - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHX = 1000060001, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHX = 1000060002, VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHX = 1000060003, VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHX = 1000060004, VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHX = 1000060005, VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHX = 1000060006, + VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060013, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHX = 1000060014, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHX = 1000060007, VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHX = 1000060008, VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHX = 1000060009, - VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHX = 1000060010, VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHX = 1000060011, VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHX = 1000060012, VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, @@ -293,7 +293,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, - VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = 1000090000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000, VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001, VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002, @@ -313,6 +313,10 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000, VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000, + VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001, + VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002, @@ -323,16 +327,32 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = 1000127001, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000, VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001, + VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, + VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, + VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, + VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004, VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000, VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001, VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146002, VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003, VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001, + VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002, + VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001, + VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, + VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), @@ -553,6 +573,40 @@ typedef enum VkFormat { VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000, + VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006, + VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016, + VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026, + VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033, VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), @@ -621,6 +675,8 @@ typedef enum VkImageLayout { VK_IMAGE_LAYOUT_PREINITIALIZED = 8, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001, VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1), @@ -851,6 +907,7 @@ typedef enum VkDynamicState { VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, + VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1), @@ -1009,6 +1066,8 @@ typedef enum VkObjectType { VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = 1000085000, VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000, + VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN, VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL, VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1), @@ -1035,6 +1094,13 @@ typedef enum VkFormatFeatureFlagBits { VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000, VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000, + VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000, + VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000, + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000, VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkFormatFeatureFlagBits; typedef VkFlags VkFormatFeatureFlags; @@ -1060,6 +1126,11 @@ typedef enum VkImageCreateFlagBits { VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, VK_IMAGE_CREATE_BIND_SFR_BIT_KHX = 0x00000040, VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020, + VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080, + VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100, + VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, + VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200, + VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400, VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageCreateFlagBits; typedef VkFlags VkImageCreateFlags; @@ -1133,6 +1204,9 @@ typedef enum VkImageAspectFlagBits { VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010, + VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020, + VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040, VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkImageAspectFlagBits; typedef VkFlags VkImageAspectFlags; @@ -1366,6 +1440,27 @@ typedef enum VkStencilFaceFlagBits { } VkStencilFaceFlagBits; typedef VkFlags VkStencilFaceFlags; +typedef struct VkApplicationInfo { + VkStructureType sType; + const void* pNext; + const char* pApplicationName; + uint32_t applicationVersion; + const char* pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; +} VkApplicationInfo; + +typedef struct VkInstanceCreateInfo { + VkStructureType sType; + const void* pNext; + VkInstanceCreateFlags flags; + const VkApplicationInfo* pApplicationInfo; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; +} VkInstanceCreateInfo; + typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( void* pUserData, size_t size, @@ -1395,29 +1490,6 @@ typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope); -typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); - -typedef struct VkApplicationInfo { - VkStructureType sType; - const void* pNext; - const char* pApplicationName; - uint32_t applicationVersion; - const char* pEngineName; - uint32_t engineVersion; - uint32_t apiVersion; -} VkApplicationInfo; - -typedef struct VkInstanceCreateInfo { - VkStructureType sType; - const void* pNext; - VkInstanceCreateFlags flags; - const VkApplicationInfo* pApplicationInfo; - uint32_t enabledLayerCount; - const char* const* ppEnabledLayerNames; - uint32_t enabledExtensionCount; - const char* const* ppEnabledExtensionNames; -} VkInstanceCreateInfo; - typedef struct VkAllocationCallbacks { void* pUserData; PFN_vkAllocationFunction pfnAllocation; @@ -1658,6 +1730,7 @@ typedef struct VkPhysicalDeviceMemoryProperties { VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; } VkPhysicalDeviceMemoryProperties; +typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); typedef struct VkDeviceQueueCreateInfo { VkStructureType sType; const void* pNext; @@ -4774,6 +4847,62 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceFdKHR( int* pFd); #endif +#define VK_KHR_maintenance2 1 +#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" + + +typedef enum VkPointClippingBehaviorKHR { + VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0, + VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1, + VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR, + VK_POINT_CLIPPING_BEHAVIOR_END_RANGE_KHR = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR, + VK_POINT_CLIPPING_BEHAVIOR_RANGE_SIZE_KHR = (VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR + 1), + VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPointClippingBehaviorKHR; + +typedef enum VkTessellationDomainOriginKHR { + VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0, + VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1, + VK_TESSELLATION_DOMAIN_ORIGIN_BEGIN_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR, + VK_TESSELLATION_DOMAIN_ORIGIN_END_RANGE_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR, + VK_TESSELLATION_DOMAIN_ORIGIN_RANGE_SIZE_KHR = (VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR + 1), + VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM_KHR = 0x7FFFFFFF +} VkTessellationDomainOriginKHR; + +typedef struct VkPhysicalDevicePointClippingPropertiesKHR { + VkStructureType sType; + void* pNext; + VkPointClippingBehaviorKHR pointClippingBehavior; +} VkPhysicalDevicePointClippingPropertiesKHR; + +typedef struct VkInputAttachmentAspectReferenceKHR { + uint32_t subpass; + uint32_t inputAttachmentIndex; + VkImageAspectFlags aspectMask; +} VkInputAttachmentAspectReferenceKHR; + +typedef struct VkRenderPassInputAttachmentAspectCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t aspectReferenceCount; + const VkInputAttachmentAspectReferenceKHR* pAspectReferences; +} VkRenderPassInputAttachmentAspectCreateInfoKHR; + +typedef struct VkImageViewUsageCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageUsageFlags usage; +} VkImageViewUsageCreateInfoKHR; + +typedef struct VkPipelineTessellationDomainOriginStateCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkTessellationDomainOriginKHR domainOrigin; +} VkPipelineTessellationDomainOriginStateCreateInfoKHR; + + + #define VK_KHR_get_surface_capabilities2 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 #define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" @@ -4827,7 +4956,7 @@ typedef struct VkPhysicalDeviceVariablePointerFeaturesKHR { #define VK_KHR_dedicated_allocation 1 -#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 1 +#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 #define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" typedef struct VkMemoryDedicatedRequirementsKHR { @@ -4851,6 +4980,11 @@ typedef struct VkMemoryDedicatedAllocateInfoKHR { #define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class" +#define VK_KHR_relaxed_block_layout 1 +#define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1 +#define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout" + + #define VK_KHR_get_memory_requirements2 1 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1 #define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" @@ -4908,6 +5042,152 @@ VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements2KHR( VkSparseImageMemoryRequirements2KHR* pSparseMemoryRequirements); #endif +#define VK_KHR_image_format_list 1 +#define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1 +#define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list" + +typedef struct VkImageFormatListCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t viewFormatCount; + const VkFormat* pViewFormats; +} VkImageFormatListCreateInfoKHR; + + + +#define VK_KHR_sampler_ycbcr_conversion 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversionKHR) + +#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1 +#define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" + + +typedef enum VkSamplerYcbcrModelConversionKHR { + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_END_RANGE_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR + 1), + VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSamplerYcbcrModelConversionKHR; + +typedef enum VkSamplerYcbcrRangeKHR { + VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0, + VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1, + VK_SAMPLER_YCBCR_RANGE_BEGIN_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR, + VK_SAMPLER_YCBCR_RANGE_END_RANGE_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR, + VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE_KHR = (VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR - VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR + 1), + VK_SAMPLER_YCBCR_RANGE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSamplerYcbcrRangeKHR; + +typedef enum VkChromaLocationKHR { + VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0, + VK_CHROMA_LOCATION_MIDPOINT_KHR = 1, + VK_CHROMA_LOCATION_BEGIN_RANGE_KHR = VK_CHROMA_LOCATION_COSITED_EVEN_KHR, + VK_CHROMA_LOCATION_END_RANGE_KHR = VK_CHROMA_LOCATION_MIDPOINT_KHR, + VK_CHROMA_LOCATION_RANGE_SIZE_KHR = (VK_CHROMA_LOCATION_MIDPOINT_KHR - VK_CHROMA_LOCATION_COSITED_EVEN_KHR + 1), + VK_CHROMA_LOCATION_MAX_ENUM_KHR = 0x7FFFFFFF +} VkChromaLocationKHR; + +typedef struct VkSamplerYcbcrConversionCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkSamplerYcbcrModelConversionKHR ycbcrModel; + VkSamplerYcbcrRangeKHR ycbcrRange; + VkComponentMapping components; + VkChromaLocationKHR xChromaOffset; + VkChromaLocationKHR yChromaOffset; + VkFilter chromaFilter; + VkBool32 forceExplicitReconstruction; +} VkSamplerYcbcrConversionCreateInfoKHR; + +typedef struct VkSamplerYcbcrConversionInfoKHR { + VkStructureType sType; + const void* pNext; + VkSamplerYcbcrConversionKHR conversion; +} VkSamplerYcbcrConversionInfoKHR; + +typedef struct VkBindImagePlaneMemoryInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageAspectFlagBits planeAspect; +} VkBindImagePlaneMemoryInfoKHR; + +typedef struct VkImagePlaneMemoryRequirementsInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageAspectFlagBits planeAspect; +} VkImagePlaneMemoryRequirementsInfoKHR; + +typedef struct VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 samplerYcbcrConversion; +} VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR; + +typedef struct VkSamplerYcbcrConversionImageFormatPropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t combinedImageSamplerDescriptorCount; +} VkSamplerYcbcrConversionImageFormatPropertiesKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversionKHR)(VkDevice device, const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversionKHR* pYcbcrConversion); +typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversionKHR)(VkDevice device, VkSamplerYcbcrConversionKHR ycbcrConversion, const VkAllocationCallbacks* pAllocator); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversionKHR( + VkDevice device, + const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSamplerYcbcrConversionKHR* pYcbcrConversion); + +VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversionKHR( + VkDevice device, + VkSamplerYcbcrConversionKHR ycbcrConversion, + const VkAllocationCallbacks* pAllocator); +#endif + +#define VK_KHR_bind_memory2 1 +#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1 +#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2" + +typedef struct VkBindBufferMemoryInfoKHR { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; +} VkBindBufferMemoryInfoKHR; + +typedef struct VkBindImageMemoryInfoKHR { + VkStructureType sType; + const void* pNext; + VkImage image; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; +} VkBindImageMemoryInfoKHR; + + +typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHR* pBindInfos); +typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHR( + VkDevice device, + uint32_t bindInfoCount, + const VkBindBufferMemoryInfoKHR* pBindInfos); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR( + VkDevice device, + uint32_t bindInfoCount, + const VkBindImageMemoryInfoKHR* pBindInfos); +#endif + #define VK_EXT_debug_report 1 VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) @@ -4951,10 +5231,12 @@ typedef enum VkDebugReportObjectTypeEXT { VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = 33, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000, VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT, - VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), + VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF } VkDebugReportObjectTypeEXT; @@ -5021,6 +5303,11 @@ VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( #define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" +#define VK_EXT_depth_range_unrestricted 1 +#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1 +#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME "VK_EXT_depth_range_unrestricted" + + #define VK_IMG_filter_cubic 1 #define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 #define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" @@ -5088,31 +5375,31 @@ typedef struct VkDebugMarkerMarkerInfoEXT { } VkDebugMarkerMarkerInfoEXT; -typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, VkDebugMarkerObjectTagInfoEXT* pTagInfo); -typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, VkDebugMarkerObjectNameInfoEXT* pNameInfo); -typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo); +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer); -typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT( VkDevice device, - VkDebugMarkerObjectTagInfoEXT* pTagInfo); + const VkDebugMarkerObjectTagInfoEXT* pTagInfo); VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT( VkDevice device, - VkDebugMarkerObjectNameInfoEXT* pNameInfo); + const VkDebugMarkerObjectNameInfoEXT* pNameInfo); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT( VkCommandBuffer commandBuffer, - VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT( VkCommandBuffer commandBuffer); VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( VkCommandBuffer commandBuffer, - VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); #endif #define VK_AMD_gcn_shader 1 @@ -5350,9 +5637,9 @@ typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { #endif /* VK_USE_PLATFORM_WIN32_KHR */ #define VK_KHX_device_group 1 -#define VK_MAX_DEVICE_GROUP_SIZE_KHX 32 -#define VK_KHX_DEVICE_GROUP_SPEC_VERSION 1 +#define VK_KHX_DEVICE_GROUP_SPEC_VERSION 2 #define VK_KHX_DEVICE_GROUP_EXTENSION_NAME "VK_KHX_device_group" +#define VK_MAX_DEVICE_GROUP_SIZE_KHX 32 typedef enum VkPeerMemoryFeatureFlagBitsKHX { @@ -5386,28 +5673,6 @@ typedef struct VkMemoryAllocateFlagsInfoKHX { uint32_t deviceMask; } VkMemoryAllocateFlagsInfoKHX; -typedef struct VkBindBufferMemoryInfoKHX { - VkStructureType sType; - const void* pNext; - VkBuffer buffer; - VkDeviceMemory memory; - VkDeviceSize memoryOffset; - uint32_t deviceIndexCount; - const uint32_t* pDeviceIndices; -} VkBindBufferMemoryInfoKHX; - -typedef struct VkBindImageMemoryInfoKHX { - VkStructureType sType; - const void* pNext; - VkImage image; - VkDeviceMemory memory; - VkDeviceSize memoryOffset; - uint32_t deviceIndexCount; - const uint32_t* pDeviceIndices; - uint32_t SFRRectCount; - const VkRect2D* pSFRRects; -} VkBindImageMemoryInfoKHX; - typedef struct VkDeviceGroupRenderPassBeginInfoKHX { VkStructureType sType; const void* pNext; @@ -5440,6 +5705,22 @@ typedef struct VkDeviceGroupBindSparseInfoKHX { uint32_t memoryDeviceIndex; } VkDeviceGroupBindSparseInfoKHX; +typedef struct VkBindBufferMemoryDeviceGroupInfoKHX { + VkStructureType sType; + const void* pNext; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; +} VkBindBufferMemoryDeviceGroupInfoKHX; + +typedef struct VkBindImageMemoryDeviceGroupInfoKHX { + VkStructureType sType; + const void* pNext; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; + uint32_t SFRRectCount; + const VkRect2D* pSFRRects; +} VkBindImageMemoryDeviceGroupInfoKHX; + typedef struct VkDeviceGroupPresentCapabilitiesKHX { VkStructureType sType; const void* pNext; @@ -5486,14 +5767,12 @@ typedef struct VkDeviceGroupSwapchainCreateInfoKHX { typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHX)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures); -typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfoKHX* pBindInfos); -typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHX)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHX* pBindInfos); typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHX)(VkCommandBuffer commandBuffer, uint32_t deviceMask); +typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHX)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities); typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHX)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHX* pModes); -typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex); -typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHX)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHX)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHX)(VkDevice device, const VkAcquireNextImageInfoKHX* pAcquireInfo, uint32_t* pImageIndex); #ifndef VK_NO_PROTOTYPES VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX( @@ -5503,34 +5782,10 @@ VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHX( uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlagsKHX* pPeerMemoryFeatures); -VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHX( - VkDevice device, - uint32_t bindInfoCount, - const VkBindBufferMemoryInfoKHX* pBindInfos); - -VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHX( - VkDevice device, - uint32_t bindInfoCount, - const VkBindImageMemoryInfoKHX* pBindInfos); - VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHX( VkCommandBuffer commandBuffer, uint32_t deviceMask); -VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX( - VkDevice device, - VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities); - -VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX( - VkDevice device, - VkSurfaceKHR surface, - VkDeviceGroupPresentModeFlagsKHX* pModes); - -VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX( - VkDevice device, - const VkAcquireNextImageInfoKHX* pAcquireInfo, - uint32_t* pImageIndex); - VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX( VkCommandBuffer commandBuffer, uint32_t baseGroupX, @@ -5540,11 +5795,25 @@ VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHX( uint32_t groupCountY, uint32_t groupCountZ); +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHX( + VkDevice device, + VkDeviceGroupPresentCapabilitiesKHX* pDeviceGroupPresentCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHX( + VkDevice device, + VkSurfaceKHR surface, + VkDeviceGroupPresentModeFlagsKHX* pModes); + VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHX( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHX( + VkDevice device, + const VkAcquireNextImageInfoKHX* pAcquireInfo, + uint32_t* pImageIndex); #endif #define VK_EXT_validation_flags 1 @@ -5639,7 +5908,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHX( VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) -#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 1 +#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 #define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" @@ -5929,6 +6198,7 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT( #define VK_EXT_display_surface_counter 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 #define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" +#define VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT typedef enum VkSurfaceCounterFlagBitsEXT { @@ -6328,6 +6598,96 @@ typedef struct VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { #define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16" +#define VK_AMD_mixed_attachment_samples 1 +#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1 +#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples" + + +#define VK_AMD_shader_fragment_mask 1 +#define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1 +#define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask" + + +#define VK_EXT_shader_stencil_export 1 +#define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1 +#define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export" + + +#define VK_EXT_sample_locations 1 +#define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1 +#define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations" + +typedef struct VkSampleLocationEXT { + float x; + float y; +} VkSampleLocationEXT; + +typedef struct VkSampleLocationsInfoEXT { + VkStructureType sType; + const void* pNext; + VkSampleCountFlagBits sampleLocationsPerPixel; + VkExtent2D sampleLocationGridSize; + uint32_t sampleLocationsCount; + const VkSampleLocationEXT* pSampleLocations; +} VkSampleLocationsInfoEXT; + +typedef struct VkAttachmentSampleLocationsEXT { + uint32_t attachmentIndex; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkAttachmentSampleLocationsEXT; + +typedef struct VkSubpassSampleLocationsEXT { + uint32_t subpassIndex; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkSubpassSampleLocationsEXT; + +typedef struct VkRenderPassSampleLocationsBeginInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t attachmentInitialSampleLocationsCount; + const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations; + uint32_t postSubpassSampleLocationsCount; + const VkSubpassSampleLocationsEXT* pSubpassSampleLocations; +} VkRenderPassSampleLocationsBeginInfoEXT; + +typedef struct VkPipelineSampleLocationsStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 sampleLocationsEnable; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkPipelineSampleLocationsStateCreateInfoEXT; + +typedef struct VkPhysicalDeviceSampleLocationsPropertiesEXT { + VkStructureType sType; + void* pNext; + VkSampleCountFlags sampleLocationSampleCounts; + VkExtent2D maxSampleLocationGridSize; + float sampleLocationCoordinateRange[2]; + uint32_t sampleLocationSubPixelBits; + VkBool32 variableSampleLocations; +} VkPhysicalDeviceSampleLocationsPropertiesEXT; + +typedef struct VkMultisamplePropertiesEXT { + VkStructureType sType; + void* pNext; + VkExtent2D maxSampleLocationGridSize; +} VkMultisamplePropertiesEXT; + + +typedef void (VKAPI_PTR *PFN_vkCmdSetSampleLocationsEXT)(VkCommandBuffer commandBuffer, const VkSampleLocationsInfoEXT* pSampleLocationsInfo); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)(VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetSampleLocationsEXT( + VkCommandBuffer commandBuffer, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMultisamplePropertiesEXT( + VkPhysicalDevice physicalDevice, + VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties); +#endif + #define VK_EXT_blend_operation_advanced 1 #define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2 #define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced" @@ -6421,6 +6781,78 @@ typedef struct VkPipelineCoverageModulationStateCreateInfoNV { #define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle" +#define VK_EXT_post_depth_coverage 1 +#define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1 +#define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage" + + +#define VK_EXT_validation_cache 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT) + +#define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache" + + +typedef enum VkValidationCacheHeaderVersionEXT { + VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1, + VK_VALIDATION_CACHE_HEADER_VERSION_BEGIN_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, + VK_VALIDATION_CACHE_HEADER_VERSION_END_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, + VK_VALIDATION_CACHE_HEADER_VERSION_RANGE_SIZE_EXT = (VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT + 1), + VK_VALIDATION_CACHE_HEADER_VERSION_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationCacheHeaderVersionEXT; + +typedef VkFlags VkValidationCacheCreateFlagsEXT; + +typedef struct VkValidationCacheCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkValidationCacheCreateFlagsEXT flags; + size_t initialDataSize; + const void* pInitialData; +} VkValidationCacheCreateInfoEXT; + +typedef struct VkShaderModuleValidationCacheCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkValidationCacheEXT validationCache; +} VkShaderModuleValidationCacheCreateInfoEXT; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateValidationCacheEXT)(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkValidationCacheEXT* pValidationCache); +typedef void (VKAPI_PTR *PFN_vkDestroyValidationCacheEXT)(VkDevice device, VkValidationCacheEXT validationCache, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkMergeValidationCachesEXT)(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, const VkValidationCacheEXT* pSrcCaches); +typedef VkResult (VKAPI_PTR *PFN_vkGetValidationCacheDataEXT)(VkDevice device, VkValidationCacheEXT validationCache, size_t* pDataSize, void* pData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateValidationCacheEXT( + VkDevice device, + const VkValidationCacheCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkValidationCacheEXT* pValidationCache); + +VKAPI_ATTR void VKAPI_CALL vkDestroyValidationCacheEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkMergeValidationCachesEXT( + VkDevice device, + VkValidationCacheEXT dstCache, + uint32_t srcCacheCount, + const VkValidationCacheEXT* pSrcCaches); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetValidationCacheDataEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + size_t* pDataSize, + void* pData); +#endif + +#define VK_EXT_shader_viewport_index_layer 1 +#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1 +#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer" + + #ifdef __cplusplus } #endif diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index 001eaf12f5..8dd55f458f 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -467,6 +467,7 @@ VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pNa "vkGetPhysicalDeviceImageFormatProperties2KHR", "vkGetPhysicalDeviceMemoryProperties", "vkGetPhysicalDeviceMemoryProperties2KHR", + "vkGetPhysicalDeviceMultisamplePropertiesEXT", "vkGetPhysicalDevicePresentRectanglesKHX", "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceProperties2KHR", diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index a3da651142..b4ca42c009 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -343,9 +343,14 @@ void DestroyInstance(VkInstance instance, VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* physical_device_count, VkPhysicalDevice* physical_devices) { - if (physical_devices && *physical_device_count >= 1) + if (!physical_devices) + *physical_device_count = 1; + else if (*physical_device_count == 0) + return VK_INCOMPLETE; + else { physical_devices[0] = &instance->physical_device; - *physical_device_count = 1; + *physical_device_count = 1; + } return VK_SUCCESS; } |