diff options
252 files changed, 3750 insertions, 8390 deletions
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index a7bc018ff6..c787113765 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -141,6 +141,7 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_ONBOARDING && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) { MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 484231225d..615701ccc1 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -207,9 +207,7 @@ std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; int PropertiesHelper::unroot_ = -1; int PropertiesHelper::parallel_run_ = -1; -#if !defined(__ANDROID_VNDK__) int PropertiesHelper::strict_run_ = -1; -#endif bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { @@ -240,7 +238,6 @@ bool PropertiesHelper::IsParallelRun() { return parallel_run_ == 1; } -#if !defined(__ANDROID_VNDK__) bool PropertiesHelper::IsStrictRun() { if (strict_run_ == -1) { // Defaults to using stricter timeouts. @@ -248,7 +245,6 @@ bool PropertiesHelper::IsStrictRun() { } return strict_run_ == 1; } -#endif int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 6049e3e19d..9e955e3c04 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -198,18 +198,14 @@ class PropertiesHelper { * will default to true. This results in shortened timeouts for flaky * sections. */ -#if !defined(__ANDROID_VNDK__) static bool IsStrictRun(); -#endif private: static std::string build_type_; static int dry_run_; static int unroot_; static int parallel_run_; -#if !defined(__ANDROID_VNDK__) static int strict_run_; -#endif }; /* diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 0dc8f5ad64..fa9bcf351c 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -49,6 +49,9 @@ interface IDumpstate { // Default mode. const int BUGREPORT_MODE_DEFAULT = 6; + // Bugreport taken for onboarding related flows. + const int BUGREPORT_MODE_ONBOARDING = 7; + // Use pre-dumped data. const int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 0x1; diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index e132b35996..376d57a624 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2176,6 +2176,11 @@ static void DumpstateWifiOnly() { printf("========================================================\n"); } +// Collects a lightweight dumpstate to be used for debugging onboarding related flows. +static void DumpstateOnboardingOnly() { + ds.AddDir(LOGPERSIST_DATA_DIR, false); +} + Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { const std::string temp_file_pattern = ds.bugreport_internal_dir_ + "/dumptrace_XXXXXX"; const size_t buf_size = temp_file_pattern.length() + 1; @@ -2308,6 +2313,7 @@ static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl( return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return dumpstate_hal_hidl::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return dumpstate_hal_hidl::DumpstateMode::DEFAULT; } @@ -2329,6 +2335,7 @@ static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAi return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; } @@ -2812,6 +2819,8 @@ static inline const char* ModeToString(Dumpstate::BugreportMode mode) { return "BUGREPORT_TELEPHONY"; case Dumpstate::BugreportMode::BUGREPORT_WIFI: return "BUGREPORT_WIFI"; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: + return "BUGREPORT_ONBOARDING"; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: return "BUGREPORT_DEFAULT"; } @@ -2857,6 +2866,10 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt options->wifi_only = true; options->do_screenshot = false; break; + case Dumpstate::BugreportMode::BUGREPORT_ONBOARDING: + options->onboarding_only = true; + options->do_screenshot = false; + break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; } @@ -3276,6 +3289,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, DumpstateWifiOnly(); } else if (options_->limited_only) { DumpstateLimitedOnly(); + } else if (options_->onboarding_only) { + DumpstateOnboardingOnly(); } else { // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefaultAfterCritical(); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 8a31c314d9..0a032ecfc3 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -201,6 +201,7 @@ class Dumpstate { BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR, BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY, BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI, + BUGREPORT_ONBOARDING = android::os::IDumpstate::BUGREPORT_MODE_ONBOARDING, BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT }; @@ -412,6 +413,7 @@ class Dumpstate { bool show_header_only = false; bool telephony_only = false; bool wifi_only = false; + bool onboarding_only = false; // Trimmed-down version of dumpstate to only include whitelisted logs. bool limited_only = false; // Whether progress updates should be published. diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b302f52f3e..e2a2927f2b 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -19,6 +19,7 @@ #include <errno.h> #include <fts.h> #include <inttypes.h> +#include <linux/fsverity.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -51,6 +52,7 @@ #include <android-base/unique_fd.h> #include <cutils/ashmem.h> #include <cutils/fs.h> +#include <cutils/misc.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> #include <linux/quota.h> @@ -84,6 +86,8 @@ using android::base::ParseUint; using android::base::Split; using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::endl; namespace android { @@ -229,6 +233,14 @@ binder::Status checkArgumentFileName(const std::string& path) { return ok(); } +binder::Status checkUidInAppRange(int32_t appUid) { + if (FIRST_APPLICATION_UID <= appUid && appUid <= LAST_APPLICATION_UID) { + return ok(); + } + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("UID %d is outside of the range", appUid)); +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -283,6 +295,14 @@ binder::Status checkArgumentFileName(const std::string& path) { } \ } +#define CHECK_ARGUMENT_UID_IN_APP_RANGE(uid) \ + { \ + binder::Status status = checkUidInAppRange((uid)); \ + if (!status.isOk()) { \ + return status; \ + } \ + } + #ifdef GRANULAR_LOCKS /** @@ -383,6 +403,33 @@ using PackageLockGuard = std::lock_guard<PackageLock>; } // namespace +binder::Status InstalldNativeService::FsveritySetupAuthToken::authenticate( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId) { + int open_flags = fcntl(authFd.get(), F_GETFL); + if (open_flags < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fcntl failed"); + } + if ((open_flags & O_ACCMODE) != O_WRONLY && (open_flags & O_ACCMODE) != O_RDWR) { + return exception(binder::Status::EX_SECURITY, "Received FD with unexpected open flag"); + } + if (fstat(authFd.get(), &this->mStatFromAuthFd) < 0) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "fstat failed"); + } + if (!S_ISREG(this->mStatFromAuthFd.st_mode)) { + return exception(binder::Status::EX_SECURITY, "Not a regular file"); + } + // Don't accept a file owned by a different app. + uid_t uid = multiuser_get_uid(userId, appUid); + if (this->mStatFromAuthFd.st_uid != uid) { + return exception(binder::Status::EX_SERVICE_SPECIFIC, "File not owned by appUid"); + } + return ok(); +} + +bool InstalldNativeService::FsveritySetupAuthToken::isSameStat(const struct stat& st) const { + return memcmp(&st, &mStatFromAuthFd, sizeof(st)) == 0; +} + status_t InstalldNativeService::start() { IPCThreadState::self()->disableBackgroundScheduling(true); status_t ret = BinderService<InstalldNativeService>::publish(); @@ -3857,5 +3904,84 @@ binder::Status InstalldNativeService::getOdexVisibility( return *_aidl_return == -1 ? error() : ok(); } +// Creates an auth token to be used in enableFsverity. This token is really to store a proof that +// the caller can write to a file, represented by the authFd. Effectively, system_server as the +// attacker-in-the-middle cannot enable fs-verity on arbitrary app files. If the FD is not writable, +// return null. +// +// appUid and userId are passed for additional ownership check, such that one app can not be +// authenticated for another app's file. These parameters are assumed trusted for this purpose of +// consistency check. +// +// Notably, creating the token allows us to manage the writable FD easily during enableFsverity. +// Since enabling fs-verity to a file requires no outstanding writable FD, passing the authFd to the +// server allows the server to hold the only reference (as long as the client app doesn't). +binder::Status InstalldNativeService::createFsveritySetupAuthToken( + const ParcelFileDescriptor& authFd, int32_t appUid, int32_t userId, + sp<IFsveritySetupAuthToken>* _aidl_return) { + CHECK_ARGUMENT_UID_IN_APP_RANGE(appUid); + ENFORCE_VALID_USER(userId); + + auto token = sp<FsveritySetupAuthToken>::make(); + binder::Status status = token->authenticate(authFd, appUid, userId); + if (!status.isOk()) { + return status; + } + *_aidl_return = token; + return ok(); +} + +// Enables fs-verity for filePath, which must be an absolute path and the same inode as in the auth +// token previously returned from createFsveritySetupAuthToken, and owned by the app uid. As +// installd is more privileged than its client / system server, we attempt to limit what a +// (compromised) client can do. +// +// The reason for this app request to go through installd is to avoid exposing a risky area (PKCS#7 +// signature verification) in the kernel to the app as an attack surface (it can't be system server +// because it can't override DAC and manipulate app files). Note that we should be able to drop +// these hops and simply the app calls the ioctl, once all upgrading devices run with a kernel +// without fs-verity built-in signature (https://r.android.com/2650402). +binder::Status InstalldNativeService::enableFsverity(const sp<IFsveritySetupAuthToken>& authToken, + const std::string& filePath, + const std::string& packageName, + int32_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(filePath); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE(); + if (authToken == nullptr) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token"); + } + + // Authenticate to check the targeting file is the same inode as the authFd. + sp<IBinder> authTokenBinder = IInterface::asBinder(authToken)->localBinder(); + if (authTokenBinder == nullptr) { + return exception(binder::Status::EX_SECURITY, "Received a non-local auth token"); + } + auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder); + unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + struct stat stFromPath; + if (fstat(rfd.get(), &stFromPath) < 0) { + *_aidl_return = errno; + return ok(); + } + if (!authTokenInstance->isSameStat(stFromPath)) { + LOG(DEBUG) << "FD authentication failed"; + *_aidl_return = EPERM; + return ok(); + } + + fsverity_enable_arg arg = {}; + arg.version = 1; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.block_size = 4096; + if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) { + *_aidl_return = errno; + } else { + *_aidl_return = 0; + } + return ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 521afc3f97..0f28234422 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -19,6 +19,7 @@ #define COMMANDS_H_ #include <inttypes.h> +#include <sys/stat.h> #include <unistd.h> #include <shared_mutex> @@ -35,8 +36,26 @@ namespace android { namespace installd { +using IFsveritySetupAuthToken = android::os::IInstalld::IFsveritySetupAuthToken; + class InstalldNativeService : public BinderService<InstalldNativeService>, public os::BnInstalld { public: + class FsveritySetupAuthToken : public os::IInstalld::BnFsveritySetupAuthToken { + public: + FsveritySetupAuthToken() : mStatFromAuthFd() {} + + binder::Status authenticate(const android::os::ParcelFileDescriptor& authFd, int32_t appUid, + int32_t userId); + bool isSameStat(const struct stat& st) const; + + private: + // Not copyable or movable + FsveritySetupAuthToken(const FsveritySetupAuthToken&) = delete; + FsveritySetupAuthToken& operator=(const FsveritySetupAuthToken&) = delete; + + struct stat mStatFromAuthFd; + }; + static status_t start(); static char const* getServiceName() { return "installd"; } virtual status_t dump(int fd, const Vector<String16> &args) override; @@ -192,6 +211,13 @@ public: const std::optional<std::string>& outputPath, int32_t* _aidl_return); + binder::Status createFsveritySetupAuthToken(const android::os::ParcelFileDescriptor& authFd, + int32_t appUid, int32_t userId, + android::sp<IFsveritySetupAuthToken>* _aidl_return); + binder::Status enableFsverity(const android::sp<IFsveritySetupAuthToken>& authToken, + const std::string& filePath, const std::string& packageName, + int32_t* _aidl_return); + private: std::recursive_mutex mLock; std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 9ad853b1df..8893e38c03 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -134,6 +134,22 @@ interface IInstalld { int getOdexVisibility(@utf8InCpp String packageName, @utf8InCpp String apkPath, @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); + interface IFsveritySetupAuthToken { + // Using an interface here is an easy way to create and maintain an IBinder object across + // the processes. When installd creates this binder object, it stores the file stat + // privately for later authentication, and only returns the reference to the caller process. + // Once the binder object has no reference count, it gets destructed automatically + // (alternatively, installd can maintain an internal mapping, but it is more error prone + // because the app may crash and not finish the fs-verity setup, keeping the memory unused + // forever). + // + // We don't necessarily need a method here, so it's left blank intentionally. + } + IFsveritySetupAuthToken createFsveritySetupAuthToken(in ParcelFileDescriptor authFd, int appUid, + int userId); + int enableFsverity(in IFsveritySetupAuthToken authToken, @utf8InCpp String filePath, + @utf8InCpp String packageName); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 5cf402c54c..df02588a22 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -18,6 +18,7 @@ #define DEXOPT_H_ #include "installd_constants.h" +#include "unique_file.h" #include <sys/types.h> @@ -156,6 +157,10 @@ const char* select_execution_binary( // artifacts. int get_odex_visibility(const char* apk_path, const char* instruction_set, const char* oat_dir); +UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path, + const char* profile_name, bool profile_guided, + bool is_public, int uid, bool is_secondary_dex); + } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 407cb244e3..27ae8f6e6f 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -14,20 +14,21 @@ ** limitations under the License. */ -#include <algorithm> #include <inttypes.h> -#include <limits> -#include <random> -#include <regex> #include <selinux/android.h> #include <selinux/avc.h> #include <stdlib.h> #include <string.h> #include <sys/capability.h> +#include <sys/mman.h> #include <sys/prctl.h> #include <sys/stat.h> -#include <sys/mman.h> #include <sys/wait.h> +#include <algorithm> +#include <iterator> +#include <limits> +#include <random> +#include <regex> #include <android-base/logging.h> #include <android-base/macros.h> @@ -47,6 +48,7 @@ #include "otapreopt_parameters.h" #include "otapreopt_utils.h" #include "system_properties.h" +#include "unique_file.h" #include "utils.h" #ifndef LOG_TAG @@ -87,6 +89,9 @@ static_assert(DEXOPT_GENERATE_APP_IMAGE == 1 << 12, "DEXOPT_GENERATE_APP_IMAGE u static_assert(DEXOPT_MASK == (0x3dfe | DEXOPT_IDLE_BACKGROUND_JOB), "DEXOPT_MASK unexpected."); +constexpr const char* kAotCompilerFilters[]{ + "space-profile", "space", "speed-profile", "speed", "everything-profile", "everything", +}; template<typename T> static constexpr bool IsPowerOfTwo(T x) { @@ -415,6 +420,32 @@ private: return (strcmp(arg, "!") == 0) ? nullptr : arg; } + bool IsAotCompilation() const { + if (std::find(std::begin(kAotCompilerFilters), std::end(kAotCompilerFilters), + parameters_.compiler_filter) == std::end(kAotCompilerFilters)) { + return false; + } + + int dexopt_flags = parameters_.dexopt_flags; + bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0; + bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0; + bool is_public = (dexopt_flags & DEXOPT_PUBLIC) != 0; + + if (profile_guided) { + UniqueFile reference_profile = + maybe_open_reference_profile(parameters_.pkgName, parameters_.apk_path, + parameters_.profile_name, profile_guided, + is_public, parameters_.uid, is_secondary_dex); + struct stat sbuf; + if (reference_profile.fd() == -1 || + (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) { + return false; + } + } + + return true; + } + bool ShouldSkipPreopt() const { // There's one thing we have to be careful about: we may/will be asked to compile an app // living in the system image. This may be a valid request - if the app wasn't compiled, @@ -439,9 +470,12 @@ private: // (This is ugly as it's the only thing where we need to understand the contents // of parameters_, but it beats postponing the decision or using the call- // backs to do weird things.) + + // In addition, no need to preopt for "verify". The existing vdex files in the OTA package + // and the /data partition will still be usable after the OTA update is applied. const char* apk_path = parameters_.apk_path; CHECK(apk_path != nullptr); - if (StartsWith(apk_path, android_root_)) { + if (StartsWith(apk_path, android_root_) || !IsAotCompilation()) { const char* last_slash = strrchr(apk_path, '/'); if (last_slash != nullptr) { std::string path(apk_path, last_slash - apk_path + 1); @@ -526,61 +560,6 @@ private: return Dexopt(); } - //////////////////////////////////// - // Helpers, mostly taken from ART // - //////////////////////////////////// - - // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. - static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { - constexpr size_t kPageSize = PAGE_SIZE; - static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two"); - CHECK_EQ(min_delta % kPageSize, 0u); - CHECK_EQ(max_delta % kPageSize, 0u); - CHECK_LT(min_delta, max_delta); - - std::default_random_engine generator; - generator.seed(GetSeed()); - std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta); - int32_t r = distribution(generator); - if (r % 2 == 0) { - r = RoundUp(r, kPageSize); - } else { - r = RoundDown(r, kPageSize); - } - CHECK_LE(min_delta, r); - CHECK_GE(max_delta, r); - CHECK_EQ(r % kPageSize, 0u); - return r; - } - - static uint64_t GetSeed() { -#ifdef __BIONIC__ - // Bionic exposes arc4random, use it. - uint64_t random_data; - arc4random_buf(&random_data, sizeof(random_data)); - return random_data; -#else -#error "This is only supposed to run with bionic. Otherwise, implement..." -#endif - } - - void AddCompilerOptionFromSystemProperty(const char* system_property, - const char* prefix, - bool runtime, - std::vector<std::string>& out) const { - const std::string* value = system_properties_.GetProperty(system_property); - if (value != nullptr) { - if (runtime) { - out.push_back("--runtime-arg"); - } - if (prefix != nullptr) { - out.push_back(StringPrintf("%s%s", prefix, value->c_str())); - } else { - out.push_back(*value); - } - } - } - static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH"; static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT"; static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA"; diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c86993cb06..c40caf56d8 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -19,9 +19,12 @@ #include <sys/mount.h> #include <sys/stat.h> #include <sys/wait.h> +#include <unistd.h> +#include <algorithm> #include <array> #include <fstream> +#include <iostream> #include <sstream> #include <android-base/file.h> @@ -29,6 +32,7 @@ #include <android-base/macros.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> +#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <libdm/dm.h> #include <selinux/android.h> @@ -37,7 +41,7 @@ #include "otapreopt_utils.h" #ifndef LOG_TAG -#define LOG_TAG "otapreopt" +#define LOG_TAG "otapreopt_chroot" #endif using android::base::StringPrintf; @@ -49,20 +53,22 @@ namespace installd { // so just try the possibilities one by one. static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"}; -static void CloseDescriptor(int fd) { - if (fd >= 0) { - int result = close(fd); - UNUSED(result); // Ignore result. Printing to logcat will open a new descriptor - // that we do *not* want. - } -} - static void CloseDescriptor(const char* descriptor_string) { int fd = -1; std::istringstream stream(descriptor_string); stream >> fd; if (!stream.fail()) { - CloseDescriptor(fd); + if (fd >= 0) { + if (close(fd) < 0) { + PLOG(ERROR) << "Failed to close " << fd; + } + } + } +} + +static void SetCloseOnExec(int fd) { + if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) { + PLOG(ERROR) << "Failed to set FD_CLOEXEC on " << fd; } } @@ -129,24 +135,39 @@ static void TryExtraMount(const char* name, const char* slot, const char* target } // Entry for otapreopt_chroot. Expected parameters are: -// [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] -// The file descriptor denoted by status-fd will be closed. The rest of the parameters will -// be passed on to otapreopt in the chroot. +// +// [cmd] [status-fd] [target-slot-suffix] +// +// The file descriptor denoted by status-fd will be closed. Dexopt commands on +// the form +// +// "dexopt" [dexopt-params] +// +// are then read from stdin until EOF and passed on to /system/bin/otapreopt one +// by one. After each call a line with the current command count is written to +// stdout and flushed. static int otapreopt_chroot(const int argc, char **arg) { // Validate arguments - // We need the command, status channel and target slot, at a minimum. - if(argc < 3) { - PLOG(ERROR) << "Not enough arguments."; + if (argc == 2 && std::string_view(arg[1]) == "--version") { + // Accept a single --version flag, to allow the script to tell this binary + // from the earlier one. + std::cout << "2" << std::endl; + return 0; + } + if (argc != 3) { + LOG(ERROR) << "Wrong number of arguments: " << argc; exit(208); } - // Close all file descriptors. They are coming from the caller, we do not want to pass them - // on across our fork/exec into a different domain. - // 1) Default descriptors. - CloseDescriptor(STDIN_FILENO); - CloseDescriptor(STDOUT_FILENO); - CloseDescriptor(STDERR_FILENO); - // 2) The status channel. - CloseDescriptor(arg[1]); + const char* status_fd = arg[1]; + const char* slot_suffix = arg[2]; + + // Set O_CLOEXEC on standard fds. They are coming from the caller, we do not + // want to pass them on across our fork/exec into a different domain. + SetCloseOnExec(STDIN_FILENO); + SetCloseOnExec(STDOUT_FILENO); + SetCloseOnExec(STDERR_FILENO); + // Close the status channel. + CloseDescriptor(status_fd); // We need to run the otapreopt tool from the postinstall partition. As such, set up a // mount namespace and change root. @@ -185,20 +206,20 @@ static int otapreopt_chroot(const int argc, char **arg) { // 2) We're in a mount namespace here, so when we die, this will be cleaned up. // 3) Ignore errors. Printing anything at this stage will open a file descriptor // for logging. - if (!ValidateTargetSlotSuffix(arg[2])) { - LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; + if (!ValidateTargetSlotSuffix(slot_suffix)) { + LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; exit(207); } - TryExtraMount("vendor", arg[2], "/postinstall/vendor"); + TryExtraMount("vendor", slot_suffix, "/postinstall/vendor"); // Try to mount the product partition. update_engine doesn't do this for us, but we // want it for product APKs. Same notes as vendor above. - TryExtraMount("product", arg[2], "/postinstall/product"); + TryExtraMount("product", slot_suffix, "/postinstall/product"); // Try to mount the system_ext partition. update_engine doesn't do this for // us, but we want it for system_ext APKs. Same notes as vendor and product // above. - TryExtraMount("system_ext", arg[2], "/postinstall/system_ext"); + TryExtraMount("system_ext", slot_suffix, "/postinstall/system_ext"); constexpr const char* kPostInstallLinkerconfig = "/postinstall/linkerconfig"; // Try to mount /postinstall/linkerconfig. we will set it up after performing the chroot @@ -329,30 +350,37 @@ static int otapreopt_chroot(const int argc, char **arg) { exit(218); } - // Now go on and run otapreopt. + // Now go on and read dexopt lines from stdin and pass them on to otapreopt. - // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc - // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1 - std::vector<std::string> cmd; - cmd.reserve(argc); - cmd.push_back("/system/bin/otapreopt"); + int count = 1; + for (std::array<char, 1000> linebuf; + std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) { + // Subtract one from gcount() since getline() counts the newline. + std::string line(&linebuf[0], std::cin.gcount() - 1); - // The first parameter is the status file descriptor, skip. - for (size_t i = 2; i < static_cast<size_t>(argc); ++i) { - cmd.push_back(arg[i]); - } + if (std::cin.fail()) { + LOG(ERROR) << "Command exceeds max length " << linebuf.size() << " - skipped: " << line; + continue; + } - // Fork and execute otapreopt in its own process. - std::string error_msg; - bool exec_result = Exec(cmd, &error_msg); - if (!exec_result) { - LOG(ERROR) << "Running otapreopt failed: " << error_msg; - } + std::vector<std::string> tokenized_line = android::base::Tokenize(line, " "); + std::vector<std::string> cmd{"/system/bin/otapreopt", slot_suffix}; + std::move(tokenized_line.begin(), tokenized_line.end(), std::back_inserter(cmd)); - if (!exec_result) { - exit(213); + LOG(INFO) << "Command " << count << ": " << android::base::Join(cmd, " "); + + // Fork and execute otapreopt in its own process. + std::string error_msg; + bool exec_result = Exec(cmd, &error_msg); + if (!exec_result) { + LOG(ERROR) << "Running otapreopt failed: " << error_msg; + } + + // Print the count to stdout and flush to indicate progress. + std::cout << count << std::endl; } + LOG(INFO) << "No more dexopt commands"; return 0; } diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index e483d54a2a..28bd7932a2 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -16,7 +16,9 @@ # limitations under the License. # -# This script will run as a postinstall step to drive otapreopt. +# This script runs as a postinstall step to drive otapreopt. It comes with the +# OTA package, but runs /system/bin/otapreopt_chroot in the (old) active system +# image. See system/extras/postinst/postinst.sh for some docs. TARGET_SLOT="$1" STATUS_FD="$2" @@ -31,12 +33,11 @@ BOOT_PROPERTY_NAME="dev.bootcomplete" BOOT_COMPLETE=$(getprop $BOOT_PROPERTY_NAME) if [ "$BOOT_COMPLETE" != "1" ] ; then - echo "Error: boot-complete not detected." + echo "$0: Error: boot-complete not detected." # We must return 0 to not block sideload. exit 0 fi - # Compute target slot suffix. # TODO: Once bootctl is not restricted, we should query from there. Or get this from # update_engine as a parameter. @@ -45,44 +46,63 @@ if [ "$TARGET_SLOT" = "0" ] ; then elif [ "$TARGET_SLOT" = "1" ] ; then TARGET_SLOT_SUFFIX="_b" else - echo "Unknown target slot $TARGET_SLOT" + echo "$0: Unknown target slot $TARGET_SLOT" exit 1 fi +if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then + # We require an updated chroot wrapper that reads dexopt commands from stdin. + # Even if we kept compat with the old binary, the OTA preopt wouldn't work due + # to missing sepolicy rules, so there's no use spending time trying to dexopt + # (b/291974157). + echo "$0: Current system image is too old to work with OTA preopt - skipping." + exit 0 +fi PREPARE=$(cmd otadexopt prepare) # Note: Ignore preparation failures. Step and done will fail and exit this. # This is necessary to support suspends - the OTA service will keep # the state around for us. -PROGRESS=$(cmd otadexopt progress) -print -u${STATUS_FD} "global_progress $PROGRESS" - -i=0 -while ((i<MAXIMUM_PACKAGES)) ; do +# Create an array with all dexopt commands in advance, to know how many there are. +otadexopt_cmds=() +while (( ${#otadexopt_cmds[@]} < MAXIMUM_PACKAGES )) ; do DONE=$(cmd otadexopt done) if [ "$DONE" = "OTA complete." ] ; then break fi + otadexopt_cmds+=("$(cmd otadexopt next)") +done - DEXOPT_PARAMS=$(cmd otadexopt next) +DONE=$(cmd otadexopt done) +cmd otadexopt cleanup - /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&- +echo "$0: Using streaming otapreopt_chroot on ${#otadexopt_cmds[@]} packages" - PROGRESS=$(cmd otadexopt progress) - print -u${STATUS_FD} "global_progress $PROGRESS" +function print_otadexopt_cmds { + for cmd in "${otadexopt_cmds[@]}" ; do + print "$cmd" + done +} - i=$((i+1)) -done +function report_progress { + while read count ; do + # mksh can't do floating point arithmetic, so emulate a fixed point calculation. + (( permilles = 1000 * count / ${#otadexopt_cmds[@]} )) + printf 'global_progress %d.%03d\n' $((permilles / 1000)) $((permilles % 1000)) >&${STATUS_FD} + done +} + +print_otadexopt_cmds | \ + /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX | \ + report_progress -DONE=$(cmd otadexopt done) if [ "$DONE" = "OTA incomplete." ] ; then - echo "Incomplete." + echo "$0: Incomplete." else - echo "Complete or error." + echo "$0: Complete or error." fi print -u${STATUS_FD} "global_progress 1.0" -cmd otadexopt cleanup exit 0 diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp index 4221a3a593..7648265f0f 100644 --- a/cmds/installd/run_dex2oat.cpp +++ b/cmds/installd/run_dex2oat.cpp @@ -208,36 +208,13 @@ void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, } // Compute compiler filter. - { - std::string dex2oat_compiler_filter_arg; - { - // If we are booting without the real /data, don't spend time compiling. - std::string vold_decrypt = GetProperty("vold.decrypt", ""); - bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || - vold_decrypt == "1"; - - bool have_dex2oat_relocation_skip_flag = false; - if (skip_compilation) { - dex2oat_compiler_filter_arg = "--compiler-filter=extract"; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", - compiler_filter); - } - if (have_dex2oat_relocation_skip_flag) { - AddRuntimeArg("-Xnorelocate"); - } - } - - if (dex2oat_compiler_filter_arg.empty()) { - dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", - "--compiler-filter=%s"); - } - AddArg(dex2oat_compiler_filter_arg); - - if (compilation_reason != nullptr) { - AddArg(std::string("--compilation-reason=") + compilation_reason); - } + if (compiler_filter != nullptr) { + AddArg(StringPrintf("--compiler-filter=%s", compiler_filter)); + } else { + AddArg(MapPropertyToArg("dalvik.vm.dex2oat-filter", "--compiler-filter=%s")); + } + if (compilation_reason != nullptr) { + AddArg(std::string("--compilation-reason=") + compilation_reason); } AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp index 304ba7b04f..56f84a5e12 100644 --- a/cmds/installd/run_dex2oat_test.cpp +++ b/cmds/installd/run_dex2oat_test.cpp @@ -441,24 +441,6 @@ TEST_F(RunDex2OatTest, Runtime) { VerifyExpectedFlags(); } -TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) { - setSystemProperty("vold.decrypt", "trigger_restart_min_framework"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - -TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) { - setSystemProperty("vold.decrypt", "1"); - CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); - - SetExpectedFlagUsed("--compiler-filter", "=extract"); - SetExpectedFlagUsed("-Xnorelocate", ""); - VerifyExpectedFlags(); -} - TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) { setSystemProperty("dalvik.vm.dex2oat-filter", "speed"); auto args = RunDex2OatArgs::MakeDefaultTestArgs(); diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 858a92cc65..4bc92afa18 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -42,9 +42,12 @@ #include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" +#include "unique_file.h" #include "utils.h" using android::base::StringPrintf; +using android::base::unique_fd; +using android::os::ParcelFileDescriptor; using std::filesystem::is_empty; namespace android { @@ -136,6 +139,16 @@ static int create(const std::string& path, uid_t owner, gid_t group, mode_t mode return fd; } +static void create_with_content(const std::string& path, uid_t owner, gid_t group, mode_t mode, + const std::string& content) { + int fd = ::open(path.c_str(), O_RDWR | O_CREAT, mode); + EXPECT_NE(fd, -1); + EXPECT_TRUE(android::base::WriteStringToFd(content, fd)); + EXPECT_EQ(::fchown(fd, owner, group), 0); + EXPECT_EQ(::fchmod(fd, mode), 0); + close(fd); +} + static void touch(const std::string& path, uid_t owner, gid_t group, mode_t mode) { EXPECT_EQ(::close(create(path.c_str(), owner, group, mode)), 0); } @@ -527,6 +540,94 @@ TEST_F(ServiceTest, GetAppSizeWrongSizes) { externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize)); } + +class FsverityTest : public ServiceTest { +protected: + binder::Status createFsveritySetupAuthToken(const std::string& path, int open_mode, + sp<IFsveritySetupAuthToken>* _aidl_return) { + unique_fd ufd(open(path.c_str(), open_mode)); + EXPECT_GE(ufd.get(), 0) << "open failed: " << strerror(errno); + ParcelFileDescriptor rfd(std::move(ufd)); + return service->createFsveritySetupAuthToken(std::move(rfd), kTestAppId, kTestUserId, + _aidl_return); + } +}; + +TEST_F(FsverityTest, enableFsverity) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token works to enable fs-verity + int32_t errno_local; + status = service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(errno_local, 0); +} + +TEST_F(FsverityTest, enableFsverity_nullAuthToken) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Verity null auth token fails + sp<IFsveritySetupAuthToken> authToken; + int32_t errno_local; + binder::Status status = + service->enableFsverity(authToken, path, "fake.package.name", &errno_local); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, enableFsverity_differentFile) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect to fs-verity setup to succeed + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity auth token does not work for a different file + const std::string anotherPath = kTestPath + "/bar"; + ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath)); + UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); }); + int32_t errno_local; + status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_NE(errno_local, 0); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDONLY, &authToken); + EXPECT_FALSE(status.isOk()); +} + +TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) { + const std::string path = kTestPath + "/foo"; + // Simulate world-writable file owned by another app + create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content"); + UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + + // Expect the fs-verity setup to fail + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_FALSE(status.isOk()); +} + static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 3ce586bcd6..c962c15a24 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -263,8 +263,8 @@ prebuilt_etc { } prebuilt_etc { - name: "android.hardware.threadnetwork.prebuilt.xml", - src: "android.hardware.threadnetwork.xml", + name: "android.hardware.thread_network.prebuilt.xml", + src: "android.hardware.thread_network.xml", defaults: ["frameworks_native_data_etc_defaults"], } diff --git a/data/etc/android.hardware.threadnetwork.xml b/data/etc/android.hardware.thread_network.xml index 9cbdc905fb..b116ed6bf8 100644 --- a/data/etc/android.hardware.threadnetwork.xml +++ b/data/etc/android.hardware.thread_network.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<!-- Adds the feature indicating support for the ThreadNetwork API --> +<!-- Adds the feature indicating support for the Thread networking protocol --> <permissions> - <feature name="android.hardware.threadnetwork" /> + <feature name="android.hardware.thread_network" /> </permissions> diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index cedd361f7e..ba8b02d597 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -14,6 +14,23 @@ * limitations under the License. */ + /** + * @defgroup APerformanceHint Performance Hint Manager + * + * APerformanceHint allows apps to create performance hint sessions for groups + * of threads, and provide hints to the system about the workload of those threads, + * to help the system more accurately allocate power for them. It is the NDK + * counterpart to the Java PerformanceHintManager SDK API. + * + * @{ + */ + +/** + * @file performance_hint.h + * @brief API for creating and managing a hint session. + */ + + #ifndef ANDROID_NATIVE_PERFORMANCE_HINT_H #define ANDROID_NATIVE_PERFORMANCE_HINT_H @@ -48,7 +65,7 @@ struct APerformanceHintSession; * An opaque type representing a handle to a performance hint manager. * It must be released after use. * - * <p>To use:<ul> + * To use:<ul> * <li>Obtain the performance hint manager instance by calling * {@link APerformanceHint_getManager} function.</li> * <li>Create an {@link APerformanceHintSession} with @@ -61,50 +78,43 @@ typedef struct APerformanceHintManager APerformanceHintManager; /** * An opaque type representing a handle to a performance hint session. * A session can only be acquired from a {@link APerformanceHintManager} - * with {@link APerformanceHint_getPreferredUpdateRateNanos}. It must be + * with {@link APerformanceHint_createSession}. It must be * freed with {@link APerformanceHint_closeSession} after use. * * A Session represents a group of threads with an inter-related workload such that hints for * their performance should be considered as a unit. The threads in a given session should be - * long-life and not created or destroyed dynamically. - * - * <p>Each session is expected to have a periodic workload with a target duration for each - * cycle. The cycle duration is likely greater than the target work duration to allow other - * parts of the pipeline to run within the available budget. For example, a renderer thread may - * work at 60hz in order to produce frames at the display's frame but have a target work - * duration of only 6ms.</p> - * - * <p>After each cycle of work, the client is expected to use - * {@link APerformanceHint_reportActualWorkDuration} to report the actual time taken to - * complete.</p> - * - * <p>To use:<ul> - * <li>Update a sessions target duration for each cycle of work - * with {@link APerformanceHint_updateTargetWorkDuration}.</li> - * <li>Report the actual duration for the last cycle of work with - * {@link APerformanceHint_reportActualWorkDuration}.</li> - * <li>Release the session instance with - * {@link APerformanceHint_closeSession}.</li></ul></p> + * long-lived and not created or destroyed dynamically. + * + * The work duration API can be used with periodic workloads to dynamically adjust thread + * performance and keep the work on schedule while optimizing the available power budget. + * When using the work duration API, the starting target duration should be specified + * while creating the session, and can later be adjusted with + * {@link APerformanceHint_updateTargetWorkDuration}. While using the work duration + * API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each + * cycle to report the actual time taken to complete to the system. + * + * All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)` */ typedef struct APerformanceHintSession APerformanceHintSession; /** * Acquire an instance of the performance hint manager. * - * @return manager instance on success, nullptr on failure. + * @return APerformanceHintManager instance on success, nullptr on failure. */ APerformanceHintManager* APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__); /** * Creates a session for the given set of threads and sets their initial target work * duration. + * * @param manager The performance hint manager instance. * @param threadIds The list of threads to be associated with this session. They must be part of - * this app's thread group. - * @param size the size of threadIds. - * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new session. - * This must be positive. - * @return manager instance on success, nullptr on failure. + * this process' thread group. + * @param size The size of the list of threadIds. + * @param initialTargetWorkDurationNanos The target duration in nanoseconds for the new session. + * This must be positive if using the work duration API, or 0 otherwise. + * @return APerformanceHintManager instance on success, nullptr on failure. */ APerformanceHintSession* APerformanceHint_createSession( APerformanceHintManager* manager, @@ -124,8 +134,8 @@ int64_t APerformanceHint_getPreferredUpdateRateNanos( * Updates this session's target duration for each cycle of work. * * @param session The performance hint session instance to update. - * @param targetDurationNanos the new desired duration in nanoseconds. This must be positive. - * @return 0 on success + * @param targetDurationNanos The new desired duration in nanoseconds. This must be positive. + * @return 0 on success. * EINVAL if targetDurationNanos is not positive. * EPIPE if communication with the system service has failed. */ @@ -136,14 +146,13 @@ int APerformanceHint_updateTargetWorkDuration( /** * Reports the actual duration for the last cycle of work. * - * <p>The system will attempt to adjust the core placement of the threads within the thread - * group and/or the frequency of the core on which they are run to bring the actual duration - * close to the target duration.</p> + * The system will attempt to adjust the scheduling and performance of the + * threads within the thread group to bring the actual duration close to the target duration. * * @param session The performance hint session instance to update. - * @param actualDurationNanos how long the thread group took to complete its last task in - * nanoseconds. This must be positive. - * @return 0 on success + * @param actualDurationNanos The duration of time the thread group took to complete its last + * task in nanoseconds. This must be positive. + * @return 0 on success. * EINVAL if actualDurationNanos is not positive. * EPIPE if communication with the system service has failed. */ @@ -164,12 +173,13 @@ void APerformanceHint_closeSession( * Set a list of threads to the performance hint session. This operation will replace * the current list of threads with the given list of threads. * - * @param session The performance hint session instance for the threads. + * @param session The performance hint session instance to update. * @param threadIds The list of threads to be associated with this session. They must be part of * this app's thread group. - * @param size the size of the list of threadIds. + * @param size The size of the list of threadIds. * @return 0 on success. - * EINVAL if the list of thread ids is empty or if any of the thread ids is not part of the thread group. + * EINVAL if the list of thread ids is empty or if any of the thread ids are not part of + the thread group. * EPIPE if communication with the system service has failed. * EPERM if any thread id doesn't belong to the application. */ @@ -178,6 +188,21 @@ int APerformanceHint_setThreads( const pid_t* threadIds, size_t size) __INTRODUCED_IN(__ANDROID_API_U__); +/** + * This tells the session that these threads can be + * safely scheduled to prefer power efficiency over performance. + * + * @param session The performance hint session instance to update. + * @param enabled The flag which sets whether this session will use power-efficient scheduling. + * @return 0 on success. + * EPIPE if communication with the system service has failed. + */ +int APerformanceHint_setPreferPowerEfficiency( + APerformanceHintSession* session, + bool enabled) __INTRODUCED_IN(__ANDROID_API_V__); + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H + +/** @} */
\ No newline at end of file diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index b58feac444..2e9949578d 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -16,6 +16,7 @@ #pragma once +#include <android/os/IInputConstants.h> #include <input/Input.h> #include <input/RingBuffer.h> #include <utils/BitSet.h> @@ -35,19 +36,20 @@ public: static const size_t MAX_DEGREE = 4; enum class Strategy : int32_t { - DEFAULT = -1, - MIN = 0, - IMPULSE = 0, - LSQ1 = 1, - LSQ2 = 2, - LSQ3 = 3, - WLSQ2_DELTA = 4, - WLSQ2_CENTRAL = 5, - WLSQ2_RECENT = 6, - INT1 = 7, - INT2 = 8, - LEGACY = 9, + DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT, + IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE, + LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1, + LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2, + LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3, + WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA, + WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL, + WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT, + INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1, + INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2, + LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY, + MIN = IMPULSE, MAX = LEGACY, + ftl_last = LEGACY, }; /* @@ -81,8 +83,6 @@ public: // TODO(b/32830165): support axis-specific strategies. VelocityTracker(const Strategy strategy = Strategy::DEFAULT); - ~VelocityTracker(); - /** Return true if the axis is supported for velocity tracking, false otherwise. */ static bool isAxisSupported(int32_t axis); diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 3f1fc33c90..6c2b313f8a 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -190,6 +190,9 @@ cc_defaults { "-performance-move-const-arg", // b/273486801 "portability*", ], + lto: { + thin: true, + }, } cc_library_headers { diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index f2b4a6ef37..7d6ae00ed2 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -193,8 +193,7 @@ public: status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { - ALOGD("FGS Logger Transaction failed"); - ALOGD("%d", err); + ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } return NO_ERROR; @@ -209,8 +208,7 @@ public: status_t err = remote()->transact(LOG_FGS_API_END_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { - ALOGD("FGS Logger Transaction failed"); - ALOGD("%d", err); + ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } return NO_ERROR; @@ -224,11 +222,10 @@ public: data.writeInt32(state); data.writeInt32(appUid); data.writeInt32(appPid); - status_t err = remote()->transact(LOG_FGS_API_BEGIN_TRANSACTION, data, &reply, + status_t err = remote()->transact(LOG_FGS_API_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) { - ALOGD("FGS Logger Transaction failed"); - ALOGD("%d", err); + ALOGD("%s: FGS Logger Transaction failed, %d", __func__, err); return err; } return NO_ERROR; diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 3da06ba4db..fc273e0367 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -73,8 +73,8 @@ MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) ALOGV("MemoryHeapBase: Attempting to force MemFD"); fd = memfd_create_region(name ? name : "MemoryHeapBase", size); if (fd < 0 || (mapfd(fd, true, size) != NO_ERROR)) return; - const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | - ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL); + const int SEAL_FLAGS = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) | F_SEAL_GROW | + F_SEAL_SHRINK | ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL); if (SEAL_FLAGS && (fcntl(fd, F_ADD_SEALS, SEAL_FLAGS) == -1)) { ALOGE("MemoryHeapBase: MemFD %s sealing with flags %x failed with error %s", name, SEAL_FLAGS, strerror(errno)); diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d36ebac109..57a38dc480 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -11,9 +11,6 @@ rust_library { name: "libbinder_rs", crate_name: "binder", srcs: ["src/lib.rs"], - shared_libs: [ - "libutils", - ], rustlibs: [ "libbinder_ndk_sys", "libdowncast_rs", @@ -97,34 +94,12 @@ rust_bindgen { crate_name: "binder_ndk_bindgen", wrapper_src: "sys/BinderBindings.hpp", source_stem: "bindings", - bindgen_flags: [ + bindgen_flag_files: [ // Unfortunately the only way to specify the rust_non_exhaustive enum // style for a type is to make it the default - "--default-enum-style", - "rust_non_exhaustive", // and then specify constified enums for the enums we don't want // rustified - "--constified-enum", - "android::c_interface::consts::.*", - - "--allowlist-type", - "android::c_interface::.*", - "--allowlist-type", - "AStatus", - "--allowlist-type", - "AIBinder_Class", - "--allowlist-type", - "AIBinder", - "--allowlist-type", - "AIBinder_Weak", - "--allowlist-type", - "AIBinder_DeathRecipient", - "--allowlist-type", - "AParcel", - "--allowlist-type", - "binder_status_t", - "--allowlist-function", - ".*", + "libbinder_ndk_bindgen_flags.txt", ], shared_libs: [ "libbinder_ndk", diff --git a/libs/binder/rust/libbinder_ndk_bindgen_flags.txt b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt new file mode 100644 index 0000000000..551c59f671 --- /dev/null +++ b/libs/binder/rust/libbinder_ndk_bindgen_flags.txt @@ -0,0 +1,11 @@ +--default-enum-style=rust_non_exhaustive +--constified-enum=android::c_interface::consts::.* +--allowlist-type=android::c_interface::.* +--allowlist-type=AStatus +--allowlist-type=AIBinder_Class +--allowlist-type=AIBinder +--allowlist-type=AIBinder_Weak +--allowlist-type=AIBinder_DeathRecipient +--allowlist-type=AParcel +--allowlist-type=binder_status_t +--allowlist-function=.* diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs index 8d9ce0e731..eb04cc3153 100644 --- a/libs/binder/rust/src/error.rs +++ b/libs/binder/rust/src/error.rs @@ -370,6 +370,94 @@ unsafe impl AsNative<sys::AStatus> for Status { } } +/// A conversion from `std::result::Result<T, E>` to `binder::Result<T>`. If this type is `Ok(T)`, +/// it's returned as is. If this type is `Err(E)`, `E` is converted into `Status` which can be +/// either a general binder exception, or a service-specific exception. +/// +/// # Examples +/// +/// ``` +/// // std::io::Error is formatted as the exception's message +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // A custom function is used to create the exception's message +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_service_specific_exception_with(NOT_FOUND, +/// |e| format!("file {} not found: {:?}", name, e))? +/// } +/// +/// // anyhow::Error is formatted as the exception's message +/// use anyhow::{Context, Result}; +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .context("file {} not found") +/// .or_service_specific_exception(NOT_FOUND)? +/// } +/// +/// // General binder exceptions can be created similarly +/// fn file_exists(name: &str) -> binder::Result<bool> { +/// std::fs::metadata(name) +/// .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)? +/// } +/// ``` +pub trait IntoBinderResult<T, E> { + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by formatting the error for debugging. + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status>; + + /// Converts the embedded error into a general binder exception of code `exception`. The + /// message of the exception is set by lazily evaluating the `op` function. + fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result<T, Status>; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by formatting the error for debugging. + fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status>; + + /// Converts the embedded error into a service-specific binder exception. `error_code` is used + /// to distinguish different service-specific binder exceptions. The message of the exception + /// is set by lazily evaluating the `op` function. + fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result<T, Status>; +} + +impl<T, E: std::fmt::Debug> IntoBinderResult<T, E> for result::Result<T, E> { + fn or_binder_exception(self, exception: ExceptionCode) -> result::Result<T, Status> { + self.or_binder_exception_with(exception, |e| format!("{:?}", e)) + } + + fn or_binder_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + exception: ExceptionCode, + op: O, + ) -> result::Result<T, Status> { + self.map_err(|e| Status::new_exception_str(exception, Some(op(e)))) + } + + fn or_service_specific_exception(self, error_code: i32) -> result::Result<T, Status> { + self.or_service_specific_exception_with(error_code, |e| format!("{:?}", e)) + } + + fn or_service_specific_exception_with<M: AsRef<str>, O: FnOnce(E) -> M>( + self, + error_code: i32, + op: O, + ) -> result::Result<T, Status> { + self.map_err(|e| Status::new_service_specific_error_str(error_code, Some(op(e)))) + } +} + #[cfg(test)] mod tests { use super::*; @@ -406,4 +494,66 @@ mod tests { assert_eq!(status.service_specific_error(), 0); assert_eq!(status.get_description(), "Status(-5, EX_ILLEGAL_STATE): ''".to_string()); } + + #[test] + fn convert_to_service_specific_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_service_specific_exception(-42); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_service_specific_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_service_specific_exception_with(-42, |e| format!("outer message: {:?}", e)); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::SERVICE_SPECIFIC); + assert_eq!(status.service_specific_error(), -42); + assert_eq!( + status.get_description(), + "Status(-8, EX_SERVICE_SPECIFIC): '-42: outer message: \"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception() { + let res: std::result::Result<(), Status> = + Err("message").or_binder_exception(ExceptionCode::ILLEGAL_STATE); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): '\"message\"'".to_string() + ); + } + + #[test] + fn convert_to_binder_exception_with() { + let res: std::result::Result<(), Status> = Err("message") + .or_binder_exception_with(ExceptionCode::ILLEGAL_STATE, |e| { + format!("outer message: {:?}", e) + }); + + assert!(res.is_err()); + let status = res.unwrap_err(); + assert_eq!(status.exception_code(), ExceptionCode::ILLEGAL_STATE); + assert_eq!(status.service_specific_error(), 0); + assert_eq!( + status.get_description(), + "Status(-5, EX_ILLEGAL_STATE): 'outer message: \"message\"'".to_string() + ); + } } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 0c8b48f1f5..8841fe640b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -106,7 +106,7 @@ use binder_ndk_sys as sys; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; -pub use error::{ExceptionCode, Status, StatusCode}; +pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use native::{ add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, LazyServiceGuard, diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp index ac968237a0..6eb707bcf7 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp @@ -3,19 +3,12 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -rust_fuzz { - name: "parcel_fuzzer_rs", - srcs: [ - "parcel_fuzzer.rs", - ], +rust_defaults { + name: "service_fuzzer_defaults_rs", rustlibs: [ - "libarbitrary", - "libnum_traits", "libbinder_rs", "libbinder_random_parcel_rs", - "binderReadParcelIface-rust", ], - fuzz_config: { cc: [ "waghpawan@google.com", @@ -26,3 +19,18 @@ rust_fuzz { hotlists: ["4637097"], }, } + +rust_fuzz { + name: "parcel_fuzzer_rs", + srcs: [ + "parcel_fuzzer.rs", + ], + defaults: [ + "service_fuzzer_defaults_rs", + ], + rustlibs: [ + "libarbitrary", + "libnum_traits", + "binderReadParcelIface-rust", + ], +} diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp index 2537ce0bcb..84130c17e1 100644 --- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp +++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp @@ -19,18 +19,10 @@ rust_fuzz { srcs: [ "service_fuzzer.rs", ], + defaults: [ + "service_fuzzer_defaults_rs", + ], rustlibs: [ - "libbinder_rs", - "libbinder_random_parcel_rs", "testServiceInterface-rust", ], - fuzz_config: { - cc: [ - "waghpawan@google.com", - "smoreland@google.com", - ], - triage_assignee: "waghpawan@google.com", - // hotlist "AIDL fuzzers bugs" on buganizer - hotlists: ["4637097"], - }, } diff --git a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp index 278dd2bf81..140270f5a1 100644 --- a/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp +++ b/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp @@ -37,7 +37,8 @@ TEST(MemoryHeapBase, MemfdSealed) { ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); - EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL); + EXPECT_EQ(ftruncate(fd, 4096), -1); } TEST(MemoryHeapBase, MemfdUnsealed) { @@ -48,7 +49,8 @@ TEST(MemoryHeapBase, MemfdUnsealed) { ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); - EXPECT_EQ(fcntl(fd, F_GET_SEALS), 0); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK); + EXPECT_EQ(ftruncate(fd, 4096), -1); } TEST(MemoryHeapBase, MemfdSealedProtected) { @@ -59,7 +61,9 @@ TEST(MemoryHeapBase, MemfdSealedProtected) { ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); - EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_SEAL | F_SEAL_FUTURE_WRITE); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL | F_SEAL_FUTURE_WRITE); + EXPECT_EQ(ftruncate(fd, 4096), -1); } TEST(MemoryHeapBase, MemfdUnsealedProtected) { @@ -71,7 +75,8 @@ TEST(MemoryHeapBase, MemfdUnsealedProtected) { ASSERT_NE(mHeap.get(), nullptr); int fd = mHeap->getHeapID(); EXPECT_NE(fd, -1); - EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_FUTURE_WRITE); + EXPECT_EQ(fcntl(fd, F_GET_SEALS), F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_FUTURE_WRITE); + EXPECT_EQ(ftruncate(fd, 4096), -1); } #else diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 0d1503eda0..383795eff5 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -129,3 +129,18 @@ cc_library { ], export_include_dirs: ["include_random_parcel_seeds"], } + +cc_binary_host { + name: "binder2corpus", + static_libs: [ + "libbinder_random_parcel_seeds", + ], + srcs: [ + "binder2corpus/binder2corpus.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + ], +} diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md new file mode 100644 index 0000000000..59bf9f31c0 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/README.md @@ -0,0 +1,31 @@ +# binder2corpus + +This tool converts recordings generated by record_binder tool to fuzzer seeds for fuzzService. + +# Steps to add corpus: + +## Start recording the service binder +ex. record_binder start manager + +## Run test on device or keep device idle +ex. atest servicemanager_test + +## Stop the recording +record_binder stop manager + +## Pull the recording on host +Recordings are present on device at /data/local/recordings/<service_name>. Use adb pull. +Use inspect command of record_binder to check if there are some transactions captured. +ex. record_binder inspect manager + +## run corpus generator tool +binder2corpus <recording_path> <dir_to_write_corpus> + +## Build fuzzer and sync data directory +ex. m servicemanager_fuzzer && adb sync data + +## Push corpus on device +ex. adb push servicemanager_fuzzer_corpus/ /data/fuzz/x86_64/servicemanager_fuzzer/ + +## Run fuzzer with corpus directory as argument +ex. adb shell /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer /data/fuzz/x86_64/servicemanager_fuzzer/servicemanager_fuzzer_corpus
\ No newline at end of file diff --git a/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp new file mode 100644 index 0000000000..c0fdaea0a8 --- /dev/null +++ b/libs/binder/tests/parcel_fuzzer/binder2corpus/binder2corpus.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/RecordedTransaction.h> + +#include <fuzzseeds/random_parcel_seeds.h> + +#include <sys/prctl.h> + +using android::generateSeedsFromRecording; +using android::status_t; +using android::base::unique_fd; +using android::binder::debug::RecordedTransaction; + +status_t generateCorpus(const char* recordingPath, const char* corpusDir) { + unique_fd fd(open(recordingPath, O_RDONLY)); + if (!fd.ok()) { + std::cerr << "Failed to open recording file at path " << recordingPath + << " with error: " << strerror(errno) << '\n'; + return android::BAD_VALUE; + } + + if (auto res = mkdir(corpusDir, 0766); res != 0) { + std::cerr + << "Failed to create corpus directory at path. Delete directory if already exists: " + << corpusDir << std::endl; + return android::BAD_VALUE; + } + + int transactionNumber = 0; + while (auto transaction = RecordedTransaction::fromFile(fd)) { + ++transactionNumber; + std::string filePath = std::string(corpusDir) + std::string("transaction_") + + std::to_string(transactionNumber); + constexpr int openFlags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC; + android::base::unique_fd corpusFd(open(filePath.c_str(), openFlags, 0666)); + if (!corpusFd.ok()) { + std::cerr << "Failed to open fd. Path " << filePath + << " with error: " << strerror(errno) << std::endl; + return android::UNKNOWN_ERROR; + } + generateSeedsFromRecording(corpusFd, transaction.value()); + } + + if (transactionNumber == 0) { + std::cerr << "No valid transaction has been found in recording file: " << recordingPath + << std::endl; + return android::BAD_VALUE; + } + + return android::NO_ERROR; +} + +void printHelp(const char* toolName) { + std::cout << "Usage: \n\n" + << toolName + << " <recording_path> <destination_directory> \n\n*Use " + "record_binder tool for recording binder transactions." + << std::endl; +} + +int main(int argc, char** argv) { + if (argc != 3) { + printHelp(argv[0]); + return 1; + } + const char* sourcePath = argv[1]; + const char* corpusDir = argv[2]; + if (android::NO_ERROR != generateCorpus(sourcePath, corpusDir)) { + std::cerr << "Failed to generate fuzzer corpus." << std::endl; + return 1; + } + return 0; +} diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp index 47d2a0a701..93ac1162ed 100644 --- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp +++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp @@ -60,8 +60,15 @@ void fuzzService(const std::vector<sp<IBinder>>& binders, FuzzedDataProvider&& p while (provider.remaining_bytes() > 0) { // Most of the AIDL services will have small set of transaction codes. - uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>() - : provider.ConsumeIntegralInRange<uint32_t>(0, 100); + // TODO(b/295942369) : Add remaining transact codes from IBinder.h + uint32_t code = provider.ConsumeBool() + ? provider.ConsumeIntegral<uint32_t>() + : provider.PickValueInArray<int64_t>( + {provider.ConsumeIntegralInRange<uint32_t>(0, 100), + IBinder::DUMP_TRANSACTION, IBinder::PING_TRANSACTION, + IBinder::SHELL_COMMAND_TRANSACTION, IBinder::INTERFACE_TRANSACTION, + IBinder::SYSPROPS_TRANSACTION, IBinder::EXTENSION_TRANSACTION, + IBinder::TWEET_TRANSACTION, IBinder::LIKE_TRANSACTION}); uint32_t flags = provider.ConsumeIntegral<uint32_t>(); Parcel data; // for increased fuzz coverage diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp index 96092b10d3..690c39afc9 100644 --- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/Android.bp @@ -36,8 +36,8 @@ cc_fuzz { triage_assignee: "waghpawan@google.com", // This fuzzer should be used only test fuzzService locally - fuzz_on_haiku_host: true, - fuzz_on_haiku_device: true, + fuzz_on_haiku_host: false, + fuzz_on_haiku_device: false, }, } diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp index 46205d7689..d2fa581822 100644 --- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/TestServiceFuzzer.cpp @@ -33,6 +33,9 @@ enum class CrashType { ON_KNOWN_UID, ON_SYSTEM_AID, ON_ROOT_AID, + ON_DUMP_TRANSACT, + ON_SHELL_CMD_TRANSACT, + CRASH_ALWAYS, }; // This service is to verify that fuzzService is functioning properly @@ -92,6 +95,16 @@ public: return Status::ok(); } + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { + if (mCrash == CrashType::ON_DUMP_TRANSACT && code == DUMP_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, DUMP."); + } else if (mCrash == CrashType::ON_SHELL_CMD_TRANSACT && + code == SHELL_COMMAND_TRANSACTION) { + LOG_ALWAYS_FATAL("Expected crash, SHELL_CMD."); + } + return BnTestService::onTransact(code, data, reply, flags); + } + private: CrashType mCrash; }; @@ -100,8 +113,10 @@ CrashType gCrashType = CrashType::NONE; extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { if (*argc < 2) { - printf("You must specify at least one argument\n"); - exit(0); // success because this is a crash test + // This fuzzer is also used as test fuzzer to check infra pipeline. + // It should always run and find a crash in TestService. + gCrashType = CrashType::CRASH_ALWAYS; + return 0; } std::string arg = std::string((*argv)[1]); @@ -121,6 +136,10 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { gCrashType = CrashType::ON_ROOT_AID; } else if (arg == "BINDER") { gCrashType = CrashType::ON_BINDER; + } else if (arg == "DUMP") { + gCrashType = CrashType::ON_DUMP_TRANSACT; + } else if (arg == "SHELL_CMD") { + gCrashType = CrashType::ON_SHELL_CMD_TRANSACT; } else { printf("INVALID ARG\n"); exit(0); // success because this is a crash test @@ -130,6 +149,9 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (gCrashType == CrashType::CRASH_ALWAYS) { + LOG_ALWAYS_FATAL("Expected crash, This fuzzer will always crash."); + } auto service = sp<TestService>::make(gCrashType); fuzzService(service, FuzzedDataProvider(data, size)); return 0; diff --git a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh index 25906d8aeb..c447bffbfd 100755 --- a/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh +++ b/libs/binder/tests/parcel_fuzzer/test_fuzzer/run_fuzz_service_test.sh @@ -27,7 +27,7 @@ then exit 1 fi -for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER; do +for CRASH_TYPE in PLAIN KNOWN_UID AID_SYSTEM AID_ROOT BINDER DUMP SHELL_CMD; do echo "INFO: Running fuzzer : test_service_fuzzer_should_crash $CRASH_TYPE" ./test_service_fuzzer_should_crash "$CRASH_TYPE" -max_total_time=30 &>"$FUZZER_OUT" diff --git a/libs/bufferstreams/rust/Android.bp b/libs/bufferstreams/rust/Android.bp index 95a85b550f..ff951487bc 100644 --- a/libs/bufferstreams/rust/Android.bp +++ b/libs/bufferstreams/rust/Android.bp @@ -17,7 +17,8 @@ rust_library { crate_name: "bufferstreams", srcs: ["src/lib.rs"], edition: "2021", - vendor_available: true, - host_supported: true, + rlibs: [ + "libnativewindow_rs", + ], min_sdk_version: "30", } diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs index 51f1c73c49..1d321c833d 100644 --- a/libs/bufferstreams/rust/src/lib.rs +++ b/libs/bufferstreams/rust/src/lib.rs @@ -14,9 +14,143 @@ //! libbufferstreams: Reactive Streams for Graphics Buffers +use nativewindow::*; +use std::sync::{Arc, Weak}; +use std::time::Instant; + /// This function will print Hello World. #[no_mangle] pub extern "C" fn hello() -> bool { println!("Hello world."); true } + +/// BufferPublishers provide buffers to BufferSusbscribers. Depending on the +/// particular object in question, these could be allocated locally or provided +/// over IPC. +/// +/// BufferPublishers are required to adhere to the following, based on the +/// reactive streams specification: +/// * The total number of on_next´s signalled by a Publisher to a Subscriber +/// MUST be less than or equal to the total number of elements requested by that +/// Subscriber´s Subscription at all times. +/// * A Publisher MAY signal fewer on_next than requested and terminate the +/// Subscription by calling on_complete or on_error. +/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber +/// MUST be signaled serially. +/// * If a Publisher fails it MUST signal an on_error. +/// * If a Publisher terminates successfully (finite stream) it MUST signal an +/// on_complete. +/// * If a Publisher signals either on_error or on_complete on a Subscriber, +/// that Subscriber’s Subscription MUST be considered cancelled. +/// * Once a terminal state has been signaled (on_error, on_complete) it is +/// REQUIRED that no further signals occur. +/// * If a Subscription is cancelled its Subscriber MUST eventually stop being +/// signaled. +/// * A Publisher MAY support multiple Subscribers and decides whether each +/// Subscription is unicast or multicast. +pub trait BufferPublisher { + /// This function will create the subscription between the publisher and + /// the subscriber. + fn subscribe(&self, subscriber: Weak<dyn BufferSubscriber>); +} + +/// BufferSubscribers can subscribe to BufferPublishers. They can request Frames +/// via the BufferSubscription they get from the publisher, then receive Frames +/// via on_next. +/// +/// BufferSubcribers are required to adhere to the following, based on the +/// reactive streams specification: +/// * The total number of on_next´s signalled by a Publisher to a Subscriber +/// MUST be less than or equal to the total number of elements requested by that +/// Subscriber´s Subscription at all times. +/// * A Publisher MAY signal fewer on_next than requested and terminate the +/// Subscription by calling on_complete or on_error. +/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber +/// MUST be signaled serially. +/// * If a Publisher fails it MUST signal an on_error. +/// * If a Publisher terminates successfully (finite stream) it MUST signal an +/// on_complete. +/// * If a Publisher signals either on_error or on_complete on a Subscriber, +/// that Subscriber’s Subscription MUST be considered cancelled. +/// * Once a terminal state has been signaled (on_error, on_complete) it is +/// REQUIRED that no further signals occur. +/// * If a Subscription is cancelled its Subscriber MUST eventually stop being +/// signaled. +/// * Publisher.subscribe MAY be called as many times as wanted but MUST be +/// with a different Subscriber each time. +/// * A Publisher MAY support multiple Subscribers and decides whether each +/// Subscription is unicast or multicast. +pub trait BufferSubscriber { + /// This function will be called at the beginning of the subscription. + fn on_subscribe(&self, subscription: Arc<dyn BufferSubscription>); + /// This function will be called for buffer that comes in. + fn on_next(&self, frame: Frame); + /// This function will be called in case of an error. + fn on_error(&self, error: BufferError); + /// This function will be called on finite streams when done. + fn on_complete(&self); +} + +/// BufferSubscriptions serve as the bridge between BufferPublishers and +/// BufferSubscribers. BufferSubscribers receive a BufferSubscription when they +/// subscribe to a BufferPublisher via on_subscribe. +/// This object is to be used by the BufferSubscriber to cancel its subscription +/// or request more buffers. +/// +/// BufferSubcriptions are required to adhere to the following, based on the +/// reactive streams specification: +/// * Subscription.request and Subscription.cancel MUST only be called inside +/// of its Subscriber context. +/// * The Subscription MUST allow the Subscriber to call Subscription.request +/// synchronously from within on_next or on_subscribe. +/// * Subscription.request MUST place an upper bound on possible synchronous +/// recursion between Publisher and Subscriber. +/// * Subscription.request SHOULD respect the responsivity of its caller by +/// returning in a timely manner. +/// * Subscription.cancel MUST respect the responsivity of its caller by +/// returning in a timely manner, MUST be idempotent and MUST be thread-safe. +/// * After the Subscription is cancelled, additional +/// Subscription.request(n: u64) MUST be NOPs. +/// * After the Subscription is cancelled, additional Subscription.cancel() +/// MUST be NOPs. +/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// MUST register the given number of additional elements to be produced to the +/// respective subscriber. +/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// MUST signal on_error if the argument is <= 0. The cause message SHOULD +/// explain that non-positive request signals are illegal. +/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// MAY synchronously call on_next on this (or other) subscriber(s). +/// * While the Subscription is not cancelled, Subscription.request(n: u64) +/// MAY synchronously call on_complete or on_error on this (or other) +/// subscriber(s). +/// * While the Subscription is not cancelled, Subscription.cancel() MUST +/// request the Publisher to eventually stop signaling its Subscriber. The +/// operation is NOT REQUIRED to affect the Subscription immediately. +/// * While the Subscription is not cancelled, Subscription.cancel() MUST +/// request the Publisher to eventually drop any references to the corresponding +/// subscriber. +/// * While the Subscription is not cancelled, calling Subscription.cancel MAY +/// cause the Publisher, if stateful, to transition into the shut-down state if +/// no other Subscription exists at this point. +/// * Calling Subscription.cancel MUST return normally. +/// * Calling Subscription.request MUST return normally. +pub trait BufferSubscription { + /// request + fn request(&self, n: u64); + /// cancel + fn cancel(&self); +} +/// Type used to describe errors produced by subscriptions. +type BufferError = Box<dyn std::error::Error + Send + Sync + 'static>; + +/// Struct used to contain the buffer. +pub struct Frame { + /// A handle to the C buffer interface. + pub buffer: AHardwareBuffer, + /// The time at which the buffer was dispatched. + pub present_time: Instant, + /// A fence used for reading/writing safely. + pub fence: i32, +} diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 732ca36b44..4842476222 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -60,6 +60,17 @@ typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVe typedef bool (*fpANGLEFreeRulesHandle)(void* handle); typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); +namespace { +static bool isVndkEnabled() { +#ifdef __BIONIC__ + // TODO(b/290159430) Use ro.vndk.version to check if VNDK is enabled instead + static bool isVndkEnabled = !android::base::GetBoolProperty("ro.vndk.deprecate", false); + return isVndkEnabled; +#endif + return false; +} +} // namespace + namespace android { enum NativeLibrary { @@ -71,6 +82,8 @@ static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt", "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt"}; +static const char* kLlndkLibrariesTxtPath = "/system/etc/llndk.libraries.txt"; + static std::string vndkVersionStr() { #ifdef __BIONIC__ return base::GetProperty("ro.vndk.version", ""); @@ -108,8 +121,14 @@ static bool readConfig(const std::string& configFile, std::vector<std::string>* } static const std::string getSystemNativeLibraries(NativeLibrary type) { - std::string nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type]; - insertVndkVersionStr(&nativeLibrariesSystemConfig); + std::string nativeLibrariesSystemConfig = ""; + + if (!isVndkEnabled() && type == NativeLibrary::LLNDK) { + nativeLibrariesSystemConfig = kLlndkLibrariesTxtPath; + } else { + nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type]; + insertVndkVersionStr(&nativeLibrariesSystemConfig); + } std::vector<std::string> soNames; if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index d7e7eb8ea1..298838d816 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -62,6 +62,7 @@ filegroup { name: "guiconstants_aidl", srcs: [ "android/gui/DropInputMode.aidl", + "android/gui/StalledTransactionInfo.aidl", "android/**/TouchOcclusionMode.aidl", ], } @@ -140,6 +141,7 @@ aidl_library { "android/gui/IWindowInfosListener.aidl", "android/gui/IWindowInfosPublisher.aidl", "android/gui/IWindowInfosReportedListener.aidl", + "android/gui/StalledTransactionInfo.aidl", "android/gui/WindowInfo.aidl", "android/gui/WindowInfosUpdate.aidl", ], diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 5c324b29cd..207fa4fd31 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -303,13 +303,8 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers // set and then check if it's empty. If there are no more pending syncs, we can // proceed with flushing the shadow queue. - // We also want to check if mSyncTransaction is null because it's possible another - // sync request came in while waiting, but it hasn't started processing yet. In that - // case, we don't actually want to flush the frames in between since they will get - // processed and merged with the sync transaction and released earlier than if they - // were sent to SF mSyncedFrameNumbers.erase(currFrameNumber); - if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) { + if (mSyncedFrameNumbers.empty()) { flushShadowQueue(); } } else { diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index f50bc203e8..e6331e7282 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -24,11 +24,11 @@ #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> -#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define BI_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define BI_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 52172090af..5b34ba12c8 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -47,23 +47,23 @@ namespace android { // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) @@ -298,8 +298,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // decrease. mCore->mDequeueCondition.notify_all(); - ATRACE_INT(mCore->mConsumerName.string(), - static_cast<int32_t>(mCore->mQueue.size())); + ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif @@ -718,7 +717,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); - BQ_LOGV("setConsumerName: '%s'", name.string()); + BQ_LOGV("setConsumerName: '%s'", name.c_str()); std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index 2930154ad4..648db67fc1 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -41,20 +41,20 @@ namespace android { // Macros for include BufferQueueCore information in log messages -#define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGV(x, ...) \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGD(x, ...) \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGI(x, ...) \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGW(x, ...) \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) -#define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \ +#define BQ_LOGE(x, ...) \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), mUniqueId, \ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__) static String8 getUniqueName() { @@ -146,23 +146,23 @@ BufferQueueCore::~BufferQueueCore() {} void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const { std::lock_guard<std::mutex> lock(mMutex); - outResult->appendFormat("%s- BufferQueue ", prefix.string()); + outResult->appendFormat("%s- BufferQueue ", prefix.c_str()); outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n", mMaxAcquiredBufferCount, mMaxDequeuedBufferCount); - outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(), + outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.c_str(), mDequeueBufferCannotBlock, mAsyncMode); - outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(), + outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.c_str(), mQueueBufferCanDrop, mLegacyBufferDrop); - outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(), + outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.c_str(), mDefaultWidth, mDefaultHeight, mDefaultBufferFormat); - outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(), + outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.c_str(), mTransformHint, mFrameCounter); - outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(), + outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.c_str(), mTransformHintInUse, mAutoPrerotation); - outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size()); + outResult->appendFormat("%sFIFO(%zu):\n", prefix.c_str(), mQueue.size()); - outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string()); + outResult->appendFormat("%s(mConsumerName=%s, ", prefix.c_str(), mConsumerName.c_str()); outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi, mConsumerUsageBits); @@ -173,12 +173,11 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const getProcessName(mConnectedPid, producerProcName); getProcessName(pid, consumerProcName); outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId, - mConnectedPid, producerProcName.string(), pid, - consumerProcName.string()); + mConnectedPid, producerProcName.c_str(), pid, consumerProcName.c_str()); Fifo::const_iterator current(mQueue.begin()); while (current != mQueue.end()) { double timestamp = current->mTimestamp / 1e9; - outResult->appendFormat("%s %02d:%p ", prefix.string(), current->mSlot, + outResult->appendFormat("%s %02d:%p ", prefix.c_str(), current->mSlot, current->mGraphicBuffer.get()); outResult->appendFormat("crop=[%d,%d,%d,%d] ", current->mCrop.left, current->mCrop.top, current->mCrop.right, current->mCrop.bottom); @@ -187,12 +186,12 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const ++current; } - outResult->appendFormat("%sSlots:\n", prefix.string()); + outResult->appendFormat("%sSlots:\n", prefix.c_str()); for (int s : mActiveBuffers) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); // A dequeued buffer might be null if it's still being allocated if (buffer.get()) { - outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(), + outResult->appendFormat("%s %s[%02d:%p] ", prefix.c_str(), (mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), @@ -200,14 +199,14 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, buffer->stride, buffer->format); } else { - outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); + outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get()); outResult->appendFormat("state=%-8s frame=%" PRIu64 "\n", mSlots[s].mBufferState.string(), mSlots[s].mFrameNumber); } } for (int s : mFreeBuffers) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - outResult->appendFormat("%s [%02d:%p] ", prefix.string(), s, buffer.get()); + outResult->appendFormat("%s [%02d:%p] ", prefix.c_str(), s, buffer.get()); outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(), buffer->handle, mSlots[s].mFrameNumber); outResult->appendFormat(" [%4ux%4u:%4u,%3X]\n", buffer->width, buffer->height, @@ -216,7 +215,7 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const for (int s : mFreeSlots) { const sp<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer); - outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.string(), s, buffer.get(), + outResult->appendFormat("%s [%02d:%p] state=%-8s\n", prefix.c_str(), s, buffer.get(), mSlots[s].mBufferState.string()); } } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index b872541fec..10f5899b68 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -47,23 +47,23 @@ namespace android { // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ - ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGD(x, ...) \ - ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGI(x, ...) \ - ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGW(x, ...) \ - ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) #define BQ_LOGE(x, ...) \ - ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \ + ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.c_str(), \ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \ ##__VA_ARGS__) @@ -573,9 +573,9 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou if (returnFlags & BUFFER_NEEDS_REALLOCATION) { BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); - sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( - width, height, format, BQ_LAYER_COUNT, usage, - {mConsumerName.string(), mConsumerName.size()}); + sp<GraphicBuffer> graphicBuffer = + new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage, + {mConsumerName.c_str(), mConsumerName.size()}); status_t error = graphicBuffer->initCheck(); @@ -1046,8 +1046,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; - ATRACE_INT(mCore->mConsumerName.string(), - static_cast<int32_t>(mCore->mQueue.size())); + ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size())); #ifndef NO_BINDER mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size()); #endif @@ -1487,7 +1486,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; allocUsage = usage | mCore->mConsumerUsageBits; - allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size()); + allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size()); mCore->mIsAllocating = true; } // Autolock scope @@ -1589,7 +1588,7 @@ status_t BufferQueueProducer::setGenerationNumber(uint32_t generationNumber) { String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mCore->mMutex); - BQ_LOGV("getConsumerName: %s", mConsumerName.string()); + BQ_LOGV("getConsumerName: %s", mConsumerName.c_str()); return mConsumerName; } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index 9f91d9d3aa..b625c3f75e 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -41,11 +41,11 @@ #include <utils/Trace.h> // Macros for including the ConsumerBase name in log messages -#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CB_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CB_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { @@ -86,8 +86,10 @@ ConsumerBase::~ConsumerBase() { // be done by ConsumerBase::onLastStrongRef(), but it's possible for a // derived class to override that method and not call // ConsumerBase::onLastStrongRef(). - LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the " - "consumer is not abandoned!", mName.string()); + LOG_ALWAYS_FATAL_IF(!mAbandoned, + "[%s] ~ConsumerBase was called, but the " + "consumer is not abandoned!", + mName.c_str()); } void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { @@ -451,7 +453,7 @@ status_t ConsumerBase::addReleaseFenceLocked(int slot, // them to get an accurate timestamp. if (currentStatus == incomingStatus) { char fenceName[32] = {}; - snprintf(fenceName, 32, "%.28s:%d", mName.string(), slot); + snprintf(fenceName, 32, "%.28s:%d", mName.c_str(), slot); sp<Fence> mergedFence = Fence::merge( fenceName, mSlots[slot].mFence, fence); if (!mergedFence.get()) { diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index a62697064b..3031fa11fc 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -23,11 +23,11 @@ #include <gui/BufferItem.h> #include <utils/Log.h> -#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define CC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define CC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index b3647d6126..d49489c5a8 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -52,11 +52,11 @@ namespace android { // Macros for including the GLConsumer name in log messages -#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -//#define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define GLC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +// #define GLC_LOGI(x, ...) ALOGI("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define GLC_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) static const struct { uint32_t width, height; diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 826a418aab..070f6bf532 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1075131 + chrisforbes@google.com jreck@google.com diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3c508c70fe..dd1fad0f6b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -59,7 +59,7 @@ #include <private/gui/ComposerServiceAIDL.h> // This server size should always be smaller than the server cache size -#define BUFFER_CACHE_MAX_SIZE 64 +#define BUFFER_CACHE_MAX_SIZE 4096 namespace android { @@ -450,7 +450,9 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener callbackFunction(transactionStats.latchTime, transactionStats.presentFence, surfaceControlStats); } + } + for (const auto& transactionStats : listenerStats.transactionStats) { for (const auto& surfaceStats : transactionStats.surfaceStats) { // The callbackMap contains the SurfaceControl object, which we need to look up the // layerId. Since we don't know which callback contains the SurfaceControl, iterate @@ -1273,7 +1275,7 @@ sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, boo sp<IBinder> display = nullptr; binder::Status status = ComposerServiceAIDL::getComposerService()->createDisplay(std::string( - displayName.string()), + displayName.c_str()), secure, requestedRefereshRate, &display); return status.isOk() ? display : nullptr; @@ -1306,6 +1308,13 @@ sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId dis return status.isOk() ? display : nullptr; } +std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTransactionInfo( + pid_t pid) { + std::optional<gui::StalledTransactionInfo> result; + ComposerServiceAIDL::getComposerService()->getStalledTransactionInfo(pid, &result); + return result; +} + void SurfaceComposerClient::Transaction::setAnimationTransaction() { mAnimation = true; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index 539a1c140e..e1726b7f34 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -46,6 +46,7 @@ import android.gui.LayerDebugInfo; import android.gui.OverlayProperties; import android.gui.PullAtomData; import android.gui.ARect; +import android.gui.StalledTransactionInfo; import android.gui.StaticDisplayInfo; import android.gui.WindowInfosListenerInfo; @@ -230,20 +231,20 @@ interface ISurfaceComposer { * The subregion can be optionally rotated. It will also be scaled to * match the size of the output buffer. */ - void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener); + oneway void captureDisplay(in DisplayCaptureArgs args, IScreenCaptureListener listener); /** * Capture the specified screen. This requires the READ_FRAME_BUFFER * permission. */ - void captureDisplayById(long displayId, IScreenCaptureListener listener); + oneway void captureDisplayById(long displayId, IScreenCaptureListener listener); /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ - void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); + oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); /** * Clears the frame statistics for animations. @@ -280,8 +281,6 @@ interface ISurfaceComposer { */ List<LayerDebugInfo> getLayerDebugInfo(); - boolean getColorManagement(); - /** * Gets the composition preference of the default data space and default pixel format, * as well as the wide color gamut data space and wide color gamut pixel format. @@ -507,4 +506,10 @@ interface ISurfaceComposer { void removeWindowInfosListener(IWindowInfosListener windowInfosListener); OverlayProperties getOverlaySupport(); + + /** + * Returns an instance of StalledTransaction if a transaction from the passed pid has not been + * applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned. + */ + @nullable StalledTransactionInfo getStalledTransactionInfo(int pid); } diff --git a/libs/renderengine/mock/Framebuffer.cpp b/libs/gui/android/gui/StalledTransactionInfo.aidl index fbdcaab697..e6aa9bd1c7 100644 --- a/libs/renderengine/mock/Framebuffer.cpp +++ b/libs/gui/android/gui/StalledTransactionInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,11 @@ * limitations under the License. */ -#include <renderengine/mock/Framebuffer.h> +package android.gui; -namespace android { -namespace renderengine { -namespace mock { - -// The Google Mock documentation recommends explicit non-header instantiations -// for better compile time performance. -Framebuffer::Framebuffer() = default; -Framebuffer::~Framebuffer() = default; - -} // namespace mock -} // namespace renderengine -} // namespace android +/** @hide */ +parcelable StalledTransactionInfo { + String layerName; + long bufferId; + long frameNumber; +}
\ No newline at end of file diff --git a/libs/gui/fuzzer/Android.bp b/libs/gui/fuzzer/Android.bp index 073cc08bff..cd738acde2 100644 --- a/libs/gui/fuzzer/Android.bp +++ b/libs/gui/fuzzer/Android.bp @@ -24,6 +24,7 @@ package { cc_defaults { name: "libgui_fuzzer_defaults", + defaults: ["android.hardware.power-ndk_shared"], static_libs: [ "android.hidl.token@1.0-utils", "libbinder_random_parcel", @@ -46,7 +47,6 @@ cc_defaults { "android.hardware.configstore-utils", "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", - "android.hardware.power-V4-ndk", "android.hidl.token@1.0", "libSurfaceFlingerProp", "libgui", diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index 4c7d0562af..a3816873a7 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -110,7 +110,6 @@ public: (override)); MOCK_METHOD(binder::Status, onPullAtom, (int32_t, gui::PullAtomData*), (override)); MOCK_METHOD(binder::Status, getLayerDebugInfo, (std::vector<gui::LayerDebugInfo>*), (override)); - MOCK_METHOD(binder::Status, getColorManagement, (bool*), (override)); MOCK_METHOD(binder::Status, getCompositionPreference, (gui::CompositionPreference*), (override)); MOCK_METHOD(binder::Status, getDisplayedContentSamplingAttributes, @@ -158,6 +157,8 @@ public: MOCK_METHOD(binder::Status, removeWindowInfosListener, (const sp<gui::IWindowInfosListener>&), (override)); MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override)); + MOCK_METHOD(binder::Status, getStalledTransactionInfo, + (int32_t, std::optional<gui::StalledTransactionInfo>*), (override)); }; class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient { diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 8d0828d88e..22c2be7bc7 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -34,13 +34,13 @@ #include <mutex> #include <condition_variable> -#define ATRACE_BUFFER_INDEX(index) \ - do { \ - if (ATRACE_ENABLED()) { \ - char ___traceBuf[1024]; \ - snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \ - android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ - } \ +#define ATRACE_BUFFER_INDEX(index) \ + do { \ + if (ATRACE_ENABLED()) { \ + char ___traceBuf[1024]; \ + snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.c_str(), (index)); \ + android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ + } \ } while (false) namespace android { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 3cf57b11e9..dbcbd3bd62 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -371,6 +371,10 @@ public: //! Get token for a physical display given its stable ID static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId); + // Returns StalledTransactionInfo if a transaction from the provided pid has not been applied + // due to an unsignaled fence. + static std::optional<gui::StalledTransactionInfo> getStalledTransactionInfo(pid_t pid); + struct SCHash { std::size_t operator()(const sp<SurfaceControl>& sc) const { return std::hash<SurfaceControl *>{}(sc.get()); diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h index 004d87574a..32dc88ba84 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h @@ -298,7 +298,7 @@ struct TWGraphicBufferProducer : public BASE { } Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override { - _hidl_cb(mBase->getConsumerName().string()); + _hidl_cb(mBase->getConsumerName().c_str()); return Void(); } diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index afeea42546..ae79e5bace 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -177,7 +177,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, while ((err = glGetError()) != GL_NO_ERROR) { msg += String8::format(", %#x", err); } - return ::testing::AssertionFailure(::testing::Message(msg.string())); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { msg += String8::format("r(%d isn't %d)", pixel[0], r); @@ -201,7 +201,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, msg += String8::format("a(%d isn't %d)", pixel[3], a); } if (!msg.isEmpty()) { - return ::testing::AssertionFailure(::testing::Message(msg.string())); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } else { return ::testing::AssertionSuccess(); } @@ -236,8 +236,8 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom); - fprintf(stderr, "assertRectEq: %s\n", msg.string()); - return ::testing::AssertionFailure(::testing::Message(msg.string())); + fprintf(stderr, "assertRectEq: %s\n", msg.c_str()); + return ::testing::AssertionFailure(::testing::Message(msg.c_str())); } else { return ::testing::AssertionSuccess(); } diff --git a/libs/gui/tests/OWNERS b/libs/gui/tests/OWNERS index 156efdb883..48cd30d39d 100644 --- a/libs/gui/tests/OWNERS +++ b/libs/gui/tests/OWNERS @@ -1,3 +1,6 @@ # Android > Android OS & Apps > Framework (Java + Native) > Window Manager > Surfaces # Bug component: 316245 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp # Buganizer template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file BLASTBufferQueue_test.cpp, DisplayInfo_test.cpp, EndToEndNativeInputTest.cpp, WindowInfos_test.cpp + +# Android > Android OS & Apps > graphics > Core Graphics Stack (CoGS) +# Bug component: 1075130 diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index f34561f668..ccd0e59616 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -59,7 +59,7 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < 4; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); + SCOPED_TRACE(String8::format("frame %d", i).c_str()); ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index e2b4f3d035..f76c0be265 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -147,8 +147,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { for (int i = 0; i < 5; i++) { const android_native_rect_t& crop(crops[i]); - SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", - crop.left, crop.top, crop.right, crop.bottom).string()); + SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, crop.top, + crop.right, crop.bottom) + .c_str()); ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); @@ -308,7 +309,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { mFW->waitForFrame(); for (int i = 0; i < numFrames; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); + SCOPED_TRACE(String8::format("frame %d", i).c_str()); // We must wait for each frame to come in because if we ever do an // updateTexImage call that doesn't consume a newly available buffer diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 567604dbec..e89998ff3d 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -415,7 +415,7 @@ TEST_F(SurfaceTest, GetConsumerName) { sp<ANativeWindow> window(surface); native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); - EXPECT_STREQ("TestConsumer", surface->getConsumerName().string()); + EXPECT_STREQ("TestConsumer", surface->getConsumerName().c_str()); } TEST_F(SurfaceTest, GetWideColorSupport) { @@ -879,10 +879,6 @@ public: return binder::Status::ok(); } - binder::Status getColorManagement(bool* /*outGetColorManagement*/) override { - return binder::Status::ok(); - } - binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override { return binder::Status::ok(); } @@ -1016,6 +1012,11 @@ public: return binder::Status::ok(); } + binder::Status getStalledTransactionInfo( + int32_t /*pid*/, std::optional<gui::StalledTransactionInfo>* /*result*/) override { + return binder::Status::ok(); + } + protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp index 341eb6f920..6c602e031d 100644 --- a/libs/input/InputVerifier.cpp +++ b/libs/input/InputVerifier.cpp @@ -31,7 +31,7 @@ namespace android { // --- InputVerifier --- InputVerifier::InputVerifier(const std::string& name) - : mVerifier(android::input::verifier::create(name)){}; + : mVerifier(android::input::verifier::create(rust::String::lossy(name))){}; Result<void> InputVerifier::processMovement(DeviceId deviceId, int32_t action, uint32_t pointerCount, diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 12c9e533c3..d571917ff9 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -140,7 +140,7 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) { #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); + tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (status != OK) { ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str()); @@ -297,7 +297,7 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t if (!findKey(ch, &keyCode, &metaState)) { #if DEBUG_MAPPING ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", - deviceId, toString(chars, numChars).string(), ch); + deviceId, toString(chars, numChars).c_str(), ch); #endif return false; } @@ -309,8 +309,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); } #if DEBUG_MAPPING - ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", - deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); + ALOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", deviceId, + toString(chars, numChars).c_str(), int32_t(outEvents.size())); for (size_t i = 0; i < outEvents.size(); i++) { ALOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", outEvents[i].getKeyCode(), outEvents[i].getMetaState(), @@ -756,8 +756,8 @@ KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Form status_t KeyCharacterMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -779,8 +779,8 @@ status_t KeyCharacterMap::Parser::parse() { status_t status = parseKey(); if (status) return status; } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } break; @@ -795,10 +795,9 @@ status_t KeyCharacterMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", + mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str()); + return BAD_VALUE; } } @@ -807,27 +806,27 @@ status_t KeyCharacterMap::Parser::parse() { if (mState != STATE_TOP) { ALOGE("%s: Unterminated key description at end of file.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (mMap->mType == KeyboardType::UNKNOWN) { ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (mFormat == Format::BASE) { if (mMap->mType == KeyboardType::OVERLAY) { ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } else if (mFormat == Format::OVERLAY) { if (mMap->mType != KeyboardType::OVERLAY) { ALOGE("%s: Overlay keyboard layout missing required keyboard " - "'type OVERLAY' declaration.", - mTokenizer->getLocation().string()); + "'type OVERLAY' declaration.", + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -837,8 +836,7 @@ status_t KeyCharacterMap::Parser::parse() { status_t KeyCharacterMap::Parser::parseType() { if (mMap->mType != KeyboardType::UNKNOWN) { - ALOGE("%s: Duplicate keyboard 'type' declaration.", - mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -860,8 +858,8 @@ status_t KeyCharacterMap::Parser::parseType() { } else if (typeToken == "OVERLAY") { type = KeyboardType::OVERLAY; } else { - ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), - typeToken.string()); + ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().c_str(), + typeToken.c_str()); return BAD_VALUE; } @@ -878,8 +876,8 @@ status_t KeyCharacterMap::Parser::parseMap() { mTokenizer->skipDelimiters(WHITESPACE); return parseMapKey(); } - ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } @@ -893,26 +891,26 @@ status_t KeyCharacterMap::Parser::parseMapKey() { } char* end; - int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + int32_t code = int32_t(strtol(codeToken.c_str(), &end, 0)); if (*end) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; const auto it = map.find(code); if (it != map.end()) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } @@ -926,23 +924,23 @@ status_t KeyCharacterMap::Parser::parseMapKey() { status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } if (mMap->mKeys.find(*keyCode) != mMap->mKeys.end()) { - ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); if (openBraceToken != "{") { - ALOGE("%s: Expected '{' after key code label, got '%s'.", - mTokenizer->getLocation().string(), openBraceToken.string()); + ALOGE("%s: Expected '{' after key code label, got '%s'.", mTokenizer->getLocation().c_str(), + openBraceToken.c_str()); return BAD_VALUE; } @@ -971,10 +969,10 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { properties.emplace_back(PROPERTY_NUMBER); } else { int32_t metaState; - status_t status = parseModifier(token.string(), &metaState); + status_t status = parseModifier(token.c_str(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return status; } properties.emplace_back(PROPERTY_META, metaState); @@ -992,8 +990,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } } - ALOGE("%s: Expected ',' or ':' after property name.", - mTokenizer->getLocation().string()); + ALOGE("%s: Expected ',' or ':' after property name.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -1011,18 +1008,17 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { char16_t character; status_t status = parseCharacterLiteral(&character); if (status || !character) { - ALOGE("%s: Invalid character literal for key.", - mTokenizer->getLocation().string()); + ALOGE("%s: Invalid character literal for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine character literal with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.character = character; @@ -1032,28 +1028,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { if (token == "none") { if (haveCharacter) { ALOGE("%s: Cannot combine multiple character literals or 'none'.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveReplacement) { ALOGE("%s: Cannot combine 'none' with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } haveCharacter = true; } else if (token == "fallback") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str()); if (!keyCode) { ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.fallbackKeyCode = *keyCode; @@ -1061,29 +1056,27 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "replace") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.c_str()); if (!keyCode) { ALOGE("%s: Invalid key code label for replace, got '%s'.", - mTokenizer->getLocation().string(), - token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } if (haveCharacter) { ALOGE("%s: Cannot combine character literal with replace action.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } if (haveFallback || haveReplacement) { ALOGE("%s: Cannot combine multiple fallback/replacement key codes.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } behavior.replacementKeyCode = *keyCode; haveReplacement = true; } else { - ALOGE("%s: Expected a key behavior after ':'.", - mTokenizer->getLocation().string()); + ALOGE("%s: Expected a key behavior after ':'.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -1096,7 +1089,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { switch (property.property) { case PROPERTY_LABEL: if (key.label) { - ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate label for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } key.label = behavior.character; @@ -1106,7 +1099,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { break; case PROPERTY_NUMBER: if (key.number) { - ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().string()); + ALOGE("%s: Duplicate number for key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } key.number = behavior.character; @@ -1118,7 +1111,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { for (const Behavior& b : key.behaviors) { if (b.metaState == property.metaState) { ALOGE("%s: Duplicate key behavior for modifier.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } } @@ -1185,8 +1178,8 @@ status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_ return BAD_VALUE; } if (combinedMeta & metaState) { - ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.c_str()); + ALOGE("%s: Duplicate modifier combination '%s'.", mTokenizer->getLocation().c_str(), + token.c_str()); return BAD_VALUE; } @@ -1259,7 +1252,7 @@ status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) } Error: - ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + ALOGE("%s: Malformed character literal.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index a194513953..ddc9ea457e 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -177,7 +177,7 @@ base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokeni #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), + tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif if (!status) { @@ -306,8 +306,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { - ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); mTokenizer->skipDelimiters(WHITESPACE); @@ -334,16 +334,15 @@ status_t KeyLayoutMap::Parser::parse() { status_t status = parseRequiredKernelConfig(); if (status) return status; } else { - ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), - keywordToken.string()); + ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { ALOGE("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + mTokenizer->getLocation().c_str(), mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } } @@ -362,26 +361,26 @@ status_t KeyLayoutMap::Parser::parseKey() { codeToken = mTokenizer->nextToken(WHITESPACE); } - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } std::unordered_map<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; if (map.find(*code) != map.end()) { - ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); + std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); if (!keyCode) { - ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), - keyCodeToken.string()); + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().c_str(), + keyCodeToken.c_str()); return BAD_VALUE; } @@ -391,15 +390,15 @@ status_t KeyLayoutMap::Parser::parseKey() { if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string()); + std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str()); if (!flag) { - ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); + ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(), + flagToken.c_str()); return BAD_VALUE; } if (flags & *flag) { - ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), - flagToken.string()); + ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(), + flagToken.c_str()); return BAD_VALUE; } flags |= *flag; @@ -417,15 +416,15 @@ status_t KeyLayoutMap::Parser::parseKey() { status_t KeyLayoutMap::Parser::parseAxis() { String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> scanCode = parseInt(scanCodeToken.string()); + std::optional<int> scanCode = parseInt(scanCodeToken.c_str()); if (!scanCode) { - ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(), + scanCodeToken.c_str()); return BAD_VALUE; } if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) { - ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(), + scanCodeToken.c_str()); return BAD_VALUE; } @@ -438,10 +437,10 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 axisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.c_str()); if (!axis) { ALOGE("%s: Expected inverted axis label, got '%s'.", - mTokenizer->getLocation().string(), axisToken.string()); + mTokenizer->getLocation().c_str(), axisToken.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; @@ -450,38 +449,38 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 splitToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> splitValue = parseInt(splitToken.string()); + std::optional<int> splitValue = parseInt(splitToken.c_str()); if (!splitValue) { ALOGE("%s: Expected split value, got '%s'.", - mTokenizer->getLocation().string(), splitToken.string()); + mTokenizer->getLocation().c_str(), splitToken.c_str()); return BAD_VALUE; } axisInfo.splitValue = *splitValue; mTokenizer->skipDelimiters(WHITESPACE); String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str()); if (!axis) { ALOGE("%s: Expected low axis label, got '%s'.", - mTokenizer->getLocation().string(), lowAxisToken.string()); + mTokenizer->getLocation().c_str(), lowAxisToken.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; mTokenizer->skipDelimiters(WHITESPACE); String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string()); + std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str()); if (!highAxis) { ALOGE("%s: Expected high axis label, got '%s'.", - mTokenizer->getLocation().string(), highAxisToken.string()); + mTokenizer->getLocation().c_str(), highAxisToken.c_str()); return BAD_VALUE; } axisInfo.highAxis = *highAxis; } else { - std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string()); + std::optional<int> axis = InputEventLookup::getAxisByLabel(token.c_str()); if (!axis) { ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().c_str(), token.c_str()); return BAD_VALUE; } axisInfo.axis = *axis; @@ -496,16 +495,16 @@ status_t KeyLayoutMap::Parser::parseAxis() { if (keywordToken == "flat") { mTokenizer->skipDelimiters(WHITESPACE); String8 flatToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> flatOverride = parseInt(flatToken.string()); + std::optional<int> flatOverride = parseInt(flatToken.c_str()); if (!flatOverride) { ALOGE("%s: Expected flat value, got '%s'.", - mTokenizer->getLocation().string(), flatToken.string()); + mTokenizer->getLocation().c_str(), flatToken.c_str()); return BAD_VALUE; } axisInfo.flatOverride = *flatOverride; } else { - ALOGE("%s: Expected keyword 'flat', got '%s'.", - mTokenizer->getLocation().string(), keywordToken.string()); + ALOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(), + keywordToken.c_str()); return BAD_VALUE; } } @@ -527,27 +526,27 @@ status_t KeyLayoutMap::Parser::parseLed() { mTokenizer->skipDelimiters(WHITESPACE); codeToken = mTokenizer->nextToken(WHITESPACE); } - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } std::unordered_map<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; if (map.find(*code) != map.end()) { - ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), - mapUsage ? "usage" : "scan code", codeToken.string()); + ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().c_str(), + mapUsage ? "usage" : "scan code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string()); + std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.c_str()); if (!ledCode) { - ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), - ledCodeToken.string()); + ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().c_str(), + ledCodeToken.c_str()); return BAD_VALUE; } @@ -569,7 +568,7 @@ static std::optional<InputDeviceSensorType> getSensorType(const char* token) { } static std::optional<int32_t> getSensorDataIndex(String8 token) { - std::string tokenStr(token.string()); + std::string tokenStr(token.c_str()); if (tokenStr == "X") { return 0; } else if (tokenStr == "Y") { @@ -594,26 +593,26 @@ static std::optional<int32_t> getSensorDataIndex(String8 token) { // sensor 0x05 GYROSCOPE Z status_t KeyLayoutMap::Parser::parseSensor() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<int> code = parseInt(codeToken.string()); + std::optional<int> code = parseInt(codeToken.c_str()); if (!code) { - ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(), - "abs code", codeToken.string()); + ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().c_str(), + "abs code", codeToken.c_str()); return BAD_VALUE; } std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode; if (map.find(*code) != map.end()) { - ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(), - "abs code", codeToken.string()); + ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().c_str(), + "abs code", codeToken.c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); String8 sensorTypeToken = mTokenizer->nextToken(WHITESPACE); - std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.string()); + std::optional<InputDeviceSensorType> typeOpt = getSensorType(sensorTypeToken.c_str()); if (!typeOpt) { - ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().string(), - sensorTypeToken.string()); + ALOGE("%s: Expected sensor code label, got '%s'.", mTokenizer->getLocation().c_str(), + sensorTypeToken.c_str()); return BAD_VALUE; } InputDeviceSensorType sensorType = typeOpt.value(); @@ -621,8 +620,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { String8 sensorDataIndexToken = mTokenizer->nextToken(WHITESPACE); std::optional<int32_t> indexOpt = getSensorDataIndex(sensorDataIndexToken); if (!indexOpt) { - ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().string(), - sensorDataIndexToken.string()); + ALOGE("%s: Expected sensor data index label, got '%s'.", mTokenizer->getLocation().c_str(), + sensorDataIndexToken.c_str()); return BAD_VALUE; } int32_t sensorDataIndex = indexOpt.value(); @@ -643,12 +642,12 @@ status_t KeyLayoutMap::Parser::parseSensor() { // requires_kernel_config CONFIG_HID_PLAYSTATION status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::string configName = codeToken.string(); + std::string configName = codeToken.c_str(); const auto result = mMap->mRequiredKernelConfigs.emplace(configName); if (!result.second) { ALOGE("%s: Duplicate entry for required kernel config %s.", - mTokenizer->getLocation().string(), configName.c_str()); + mTokenizer->getLocation().c_str(), configName.c_str()); return BAD_VALUE; } diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index 548f894d22..315f5a6d4f 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -163,8 +163,8 @@ PropertyMap::Parser::~Parser() {} status_t PropertyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -172,7 +172,7 @@ status_t PropertyMap::Parser::parse() { if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); if (keyToken.isEmpty()) { - ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -180,7 +180,7 @@ status_t PropertyMap::Parser::parse() { if (mTokenizer->nextChar() != '=') { ALOGE("%s: Expected '=' between property key and value.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -189,20 +189,20 @@ status_t PropertyMap::Parser::parse() { String8 valueToken = mTokenizer->nextToken(WHITESPACE); if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { ALOGE("%s: Found reserved character '\\' or '\"' in property value.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } if (mMap->hasProperty(keyToken.string())) { ALOGE("%s: Duplicate property value for key '%s'.", - mTokenizer->getLocation().string(), keyToken.string()); + mTokenizer->getLocation().c_str(), keyToken.c_str()); return BAD_VALUE; } diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 8704eee73d..116b778608 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "VelocityTracker" #include <android-base/logging.h> +#include <ftl/enum.h> #include <inttypes.h> #include <limits.h> #include <math.h> @@ -148,27 +149,19 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r VelocityTracker::VelocityTracker(const Strategy strategy) : mLastEventTime(0), mCurrentPointerIdBits(0), mOverrideStrategy(strategy) {} -VelocityTracker::~VelocityTracker() { -} - bool VelocityTracker::isAxisSupported(int32_t axis) { return DEFAULT_STRATEGY_BY_AXIS.find(axis) != DEFAULT_STRATEGY_BY_AXIS.end(); } void VelocityTracker::configureStrategy(int32_t axis) { const bool isDifferentialAxis = DIFFERENTIAL_AXES.find(axis) != DIFFERENTIAL_AXES.end(); - - std::unique_ptr<VelocityTrackerStrategy> createdStrategy; - if (mOverrideStrategy != VelocityTracker::Strategy::DEFAULT) { - createdStrategy = createStrategy(mOverrideStrategy, /*deltaValues=*/isDifferentialAxis); + if (isDifferentialAxis || mOverrideStrategy == VelocityTracker::Strategy::DEFAULT) { + // Do not allow overrides of strategies for differential axes, for now. + mConfiguredStrategies[axis] = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis), + /*deltaValues=*/isDifferentialAxis); } else { - createdStrategy = createStrategy(DEFAULT_STRATEGY_BY_AXIS.at(axis), - /*deltaValues=*/isDifferentialAxis); + mConfiguredStrategies[axis] = createStrategy(mOverrideStrategy, /*deltaValues=*/false); } - - LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr, - "Could not create velocity tracker strategy for axis '%" PRId32 "'!", axis); - mConfiguredStrategies[axis] = std::move(createdStrategy); } std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( @@ -216,6 +209,9 @@ std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( default: break; } + LOG(FATAL) << "Invalid strategy: " << ftl::enum_string(strategy) + << ", deltaValues = " << deltaValues; + return nullptr; } @@ -272,12 +268,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t mConfiguredStrategies[axis]->addMovement(eventTime, pointerId, position); if (DEBUG_VELOCITY) { - ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", pointerId=%" PRId32 - ", activePointerId=%s", - eventTime, pointerId, toString(mActivePointerId).c_str()); - - ALOGD(" %d: axis=%d, position=%0.3f, velocity=%s", pointerId, axis, position, - toString(getVelocity(axis, pointerId)).c_str()); + LOG(INFO) << "VelocityTracker: addMovement axis=" << MotionEvent::getLabel(axis) + << ", eventTime=" << eventTime << ", pointerId=" << pointerId + << ", activePointerId=" << toString(mActivePointerId) << ", position=" << position + << ", velocity=" << toString(getVelocity(axis, pointerId)); } } diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 865366bcb2..de62c870ff 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -79,8 +79,8 @@ VirtualKeyMap::Parser::~Parser() { status_t VirtualKeyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); #endif mTokenizer->skipDelimiters(WHITESPACE); @@ -91,7 +91,7 @@ status_t VirtualKeyMap::Parser::parse() { String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); if (token != "0x01") { ALOGE("%s: Unknown virtual key type, expected 0x01.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -103,7 +103,7 @@ status_t VirtualKeyMap::Parser::parse() { && parseNextIntField(&defn.height); if (!success) { ALOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", - mTokenizer->getLocation().string()); + mTokenizer->getLocation().c_str()); return BAD_VALUE; } @@ -116,9 +116,8 @@ status_t VirtualKeyMap::Parser::parse() { } while (consumeFieldDelimiterAndSkipWhitespace()); if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().c_str(), + mTokenizer->peekRemainderOfLine().c_str()); return BAD_VALUE; } } @@ -146,9 +145,9 @@ bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); char* end; - *outValue = strtol(token.string(), &end, 0); + *outValue = strtol(token.c_str(), &end, 0); if (token.isEmpty() || *end != '\0') { - ALOGE("Expected an integer, got '%s'.", token.string()); + ALOGE("Expected an integer, got '%s'.", token.c_str()); return false; } return true; diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index dab843b48f..8f6f95b7d1 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -57,4 +57,88 @@ interface IInputConstants /* The default pointer acceleration value. */ const int DEFAULT_POINTER_ACCELERATION = 3; + + /** + * Use the default Velocity Tracker Strategy. Different axes may use different default + * strategies. + */ + const int VELOCITY_TRACKER_STRATEGY_DEFAULT = -1; + + /** + * Velocity Tracker Strategy: Impulse. + * Physical model of pushing an object. Quality: VERY GOOD. + * Works with duplicate coordinates, unclean finger liftoff. + */ + const int VELOCITY_TRACKER_STRATEGY_IMPULSE = 0; + + /** + * Velocity Tracker Strategy: LSQ1. + * 1st order least squares. Quality: POOR. + * Frequently underfits the touch data especially when the finger accelerates + * or changes direction. Often underestimates velocity. The direction + * is overly influenced by historical touch points. + */ + const int VELOCITY_TRACKER_STRATEGY_LSQ1 = 1; + + /** + * Velocity Tracker Strategy: LSQ2. + * 2nd order least squares. Quality: VERY GOOD. + * Pretty much ideal, but can be confused by certain kinds of touch data, + * particularly if the panel has a tendency to generate delayed, + * duplicate or jittery touch coordinates when the finger is released. + */ + const int VELOCITY_TRACKER_STRATEGY_LSQ2 = 2; + + /** + * Velocity Tracker Strategy: LSQ3. + * 3rd order least squares. Quality: UNUSABLE. + * Frequently overfits the touch data yielding wildly divergent estimates + * of the velocity when the finger is released. + */ + const int VELOCITY_TRACKER_STRATEGY_LSQ3 = 3; + + /** + * Velocity Tracker Strategy: WLSQ2_DELTA. + * 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL + */ + const int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 4; + + /** + * Velocity Tracker Strategy: WLSQ2_CENTRAL. + * 2nd order weighted least squares, central weighting. Quality: EXPERIMENTALe + */ + const int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 5; + + /** + * Velocity Tracker Strategy: WLSQ2_RECENT. + * 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL + */ + const int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 6; + + /** + * Velocity Tracker Strategy: INT1. + * 1st order integrating filter. Quality: GOOD. + * Not as good as 'lsq2' because it cannot estimate acceleration but it is + * more tolerant of errors. Like 'lsq1', this strategy tends to underestimate + * the velocity of a fling but this strategy tends to respond to changes in + * direction more quickly and accurately. + */ + const int VELOCITY_TRACKER_STRATEGY_INT1 = 7; + + /** + * Velocity Tracker Strategy: INT2. + * 2nd order integrating filter. Quality: EXPERIMENTAL. + * For comparison purposes only. Unlike 'int1' this strategy can compensate + * for acceleration but it typically overestimates the effect. + */ + const int VELOCITY_TRACKER_STRATEGY_INT2 = 8; + + /** + * Velocity Tracker Strategy: Legacy. + * Legacy velocity tracker algorithm. Quality: POOR. + * For comparison purposes only. This algorithm is strongly influenced by + * old data points, consistently underestimates velocity and takes a very long + * time to adjust to changes in direction. + */ + const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9; } diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 688d941bf6..1d3c434f76 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -23,6 +23,7 @@ pub use input::{DeviceId, MotionAction, MotionFlags}; pub use input_verifier::InputVerifier; #[cxx::bridge(namespace = "android::input")] +#[allow(unsafe_op_in_unsafe_fn)] mod ffi { #[namespace = "android"] unsafe extern "C++" { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index cadac88030..86b996b3b6 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -18,6 +18,7 @@ cc_test { "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "RingBuffer_test.cpp", "TfLiteMotionPredictor_test.cpp", diff --git a/libs/renderengine/mock/Image.cpp b/libs/input/tests/InputVerifier_test.cpp index 57f4346f8f..e24fa6ed0b 100644 --- a/libs/renderengine/mock/Image.cpp +++ b/libs/input/tests/InputVerifier_test.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,16 @@ * limitations under the License. */ -#include <renderengine/mock/Image.h> +#include <gtest/gtest.h> +#include <input/InputVerifier.h> +#include <string> namespace android { -namespace renderengine { -namespace mock { -// The Google Mock documentation recommends explicit non-header instantiations -// for better compile time performance. -Image::Image() = default; -Image::~Image() = default; +TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) { + constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)}; + const std::string name(bytes, sizeof(bytes)); + InputVerifier verifier(name); +} -} // namespace mock -} // namespace renderengine } // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index ffebff1e65..1c8ec90373 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -278,6 +278,11 @@ static void computeAndCheckAxisScrollVelocity( const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, std::optional<float> targetVelocity) { checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); + // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall + // back to a strategy that supports differential axes. + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions, + AMOTION_EVENT_AXIS_SCROLL), + targetVelocity); } static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions, diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 0128859ca6..275b7a4888 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -38,10 +38,10 @@ namespace android { // Macros for including the SurfaceTexture name in log messages -#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) -#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) static const struct { uint32_t width, height; diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index cf16739e89..32b229d77c 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -19,7 +19,7 @@ #include <surfacetexture/SurfaceTexture.h> // Macro for including the SurfaceTexture name in log messages -#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.c_str(), ##__VA_ARGS__) namespace android { diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index d3d4cbafdf..9f610e1a50 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -26,10 +26,10 @@ namespace android { // Macros for including the SurfaceTexture name in log messages -#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) -#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.c_str(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.c_str(), ##__VA_ARGS__) static const mat4 mtxIdentity; diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index a5bcc6293a..a2ec57cd3c 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -61,6 +61,7 @@ impl AHardwareBuffer { /// program termination. /// /// Available since API level 26. + #[inline] pub fn new( width: u32, height: u32, @@ -199,6 +200,7 @@ mod ahardwarebuffer_tests { #[test] #[should_panic] fn take_from_raw_panics_on_null() { + // SAFETY: Passing a null pointer is safe, it should just panic. unsafe { AHardwareBuffer::take_from_raw(ptr::null_mut()) }; } @@ -216,9 +218,13 @@ mod ahardwarebuffer_tests { }; let mut raw_buffer_ptr = ptr::null_mut(); + // SAFETY: The pointers are valid because they come from references, and + // `AHardwareBuffer_allocate` doesn't retain them after it returns. let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut raw_buffer_ptr) }; assert_eq!(status, 0); + // SAFETY: The pointer must be valid because it was just allocated successfully, and we + // don't use it after calling this. let buffer = unsafe { AHardwareBuffer::take_from_raw(raw_buffer_ptr as *mut c_void) }; assert_eq!(buffer.width(), 1024); } diff --git a/libs/nativewindow/tests/benchmark/Android.bp b/libs/nativewindow/tests/benchmark/Android.bp new file mode 100644 index 0000000000..6f844cf864 --- /dev/null +++ b/libs/nativewindow/tests/benchmark/Android.bp @@ -0,0 +1,50 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_defaults { + name: "nativewindow_benchmark_defaults_cc", + shared_libs: ["libnativewindow"], + static_libs: [ + "libbase", + "libgoogle-benchmark-main", + ], + test_suites: [ + "device-tests", + "NativeWindowBenchmarks", + ], +} + +cc_benchmark { + name: "nativewindow_buffer_benchmarks_cc", + srcs: ["buffer_benchmarks.cc"], + defaults: ["nativewindow_benchmark_defaults_cc"], +} + +rust_defaults { + name: "nativewindow_benchmark_defaults_rs", + rustlibs: [ + "libnativewindow_rs", + "libcriterion", + ], + test_suites: [ + "device-tests", + "NativeWindowBenchmarks", + ], +} + +rust_benchmark { + name: "nativewindow_buffer_benchmarks_rs", + srcs: ["buffer_benchmarks.rs"], + defaults: ["nativewindow_benchmark_defaults_rs"], +} diff --git a/libs/nativewindow/tests/benchmark/README.md b/libs/nativewindow/tests/benchmark/README.md new file mode 100644 index 0000000000..7eae538dd2 --- /dev/null +++ b/libs/nativewindow/tests/benchmark/README.md @@ -0,0 +1,22 @@ +# libnativewindow Benchmarks + +This directory contains benchmarks for the C++ and Rust variants of +libnativewindow. + +## Running + +It is currently a little tricky to get statistics from Rust benchmarks directly +from tradefed. But we can hack it by using atest to build/push, then running +the benchmarks by hand to get stats. + +``` + $ atest nativewindow_buffer_benchmarks_rs nativewindow_buffer_benchmarks_cc -d + $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_cc/x86_64/nativewindow_buffer_benchmarks_cc + $ adb shell /data/local/tmp/nativewindow_buffer_benchmarks_rs/x86_64/nativewindow_buffer_benchmarks_rs --bench +``` + +## Results + +On a remote emulator, the results we see from the benchmarks from Rust and C++ +seem to be roughly equivalent! Allocating/deallocating a 720p buffer takes +~2.3ms on each. diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc new file mode 100644 index 0000000000..9b31993809 --- /dev/null +++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.cc @@ -0,0 +1,74 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <android-base/macros.h> +#include <android/hardware_buffer.h> +#include <benchmark/benchmark.h> + +constexpr AHardwareBuffer_Desc k720pDesc = {.width = 1280, + .height = 720, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + .stride = 0}; + +static void BM_BufferAllocationDeallocation(benchmark::State& state) { + AHardwareBuffer* buffer = nullptr; + for (auto _ : state) { + int status = AHardwareBuffer_allocate(&k720pDesc, &buffer); + if (UNLIKELY(status != 0)) { + state.SkipWithError("Unable to allocate buffer."); + } + AHardwareBuffer_release(buffer); + buffer = nullptr; + } +} +BENCHMARK(BM_BufferAllocationDeallocation); + +static void BM_AHardwareBuffer_Id(benchmark::State& state) { + AHardwareBuffer* buffer = nullptr; + int status = AHardwareBuffer_allocate(&k720pDesc, &buffer); + if (UNLIKELY(status != 0)) { + state.SkipWithError("Unable to allocate buffer."); + } + + for (auto _ : state) { + uint64_t id = 0; + int status = AHardwareBuffer_getId(buffer, &id); + if (UNLIKELY(status != 0)) { + state.SkipWithError("Unable to get ID."); + } + } + + AHardwareBuffer_release(buffer); +} +BENCHMARK(BM_AHardwareBuffer_Id); + +static void BM_AHardwareBuffer_Desc(benchmark::State& state) { + AHardwareBuffer* buffer = nullptr; + int status = AHardwareBuffer_allocate(&k720pDesc, &buffer); + if (UNLIKELY(status != 0)) { + state.SkipWithError("Unable to allocate buffer."); + } + + for (auto _ : state) { + AHardwareBuffer_Desc desc = {}; + AHardwareBuffer_describe(buffer, &desc); + } + + AHardwareBuffer_release(buffer); +} +BENCHMARK(BM_AHardwareBuffer_Desc); + +BENCHMARK_MAIN(); diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs new file mode 100644 index 0000000000..fbd49c0b50 --- /dev/null +++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs @@ -0,0 +1,60 @@ +// Copyright (C) 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Benchmark for libnativewindow AHardwareBuffer bindings + +#![allow(dead_code)] +#![allow(missing_docs)] + +use criterion::*; +use nativewindow::*; + +#[inline] +fn create_720p_buffer() -> AHardwareBuffer { + AHardwareBuffer::new( + 1280, + 720, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + ) + .unwrap() +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("allocate_deallocate", |b| { + b.iter(|| { + let buffer = create_720p_buffer(); + drop(buffer); + }) + }); + + let buffer = create_720p_buffer(); + c.bench_with_input(BenchmarkId::new("id", "buffer"), &buffer, |b, buffer| { + b.iter(|| { + buffer.id(); + }) + }); + + // This benchmark exercises getters that need to fetch data via an + // underlying call to AHardwareBuffer_describe. + c.bench_with_input(BenchmarkId::new("desc", "buffer"), &buffer, |b, buffer| { + b.iter(|| { + buffer.width(); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 8d19c45527..ba2eb7d224 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -56,30 +56,8 @@ cc_defaults { filegroup { name: "librenderengine_sources", srcs: [ - "Description.cpp", "ExternalTexture.cpp", - "Mesh.cpp", "RenderEngine.cpp", - "Texture.cpp", - ], -} - -filegroup { - name: "librenderengine_gl_sources", - srcs: [ - "gl/GLESRenderEngine.cpp", - "gl/GLExtensions.cpp", - "gl/GLFramebuffer.cpp", - "gl/GLImage.cpp", - "gl/GLShadowTexture.cpp", - "gl/GLShadowVertexGenerator.cpp", - "gl/GLSkiaShadowPort.cpp", - "gl/GLVertexBuffer.cpp", - "gl/ImageManager.cpp", - "gl/Program.cpp", - "gl/ProgramCache.cpp", - "gl/filters/BlurFilter.cpp", - "gl/filters/GenericProgram.cpp", ], } @@ -96,6 +74,7 @@ filegroup { "skia/AutoBackendTexture.cpp", "skia/Cache.cpp", "skia/ColorSpaces.cpp", + "skia/GLExtensions.cpp", "skia/SkiaRenderEngine.cpp", "skia/SkiaGLRenderEngine.cpp", "skia/SkiaVkRenderEngine.cpp", @@ -136,7 +115,6 @@ cc_library_static { ], srcs: [ ":librenderengine_sources", - ":librenderengine_gl_sources", ":librenderengine_threaded_sources", ":librenderengine_skia_sources", ], @@ -155,8 +133,6 @@ cc_library_static { name: "librenderengine_mocks", defaults: ["librenderengine_defaults"], srcs: [ - "mock/Framebuffer.cpp", - "mock/Image.cpp", "mock/RenderEngine.cpp", ], static_libs: [ diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp deleted file mode 100644 index 245c9e1e30..0000000000 --- a/libs/renderengine/Description.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <renderengine/private/Description.h> - -#include <stdint.h> - -#include <utils/TypeHelpers.h> - -namespace android { -namespace renderengine { - -Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) { - ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK); - switch (transfer) { - case ui::Dataspace::TRANSFER_ST2084: - return Description::TransferFunction::ST2084; - case ui::Dataspace::TRANSFER_HLG: - return Description::TransferFunction::HLG; - case ui::Dataspace::TRANSFER_LINEAR: - return Description::TransferFunction::LINEAR; - default: - return Description::TransferFunction::SRGB; - } -} - -bool Description::hasInputTransformMatrix() const { - const mat4 identity; - return inputTransformMatrix != identity; -} - -bool Description::hasOutputTransformMatrix() const { - const mat4 identity; - return outputTransformMatrix != identity; -} - -bool Description::hasColorMatrix() const { - const mat4 identity; - return colorMatrix != identity; -} - -bool Description::hasDisplayColorMatrix() const { - const mat4 identity; - return displayColorMatrix != identity; -} - -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp index 9eb42cd8e1..6f2a96a87b 100644 --- a/libs/renderengine/ExternalTexture.cpp +++ b/libs/renderengine/ExternalTexture.cpp @@ -27,14 +27,6 @@ ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, : mBuffer(buffer), mRenderEngine(renderEngine), mWritable(usage & WRITEABLE) { LOG_ALWAYS_FATAL_IF(buffer == nullptr, "Attempted to bind a null buffer to an external texture!"); - // GLESRenderEngine has a separate texture cache for output buffers, - if (usage == WRITEABLE && - (mRenderEngine.getRenderEngineType() == - renderengine::RenderEngine::RenderEngineType::GLES || - mRenderEngine.getRenderEngineType() == - renderengine::RenderEngine::RenderEngineType::THREADED)) { - return; - } mRenderEngine.mapExternalTextureBuffer(mBuffer, mWritable); } diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp deleted file mode 100644 index ed2f45fdf5..0000000000 --- a/libs/renderengine/Mesh.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <renderengine/Mesh.h> - -#include <utils/Log.h> - -namespace android { -namespace renderengine { - -Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize, - size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, - size_t indexCount) - : mVertexCount(vertexCount), - mVertexSize(vertexSize), - mTexCoordsSize(texCoordSize), - mCropCoordsSize(cropCoordsSize), - mShadowColorSize(shadowColorSize), - mShadowParamsSize(shadowParamsSize), - mPrimitive(primitive), - mIndexCount(indexCount) { - if (vertexCount == 0) { - mVertices.resize(1); - mVertices[0] = 0.0f; - mStride = 0; - return; - } - size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize; - size_t remainder = (stride * vertexCount) / vertexCount; - // Since all of the input parameters are unsigned, if stride is less than - // either vertexSize or texCoordSize, it must have overflowed. remainder - // will be equal to stride as long as stride * vertexCount doesn't overflow. - if ((stride < vertexSize) || (remainder != stride)) { - ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize, - texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize); - mVertices.resize(1); - mVertices[0] = 0.0f; - mVertexCount = 0; - mVertexSize = 0; - mTexCoordsSize = 0; - mCropCoordsSize = 0; - mShadowColorSize = 0; - mShadowParamsSize = 0; - mStride = 0; - return; - } - - mVertices.resize(stride * vertexCount); - mStride = stride; - mIndices.resize(indexCount); -} - -Mesh::Primitive Mesh::getPrimitive() const { - return mPrimitive; -} - -float const* Mesh::getPositions() const { - return mVertices.data(); -} -float* Mesh::getPositions() { - return mVertices.data(); -} - -float const* Mesh::getTexCoords() const { - return mVertices.data() + mVertexSize; -} -float* Mesh::getTexCoords() { - return mVertices.data() + mVertexSize; -} - -float const* Mesh::getCropCoords() const { - return mVertices.data() + mVertexSize + mTexCoordsSize; -} -float* Mesh::getCropCoords() { - return mVertices.data() + mVertexSize + mTexCoordsSize; -} - -float const* Mesh::getShadowColor() const { - return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize; -} -float* Mesh::getShadowColor() { - return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize; -} - -float const* Mesh::getShadowParams() const { - return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize; -} -float* Mesh::getShadowParams() { - return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize; -} - -uint16_t const* Mesh::getIndices() const { - return mIndices.data(); -} - -uint16_t* Mesh::getIndices() { - return mIndices.data(); -} - -size_t Mesh::getVertexCount() const { - return mVertexCount; -} - -size_t Mesh::getVertexSize() const { - return mVertexSize; -} - -size_t Mesh::getTexCoordsSize() const { - return mTexCoordsSize; -} - -size_t Mesh::getShadowColorSize() const { - return mShadowColorSize; -} - -size_t Mesh::getShadowParamsSize() const { - return mShadowParamsSize; -} - -size_t Mesh::getByteStride() const { - return mStride * sizeof(float); -} - -size_t Mesh::getStride() const { - return mStride; -} - -size_t Mesh::getIndexCount() const { - return mIndexCount; -} - -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index 5d23a5ef8d..66e1aa1ca1 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1075131 + adyabr@google.com alecmouri@google.com djsollen@google.com diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index d08c2213ad..3e1ac33d57 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -18,7 +18,6 @@ #include <cutils/properties.h> #include <log/log.h> -#include "gl/GLESRenderEngine.h" #include "renderengine/ExternalTexture.h" #include "threaded/RenderEngineThreaded.h" @@ -30,11 +29,6 @@ namespace renderengine { std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { switch (args.renderEngineType) { - case RenderEngineType::THREADED: - ALOGD("Threaded RenderEngine with GLES Backend"); - return renderengine::threaded::RenderEngineThreaded::create( - [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }, - args.renderEngineType); case RenderEngineType::SKIA_GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); @@ -56,10 +50,6 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg return android::renderengine::skia::SkiaVkRenderEngine::create(args); }, args.renderEngineType); - case RenderEngineType::GLES: - default: - ALOGD("RenderEngine with GLES Backend"); - return renderengine::gl::GLESRenderEngine::create(args); } } @@ -78,13 +68,11 @@ void RenderEngine::validateOutputBufferUsage(const sp<GraphicBuffer>& buffer) { ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); updateProtectedContext(layers, buffer); - drawLayersInternal(std::move(resultPromise), display, layers, buffer, useFramebufferCache, - std::move(bufferFence)); + drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence)); return resultFuture; } diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp deleted file mode 100644 index 154cde80b9..0000000000 --- a/libs/renderengine/Texture.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <renderengine/Texture.h> - -namespace android { -namespace renderengine { - -Texture::Texture() - : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {} - -Texture::Texture(Target textureTarget, uint32_t textureName) - : mTextureName(textureName), - mTextureTarget(textureTarget), - mWidth(0), - mHeight(0), - mFiltering(false) {} - -void Texture::init(Target textureTarget, uint32_t textureName) { - mTextureName = textureName; - mTextureTarget = textureTarget; -} - -Texture::~Texture() {} - -void Texture::setMatrix(float const* matrix) { - mTextureMatrix = mat4(matrix); -} - -void Texture::setFiltering(bool enabled) { - mFiltering = enabled; -} - -void Texture::setDimensions(size_t width, size_t height) { - mWidth = width; - mHeight = height; -} - -uint32_t Texture::getTextureName() const { - return mTextureName; -} - -uint32_t Texture::getTextureTarget() const { - return mTextureTarget; -} - -const mat4& Texture::getMatrix() const { - return mTextureMatrix; -} - -bool Texture::getFiltering() const { - return mFiltering; -} - -size_t Texture::getWidth() const { - return mWidth; -} - -size_t Texture::getHeight() const { - return mHeight; -} - -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index bd7b617ae7..a7f1df9a9b 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -43,10 +43,6 @@ std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) { return "skiavk"; case RenderEngine::RenderEngineType::SKIA_VK_THREADED: return "skiavkthreaded"; - case RenderEngine::RenderEngineType::GLES: - case RenderEngine::RenderEngineType::THREADED: - LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?"); - return "unused"; } } @@ -108,10 +104,6 @@ std::pair<uint32_t, uint32_t> getDisplaySize() { return std::pair<uint32_t, uint32_t>(width, height); } -// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove -// GLESRenderEngine we can remove this, too. -static constexpr const bool kUseFrameBufferCache = false; - static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) { auto args = RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) @@ -121,7 +113,6 @@ static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngi .setSupportsBackgroundBlur(true) .setContextPriority(RenderEngine::ContextPriority::REALTIME) .setRenderEngineType(type) - .setUseColorManagerment(true) .build(); return RenderEngine::create(args); } @@ -173,10 +164,7 @@ static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re, }; auto layers = std::vector<LayerSettings>{layer}; - sp<Fence> waitFence = - re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()) - .get() - .value(); + sp<Fence> waitFence = re.drawLayers(display, layers, texture, base::unique_fd()).get().value(); waitFence->waitForever(LOG_TAG); return texture; } @@ -205,10 +193,8 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& // This loop starts and stops the timer. for (auto _ : benchState) { - sp<Fence> waitFence = re.drawLayers(display, layers, outputBuffer, kUseFrameBufferCache, - base::unique_fd()) - .get() - .value(); + sp<Fence> waitFence = + re.drawLayers(display, layers, outputBuffer, base::unique_fd()).get().value(); waitFence->waitForever(LOG_TAG); } diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp deleted file mode 100644 index 0d7df101f4..0000000000 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ /dev/null @@ -1,1866 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#include "EGL/egl.h" -#undef LOG_TAG -#define LOG_TAG "RenderEngine" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <sched.h> -#include <cmath> -#include <fstream> -#include <sstream> -#include <unordered_set> - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <android-base/stringprintf.h> -#include <cutils/compiler.h> -#include <cutils/properties.h> -#include <gui/DebugEGLImageTracker.h> -#include <renderengine/Mesh.h> -#include <renderengine/Texture.h> -#include <renderengine/private/Description.h> -#include <sync/sync.h> -#include <ui/ColorSpace.h> -#include <ui/DebugUtils.h> -#include <ui/GraphicBuffer.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <utils/KeyedVector.h> -#include <utils/Trace.h> -#include "GLESRenderEngine.h" -#include "GLExtensions.h" -#include "GLFramebuffer.h" -#include "GLImage.h" -#include "GLShadowVertexGenerator.h" -#include "Program.h" -#include "ProgramCache.h" -#include "filters/BlurFilter.h" - -bool checkGlError(const char* op, int lineNumber) { - bool errorFound = false; - GLint error = glGetError(); - while (error != GL_NO_ERROR) { - errorFound = true; - error = glGetError(); - ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); - } - return errorFound; -} - -static constexpr bool outputDebugPPMs = false; - -void writePPM(const char* basename, GLuint width, GLuint height) { - ALOGV("writePPM #%s: %d x %d", basename, width, height); - - std::vector<GLubyte> pixels(width * height * 4); - std::vector<GLubyte> outBuffer(width * height * 3); - - // TODO(courtneygo): We can now have float formats, need - // to remove this code or update to support. - // Make returned pixels fit in uint32_t, one byte per component - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); - if (checkGlError(__FUNCTION__, __LINE__)) { - return; - } - - std::string filename(basename); - filename.append(".ppm"); - std::ofstream file(filename.c_str(), std::ios::binary); - if (!file.is_open()) { - ALOGE("Unable to open file: %s", filename.c_str()); - ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " - "surfaceflinger to write debug images"); - return; - } - - file << "P6\n"; - file << width << "\n"; - file << height << "\n"; - file << 255 << "\n"; - - auto ptr = reinterpret_cast<char*>(pixels.data()); - auto outPtr = reinterpret_cast<char*>(outBuffer.data()); - for (int y = height - 1; y >= 0; y--) { - char* data = ptr + y * width * sizeof(uint32_t); - - for (GLuint x = 0; x < width; x++) { - // Only copy R, G and B components - outPtr[0] = data[0]; - outPtr[1] = data[1]; - outPtr[2] = data[2]; - data += sizeof(uint32_t); - outPtr += 3; - } - } - file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); -} - -namespace android { -namespace renderengine { -namespace gl { - -class BindNativeBufferAsFramebuffer { -public: - BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer, - const bool useFramebufferCache) - : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { - mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), - useFramebufferCache) - ? mEngine.bindFrameBuffer(mFramebuffer) - : NO_MEMORY; - } - ~BindNativeBufferAsFramebuffer() { - mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); - mEngine.unbindFrameBuffer(mFramebuffer); - } - status_t getStatus() const { return mStatus; } - -private: - GLESRenderEngine& mEngine; - Framebuffer* mFramebuffer; - status_t mStatus; -}; - -using base::StringAppendF; -using ui::Dataspace; - -static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, - EGLint wanted, EGLConfig* outConfig) { - EGLint numConfigs = -1, n = 0; - eglGetConfigs(dpy, nullptr, 0, &numConfigs); - std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); - eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); - configs.resize(n); - - if (!configs.empty()) { - if (attribute != EGL_NONE) { - for (EGLConfig config : configs) { - EGLint value = 0; - eglGetConfigAttrib(dpy, config, attribute, &value); - if (wanted == value) { - *outConfig = config; - return NO_ERROR; - } - } - } else { - // just pick the first one - *outConfig = configs[0]; - return NO_ERROR; - } - } - - return NAME_NOT_FOUND; -} - -static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, - EGLConfig* config) { - // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if - // it is to be used with WIFI displays - status_t err; - EGLint wantedAttribute; - EGLint wantedAttributeValue; - - std::vector<EGLint> attribs; - if (renderableType) { - const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format); - const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102; - - // Default to 8 bits per channel. - const EGLint tmpAttribs[] = { - EGL_RENDERABLE_TYPE, - renderableType, - EGL_RECORDABLE_ANDROID, - EGL_TRUE, - EGL_SURFACE_TYPE, - EGL_WINDOW_BIT | EGL_PBUFFER_BIT, - EGL_FRAMEBUFFER_TARGET_ANDROID, - EGL_TRUE, - EGL_RED_SIZE, - is1010102 ? 10 : 8, - EGL_GREEN_SIZE, - is1010102 ? 10 : 8, - EGL_BLUE_SIZE, - is1010102 ? 10 : 8, - EGL_ALPHA_SIZE, - is1010102 ? 2 : 8, - EGL_NONE, - }; - std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)), - std::back_inserter(attribs)); - wantedAttribute = EGL_NONE; - wantedAttributeValue = EGL_NONE; - } else { - // if no renderable type specified, fallback to a simplified query - wantedAttribute = EGL_NATIVE_VISUAL_ID; - wantedAttributeValue = format; - } - - err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue, - config); - if (err == NO_ERROR) { - EGLint caveat; - if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) - ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); - } - - return err; -} - -std::optional<RenderEngine::ContextPriority> GLESRenderEngine::createContextPriority( - const RenderEngineCreationArgs& args) { - if (!GLExtensions::getInstance().hasContextPriority()) { - return std::nullopt; - } - - switch (args.contextPriority) { - case RenderEngine::ContextPriority::REALTIME: - if (gl::GLExtensions::getInstance().hasRealtimePriority()) { - return RenderEngine::ContextPriority::REALTIME; - } else { - ALOGI("Realtime priority unsupported, degrading gracefully to high priority"); - return RenderEngine::ContextPriority::HIGH; - } - case RenderEngine::ContextPriority::HIGH: - case RenderEngine::ContextPriority::MEDIUM: - case RenderEngine::ContextPriority::LOW: - return args.contextPriority; - default: - return std::nullopt; - } -} - -std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) { - // initialize EGL for the default display - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!eglInitialize(display, nullptr, nullptr)) { - LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError()); - } - - const auto eglVersion = eglQueryString(display, EGL_VERSION); - if (!eglVersion) { - checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed"); - } - - // Use the Android impl to grab EGL_NV_context_priority_realtime - const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS); - if (!eglExtensions) { - checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed"); - } - - GLExtensions& extensions = GLExtensions::getInstance(); - extensions.initWithEGLStrings(eglVersion, eglExtensions); - - // The code assumes that ES2 or later is available if this extension is - // supported. - EGLConfig config = EGL_NO_CONFIG; - if (!extensions.hasNoConfigContext()) { - config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true); - } - - const std::optional<RenderEngine::ContextPriority> priority = createContextPriority(args); - EGLContext protectedContext = EGL_NO_CONTEXT; - if (args.enableProtectedContext && extensions.hasProtectedContent()) { - protectedContext = - createEglContext(display, config, nullptr, priority, Protection::PROTECTED); - ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); - } - - EGLContext ctxt = - createEglContext(display, config, protectedContext, priority, Protection::UNPROTECTED); - - // if can't create a GL context, we can only abort. - LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); - - EGLSurface stub = EGL_NO_SURFACE; - if (!extensions.hasSurfacelessContext()) { - stub = createStubEglPbufferSurface(display, config, args.pixelFormat, - Protection::UNPROTECTED); - LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer"); - } - EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt); - LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current"); - extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), - glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); - - EGLSurface protectedStub = EGL_NO_SURFACE; - if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { - protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat, - Protection::PROTECTED); - ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer"); - } - - // now figure out what version of GL did we actually get - GlesVersion version = parseGlesVersion(extensions.getVersion()); - - LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0, - "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur"); - - // initialize the renderer while GL is current - std::unique_ptr<GLESRenderEngine> engine; - switch (version) { - case GLES_VERSION_1_0: - case GLES_VERSION_1_1: - LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); - break; - case GLES_VERSION_2_0: - case GLES_VERSION_3_0: - engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub, - protectedContext, protectedStub); - break; - } - - ALOGI("OpenGL ES informations:"); - ALOGI("vendor : %s", extensions.getVendor()); - ALOGI("renderer : %s", extensions.getRenderer()); - ALOGI("version : %s", extensions.getVersion()); - ALOGI("extensions: %s", extensions.getExtensions()); - ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); - ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); - return engine; -} - -EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { - status_t err; - EGLConfig config; - - // First try to get an ES3 config - err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); - if (err != NO_ERROR) { - // If ES3 fails, try to get an ES2 config - err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); - if (err != NO_ERROR) { - // If ES2 still doesn't work, probably because we're on the emulator. - // try a simplified query - ALOGW("no suitable EGLConfig found, trying a simpler query"); - err = selectEGLConfig(display, format, 0, &config); - if (err != NO_ERROR) { - // this EGL is too lame for android - LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); - } - } - } - - if (logConfig) { - // print some debugging info - EGLint r, g, b, a; - eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); - eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); - eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); - eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); - ALOGI("EGL information:"); - ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); - ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); - ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); - ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); - ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); - } - - return config; -} - -GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, - EGLConfig config, EGLContext ctxt, EGLSurface stub, - EGLContext protectedContext, EGLSurface protectedStub) - : RenderEngine(args.renderEngineType), - mEGLDisplay(display), - mEGLConfig(config), - mEGLContext(ctxt), - mStubSurface(stub), - mProtectedEGLContext(protectedContext), - mProtectedStubSurface(protectedStub), - mVpWidth(0), - mVpHeight(0), - mFramebufferImageCacheSize(args.imageCacheSize), - mUseColorManagement(args.useColorManagement), - mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) { - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); - glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - - // Initialize protected EGL Context. - if (mProtectedEGLContext != EGL_NO_CONTEXT) { - EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface, - mProtectedEGLContext); - ALOGE_IF(!success, "can't make protected context current"); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glPixelStorei(GL_PACK_ALIGNMENT, 4); - success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext); - LOG_ALWAYS_FATAL_IF(!success, "can't make default context current"); - } - - // mColorBlindnessCorrection = M; - - if (mUseColorManagement) { - const ColorSpace srgb(ColorSpace::sRGB()); - const ColorSpace displayP3(ColorSpace::DisplayP3()); - const ColorSpace bt2020(ColorSpace::BT2020()); - - // no chromatic adaptation needed since all color spaces use D65 for their white points. - mSrgbToXyz = mat4(srgb.getRGBtoXYZ()); - mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ()); - mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ()); - mXyzToSrgb = mat4(srgb.getXYZtoRGB()); - mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); - mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); - - // Compute sRGB to Display P3 and BT2020 transform matrix. - // NOTE: For now, we are limiting output wide color space support to - // Display-P3 and BT2020 only. - mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz; - mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz; - - // Compute Display P3 to sRGB and BT2020 transform matrix. - mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz; - mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz; - - // Compute BT2020 to sRGB and Display P3 transform matrix - mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz; - mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz; - } - - char value[PROPERTY_VALUE_MAX]; - property_get("debug.egl.traceGpuCompletion", value, "0"); - if (atoi(value)) { - mTraceGpuCompletion = true; - mFlushTracer = std::make_unique<FlushTracer>(this); - } - - if (args.supportsBackgroundBlur) { - mBlurFilter = new BlurFilter(*this); - checkErrors("BlurFilter creation"); - } - - mImageManager = std::make_unique<ImageManager>(this); - mImageManager->initThread(); - mDrawingBuffer = createFramebuffer(); - sp<GraphicBuffer> buf = - sp<GraphicBuffer>::make(1, 1, PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE, - "placeholder"); - - const status_t err = buf->initCheck(); - if (err != OK) { - ALOGE("Error allocating placeholder buffer: %d", err); - return; - } - mPlaceholderBuffer = buf.get(); - EGLint attributes[] = { - EGL_NONE, - }; - mPlaceholderImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - mPlaceholderBuffer, attributes); - ALOGE_IF(mPlaceholderImage == EGL_NO_IMAGE_KHR, "Failed to create placeholder image: %#x", - eglGetError()); - - mShadowTexture = std::make_unique<GLShadowTexture>(); -} - -GLESRenderEngine::~GLESRenderEngine() { - // Destroy the image manager first. - mImageManager = nullptr; - mShadowTexture = nullptr; - cleanFramebufferCache(); - ProgramCache::getInstance().purgeCaches(); - std::lock_guard<std::mutex> lock(mRenderingMutex); - glDisableVertexAttribArray(Program::position); - unbindFrameBuffer(mDrawingBuffer.get()); - mDrawingBuffer = nullptr; - eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage); - mImageCache.clear(); - if (mStubSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEGLDisplay, mStubSurface); - } - if (mProtectedStubSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEGLDisplay, mProtectedStubSurface); - } - if (mEGLContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEGLDisplay, mEGLContext); - } - if (mProtectedEGLContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEGLDisplay, mProtectedEGLContext); - } - eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglTerminate(mEGLDisplay); - eglReleaseThread(); -} - -std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() { - return std::make_unique<GLFramebuffer>(*this); -} - -std::unique_ptr<Image> GLESRenderEngine::createImage() { - return std::make_unique<GLImage>(*this); -} - -Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { - return mDrawingBuffer.get(); -} - -std::future<void> GLESRenderEngine::primeCache() { - ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, - mUseColorManagement, mPrecacheToneMapperShaderOnly); - return {}; -} - -base::unique_fd GLESRenderEngine::flush() { - ATRACE_CALL(); - if (!GLExtensions::getInstance().hasNativeFenceSync()) { - return base::unique_fd(); - } - - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); - return base::unique_fd(); - } - - // native fence fd will not be populated until flush() is done. - glFlush(); - - // get the fence fd - base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); - eglDestroySyncKHR(mEGLDisplay, sync); - if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { - ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); - } - - // Only trace if we have a valid fence, as current usage falls back to - // calling finish() if the fence fd is invalid. - if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) { - mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); - } - - return fenceFd; -} - -bool GLESRenderEngine::finish() { - ATRACE_CALL(); - if (!GLExtensions::getInstance().hasFenceSync()) { - ALOGW("no synchronization support"); - return false; - } - - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); - if (sync == EGL_NO_SYNC_KHR) { - ALOGW("failed to create EGL fence sync: %#x", eglGetError()); - return false; - } - - if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) { - mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); - } - - return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR); -} - -bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) { - EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/); - EGLint error = eglGetError(); - eglDestroySyncKHR(mEGLDisplay, sync); - if (result != EGL_CONDITION_SATISFIED_KHR) { - if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGW("fence wait timed out"); - } else { - ALOGW("error waiting on EGL fence: %#x", error); - } - return false; - } - - return true; -} - -bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) { - if (!GLExtensions::getInstance().hasNativeFenceSync() || - !GLExtensions::getInstance().hasWaitSync()) { - return false; - } - - // release the fd and transfer the ownership to EGLSync - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; - EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); - if (sync == EGL_NO_SYNC_KHR) { - ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); - return false; - } - - // XXX: The spec draft is inconsistent as to whether this should return an - // EGLint or void. Ignore the return value for now, as it's not strictly - // needed. - eglWaitSyncKHR(mEGLDisplay, sync, 0); - EGLint error = eglGetError(); - eglDestroySyncKHR(mEGLDisplay, sync); - if (error != EGL_SUCCESS) { - ALOGE("failed to wait for EGL native fence sync: %#x", error); - return false; - } - - return true; -} - -void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) { - ATRACE_CALL(); - glDisable(GL_BLEND); - glClearColor(red, green, blue, alpha); - glClear(GL_COLOR_BUFFER_BIT); -} - -void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue, - float alpha) { - size_t c; - Rect const* r = region.getArray(&c); - Mesh mesh = Mesh::Builder() - .setPrimitive(Mesh::TRIANGLES) - .setVertices(c * 6 /* count */, 2 /* size */) - .build(); - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - for (size_t i = 0; i < c; i++, r++) { - position[i * 6 + 0].x = r->left; - position[i * 6 + 0].y = r->top; - position[i * 6 + 1].x = r->left; - position[i * 6 + 1].y = r->bottom; - position[i * 6 + 2].x = r->right; - position[i * 6 + 2].y = r->bottom; - position[i * 6 + 3].x = r->left; - position[i * 6 + 3].y = r->top; - position[i * 6 + 4].x = r->right; - position[i * 6 + 4].y = r->bottom; - position[i * 6 + 5].x = r->right; - position[i * 6 + 5].y = r->top; - } - setupFillWithColor(red, green, blue, alpha); - drawMesh(mesh); -} - -void GLESRenderEngine::setScissor(const Rect& region) { - glScissor(region.left, region.top, region.getWidth(), region.getHeight()); - glEnable(GL_SCISSOR_TEST); -} - -void GLESRenderEngine::disableScissor() { - glDisable(GL_SCISSOR_TEST); -} - -void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { - glGenTextures(count, names); -} - -void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { - for (int i = 0; i < count; ++i) { - mTextureView.erase(names[i]); - } - glDeleteTextures(count, names); -} - -void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { - ATRACE_CALL(); - const GLImage& glImage = static_cast<const GLImage&>(image); - const GLenum target = GL_TEXTURE_EXTERNAL_OES; - - glBindTexture(target, texName); - if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { - glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage())); - } -} - -void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& bufferFence) { - ATRACE_CALL(); - - bool found = false; - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - auto cachedImage = mImageCache.find(buffer->getId()); - found = (cachedImage != mImageCache.end()); - } - - // If we couldn't find the image in the cache at this time, then either - // SurfaceFlinger messed up registering the buffer ahead of time or we got - // backed up creating other EGLImages. - if (!found) { - status_t cacheResult = mImageManager->cache(buffer); - if (cacheResult != NO_ERROR) { - ALOGE("Error with caching buffer: %d", cacheResult); - return; - } - } - - // Whether or not we needed to cache, re-check mImageCache to make sure that - // there's an EGLImage. The current threading model guarantees that we don't - // destroy a cached image until it's really not needed anymore (i.e. this - // function should not be called), so the only possibility is that something - // terrible went wrong and we should just bind something and move on. - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - auto cachedImage = mImageCache.find(buffer->getId()); - - if (cachedImage == mImageCache.end()) { - // We failed creating the image if we got here, so bail out. - ALOGE("Failed to create an EGLImage when rendering"); - bindExternalTextureImage(texName, *createImage()); - return; - } - - bindExternalTextureImage(texName, *cachedImage->second); - mTextureView.insert_or_assign(texName, buffer->getId()); - } - - // Wait for the new buffer to be ready. - if (bufferFence != nullptr && bufferFence->isValid()) { - if (GLExtensions::getInstance().hasWaitSync()) { - base::unique_fd fenceFd(bufferFence->dup()); - if (fenceFd == -1) { - ALOGE("error dup'ing fence fd: %d", errno); - return; - } - if (!waitFence(std::move(fenceFd))) { - ALOGE("failed to wait on fence fd"); - return; - } - } else { - status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer"); - if (err != NO_ERROR) { - ALOGE("error waiting for fence: %d", err); - return; - } - } - } - - return; -} - -void GLESRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, - bool /*isRenderable*/) { - ATRACE_CALL(); - mImageManager->cacheAsync(buffer, nullptr); -} - -std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::cacheExternalTextureBufferForTesting( - const sp<GraphicBuffer>& buffer) { - auto barrier = std::make_shared<ImageManager::Barrier>(); - mImageManager->cacheAsync(buffer, barrier); - return barrier; -} - -status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) { - if (buffer == nullptr) { - return BAD_VALUE; - } - - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - if (mImageCache.count(buffer->getId()) > 0) { - // If there's already an image then fail fast here. - return NO_ERROR; - } - } - ATRACE_CALL(); - - // Create the image without holding a lock so that we don't block anything. - std::unique_ptr<Image> newImage = createImage(); - - bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), - buffer->getUsage() & GRALLOC_USAGE_PROTECTED); - if (!created) { - ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(), - buffer->getUsage(), buffer->getPixelFormat()); - return NO_INIT; - } - - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - if (mImageCache.count(buffer->getId()) > 0) { - // In theory it's possible for another thread to recache the image, - // so bail out if another thread won. - return NO_ERROR; - } - mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); - } - - return NO_ERROR; -} - -void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { - mImageManager->releaseAsync(buffer->getId(), nullptr); -} - -std::shared_ptr<ImageManager::Barrier> GLESRenderEngine::unbindExternalTextureBufferForTesting( - uint64_t bufferId) { - auto barrier = std::make_shared<ImageManager::Barrier>(); - mImageManager->releaseAsync(bufferId, barrier); - return barrier; -} - -void GLESRenderEngine::unbindExternalTextureBufferInternal(uint64_t bufferId) { - std::unique_ptr<Image> image; - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - const auto& cachedImage = mImageCache.find(bufferId); - - if (cachedImage != mImageCache.end()) { - ALOGV("Destroying image for buffer: %" PRIu64, bufferId); - // Move the buffer out of cache first, so that we can destroy - // without holding the cache's lock. - image = std::move(cachedImage->second); - mImageCache.erase(bufferId); - return; - } - } - ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); -} - -int GLESRenderEngine::getContextPriority() { - int value; - eglQueryContext(mEGLDisplay, mEGLContext, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value); - return value; -} - -FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { - // Translate win by the rounded corners rect coordinates, to have all values in - // layer coordinate space. - FloatRect cropWin = layer.geometry.boundaries; - const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop; - cropWin.left -= roundedCornersCrop.left; - cropWin.right -= roundedCornersCrop.left; - cropWin.top -= roundedCornersCrop.top; - cropWin.bottom -= roundedCornersCrop.top; - Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>()); - cropCoords[0] = vec2(cropWin.left, cropWin.top); - cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight()); - cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight()); - cropCoords[3] = vec2(cropWin.right, cropWin.top); - - setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight()); - return cropWin; -} - -void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, - const LayerSettings& layer, const Mesh& mesh) { - // We separate the layer into 3 parts essentially, such that we only turn on blending for the - // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle. - FloatRect bounds = layer.geometry.roundedCornersCrop; - - // Explicitly compute the transform from the clip rectangle to the physical - // display. Normally, this is done in glViewport but we explicitly compute - // it here so that we can get the scissor bounds correct. - const Rect& source = display.clip; - const Rect& destination = display.physicalDisplay; - // Here we compute the following transform: - // 1. Translate the top left corner of the source clip to (0, 0) - // 2. Rotate the clip rectangle about the origin in accordance with the - // orientation flag - // 3. Translate the top left corner back to the origin. - // 4. Scale the clip rectangle to the destination rectangle dimensions - // 5. Translate the top left corner to the destination rectangle's top left - // corner. - const mat4 translateSource = mat4::translate(vec4(-source.left, -source.top, 0, 1)); - mat4 rotation; - int displacementX = 0; - int displacementY = 0; - float destinationWidth = static_cast<float>(destination.getWidth()); - float destinationHeight = static_cast<float>(destination.getHeight()); - float sourceWidth = static_cast<float>(source.getWidth()); - float sourceHeight = static_cast<float>(source.getHeight()); - const float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; - switch (display.orientation) { - case ui::Transform::ROT_90: - rotation = mat4::rotate(rot90InRadians, vec3(0, 0, 1)); - displacementX = source.getHeight(); - std::swap(sourceHeight, sourceWidth); - break; - case ui::Transform::ROT_180: - rotation = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)); - displacementY = source.getHeight(); - displacementX = source.getWidth(); - break; - case ui::Transform::ROT_270: - rotation = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)); - displacementY = source.getWidth(); - std::swap(sourceHeight, sourceWidth); - break; - default: - break; - } - - const mat4 intermediateTranslation = mat4::translate(vec4(displacementX, displacementY, 0, 1)); - const mat4 scale = mat4::scale( - vec4(destinationWidth / sourceWidth, destinationHeight / sourceHeight, 1, 1)); - const mat4 translateDestination = - mat4::translate(vec4(destination.left, destination.top, 0, 1)); - const mat4 globalTransform = - translateDestination * scale * intermediateTranslation * rotation * translateSource; - - const mat4 transformMatrix = globalTransform * layer.geometry.positionTransform; - const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0); - const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0); - const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate; - const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate; - bounds = FloatRect(std::min(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]), - std::min(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1]), - std::max(leftTopCoordinateInBuffer[0], rightBottomCoordinateInBuffer[0]), - std::max(leftTopCoordinateInBuffer[1], rightBottomCoordinateInBuffer[1])); - - // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners - // and the middle part without rounded corners. - const int32_t radius = ceil( - (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / 2.0); - const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius); - setScissor(topRect); - drawMesh(mesh); - const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom); - setScissor(bottomRect); - drawMesh(mesh); - - // The middle part of the layer can turn off blending. - if (topRect.bottom < bottomRect.top) { - const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, - bounds.bottom - radius); - setScissor(middleRect); - mState.cornerRadius = 0.0; - disableBlending(); - drawMesh(mesh); - } - disableScissor(); -} - -status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { - ATRACE_CALL(); - GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer); - EGLImageKHR eglImage = glFramebuffer->getEGLImage(); - uint32_t textureName = glFramebuffer->getTextureName(); - uint32_t framebufferName = glFramebuffer->getFramebufferName(); - - // Bind the texture and turn our EGLImage into a texture - glBindTexture(GL_TEXTURE_2D, textureName); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); - - // Bind the Framebuffer to render into - glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); - - uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); - ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", - glStatus); - - return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; -} - -void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) { - ATRACE_CALL(); - - // back to main framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -bool GLESRenderEngine::canSkipPostRenderCleanup() const { - return mPriorResourcesCleaned || - (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled); -} - -void GLESRenderEngine::cleanupPostRender() { - ATRACE_CALL(); - - if (canSkipPostRenderCleanup()) { - // If we don't have a prior frame needing cleanup, then don't do anything. - return; - } - - // Bind the texture to placeholder so that backing image data can be freed. - GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); - glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); - - // Release the cached fence here, so that we don't churn reallocations when - // we could no-op repeated calls of this method instead. - mLastDrawFence = nullptr; - mPriorResourcesCleaned = true; -} - -void GLESRenderEngine::cleanFramebufferCache() { - std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); - // Bind the texture to placeholder so that backing image data can be freed. - GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); - glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); - - while (!mFramebufferImageCache.empty()) { - EGLImageKHR expired = mFramebufferImageCache.front().second; - mFramebufferImageCache.pop_front(); - eglDestroyImageKHR(mEGLDisplay, expired); - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - } -} - -void GLESRenderEngine::checkErrors() const { - checkErrors(nullptr); -} - -void GLESRenderEngine::checkErrors(const char* tag) const { - do { - // there could be more than one error flag - GLenum error = glGetError(); - if (error == GL_NO_ERROR) break; - if (tag == nullptr) { - ALOGE("GL error 0x%04x", int(error)); - } else { - ALOGE("GL error: %s -> 0x%04x", tag, int(error)); - } - } while (true); -} - -bool GLESRenderEngine::supportsProtectedContent() const { - return mProtectedEGLContext != EGL_NO_CONTEXT; -} - -void GLESRenderEngine::useProtectedContext(bool useProtectedContext) { - if (useProtectedContext == mInProtectedContext || - (useProtectedContext && !supportsProtectedContent())) { - return; - } - - const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface; - const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; - if (eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE) { - mInProtectedContext = useProtectedContext; - } -} -EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, - bool isProtected, - bool useFramebufferCache) { - sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer); - if (useFramebufferCache) { - std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); - for (const auto& image : mFramebufferImageCache) { - if (image.first == graphicBuffer->getId()) { - return image.second; - } - } - } - EGLint attributes[] = { - isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - isProtected ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - nativeBuffer, attributes); - if (useFramebufferCache) { - if (image != EGL_NO_IMAGE_KHR) { - std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); - if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { - EGLImageKHR expired = mFramebufferImageCache.front().second; - mFramebufferImageCache.pop_front(); - eglDestroyImageKHR(mEGLDisplay, expired); - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - } - mFramebufferImageCache.push_back({graphicBuffer->getId(), image}); - } - } - - if (image != EGL_NO_IMAGE_KHR) { - DEBUG_EGL_IMAGE_TRACKER_CREATE(); - } - return image; -} - -void GLESRenderEngine::drawLayersInternal( - const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) { - ATRACE_CALL(); - if (layers.empty()) { - ALOGV("Drawing empty layer stack"); - resultPromise->set_value(Fence::NO_FENCE); - return; - } - - if (bufferFence.get() >= 0) { - // Duplicate the fence for passing to waitFence. - base::unique_fd bufferFenceDup(dup(bufferFence.get())); - if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) { - ATRACE_NAME("Waiting before draw"); - sync_wait(bufferFence.get(), -1); - } - } - - if (buffer == nullptr) { - ALOGE("No output buffer provided. Aborting GPU composition."); - resultPromise->set_value(base::unexpected(BAD_VALUE)); - return; - } - - validateOutputBufferUsage(buffer->getBuffer()); - - std::unique_ptr<BindNativeBufferAsFramebuffer> fbo; - // Gathering layers that requested blur, we'll need them to decide when to render to an - // offscreen buffer, and when to render to the native buffer. - std::deque<const LayerSettings> blurLayers; - if (CC_LIKELY(mBlurFilter != nullptr)) { - for (const auto& layer : layers) { - if (layer.backgroundBlurRadius > 0) { - blurLayers.push_back(layer); - } - } - } - const auto blurLayersSize = blurLayers.size(); - - if (blurLayersSize == 0) { - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, - buffer->getBuffer() - .get() - ->getNativeBuffer(), - useFramebufferCache); - if (fbo->getStatus() != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->getBuffer()->handle); - checkErrors(); - resultPromise->set_value(base::unexpected(fbo->getStatus())); - return; - } - setViewportAndProjection(display.physicalDisplay, display.clip); - } else { - setViewportAndProjection(display.physicalDisplay, display.clip); - auto status = - mBlurFilter->setAsDrawTarget(display, blurLayers.front().backgroundBlurRadius); - if (status != NO_ERROR) { - ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).", - buffer->getBuffer()->handle); - checkErrors(); - resultPromise->set_value(base::unexpected(status)); - return; - } - } - - // clear the entire buffer, sometimes when we reuse buffers we'd persist - // ghost images otherwise. - // we also require a full transparent framebuffer for overlays. This is - // probably not quite efficient on all GPUs, since we could filter out - // opaque layers. - clearWithColor(0.0, 0.0, 0.0, 0.0); - - setOutputDataSpace(display.outputDataspace); - setDisplayMaxLuminance(display.maxLuminance); - setDisplayColorTransform(display.colorTransform); - - const mat4 projectionMatrix = - ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix; - - Mesh mesh = Mesh::Builder() - .setPrimitive(Mesh::TRIANGLE_FAN) - .setVertices(4 /* count */, 2 /* size */) - .setTexCoords(2 /* size */) - .setCropCoords(2 /* size */) - .build(); - for (const auto& layer : layers) { - if (blurLayers.size() > 0 && blurLayers.front() == layer) { - blurLayers.pop_front(); - - auto status = mBlurFilter->prepare(); - if (status != NO_ERROR) { - ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", - buffer->getBuffer()->handle); - checkErrors("Can't render first blur pass"); - resultPromise->set_value(base::unexpected(status)); - return; - } - - if (blurLayers.size() == 0) { - // Done blurring, time to bind the native FBO and render our blur onto it. - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, - buffer.get() - ->getBuffer() - ->getNativeBuffer(), - useFramebufferCache); - status = fbo->getStatus(); - setViewportAndProjection(display.physicalDisplay, display.clip); - } else { - // There's still something else to blur, so let's keep rendering to our FBO - // instead of to the display. - status = mBlurFilter->setAsDrawTarget(display, - blurLayers.front().backgroundBlurRadius); - } - if (status != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->getBuffer()->handle); - checkErrors("Can't bind native framebuffer"); - resultPromise->set_value(base::unexpected(status)); - return; - } - - status = mBlurFilter->render(blurLayersSize > 1); - if (status != NO_ERROR) { - ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).", - buffer->getBuffer()->handle); - checkErrors("Can't render blur filter"); - resultPromise->set_value(base::unexpected(status)); - return; - } - } - - // Ensure luminance is at least 100 nits to avoid div-by-zero - const float maxLuminance = std::max(100.f, layer.source.buffer.maxLuminanceNits); - mState.maxMasteringLuminance = maxLuminance; - mState.maxContentLuminance = maxLuminance; - mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; - - const FloatRect bounds = layer.geometry.boundaries; - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - position[0] = vec2(bounds.left, bounds.top); - position[1] = vec2(bounds.left, bounds.bottom); - position[2] = vec2(bounds.right, bounds.bottom); - position[3] = vec2(bounds.right, bounds.top); - - setupLayerCropping(layer, mesh); - setColorTransform(layer.colorTransform); - - bool usePremultipliedAlpha = true; - bool disableTexture = true; - bool isOpaque = false; - if (layer.source.buffer.buffer != nullptr) { - disableTexture = false; - isOpaque = layer.source.buffer.isOpaque; - - sp<GraphicBuffer> gBuf = layer.source.buffer.buffer->getBuffer(); - validateInputBufferUsage(gBuf); - bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, - layer.source.buffer.fence); - - usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; - Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); - mat4 texMatrix = layer.source.buffer.textureTransform; - - texture.setMatrix(texMatrix.asArray()); - texture.setFiltering(layer.source.buffer.useTextureFiltering); - - texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); - setSourceY410BT2020(layer.source.buffer.isY410BT2020); - - renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); - texCoords[0] = vec2(0.0, 0.0); - texCoords[1] = vec2(0.0, 1.0); - texCoords[2] = vec2(1.0, 1.0); - texCoords[3] = vec2(1.0, 0.0); - setupLayerTexturing(texture); - - // Do not cache protected EGLImage, protected memory is limited. - if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) { - unmapExternalTextureBuffer(std::move(gBuf)); - } - } - - const half3 solidColor = layer.source.solidColor; - const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); - const float radius = - (layer.geometry.roundedCornersRadius.x + layer.geometry.roundedCornersRadius.y) / - 2.0f; - // Buffer sources will have a black solid color ignored in the shader, - // so in that scenario the solid color passed here is arbitrary. - setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, radius); - if (layer.disableBlending) { - glDisable(GL_BLEND); - } - setSourceDataSpace(layer.sourceDataspace); - - if (layer.shadow.length > 0.0f) { - handleShadow(layer.geometry.boundaries, radius, layer.shadow); - } - // We only want to do a special handling for rounded corners when having rounded corners - // is the only reason it needs to turn on blending, otherwise, we handle it like the - // usual way since it needs to turn on blending anyway. - else if (radius > 0.0 && color.a >= 1.0f && isOpaque) { - handleRoundedCorners(display, layer, mesh); - } else { - drawMesh(mesh); - } - - // Cleanup if there's a buffer source - if (layer.source.buffer.buffer != nullptr) { - disableBlending(); - setSourceY410BT2020(false); - disableTexturing(); - } - } - - base::unique_fd drawFence = flush(); - - // If flush failed or we don't support native fences, we need to force the - // gl command stream to be executed. - if (drawFence.get() < 0) { - bool success = finish(); - if (!success) { - ALOGE("Failed to flush RenderEngine commands"); - checkErrors(); - // Chances are, something illegal happened (either the caller passed - // us bad parameters, or we messed up our shader generation). - resultPromise->set_value(base::unexpected(INVALID_OPERATION)); - return; - } - mLastDrawFence = nullptr; - } else { - // The caller takes ownership of drawFence, so we need to duplicate the - // fd here. - mLastDrawFence = new Fence(dup(drawFence.get())); - } - mPriorResourcesCleaned = false; - - checkErrors(); - resultPromise->set_value(sp<Fence>::make(std::move(drawFence))); -} - -void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { - ATRACE_CALL(); - mVpWidth = viewport.getWidth(); - mVpHeight = viewport.getHeight(); - - // We pass the the top left corner instead of the bottom left corner, - // because since we're rendering off-screen first. - glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight); - - mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1); -} - -void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color, float cornerRadius) { - mState.isPremultipliedAlpha = premultipliedAlpha; - mState.isOpaque = opaque; - mState.color = color; - mState.cornerRadius = cornerRadius; - - if (disableTexture) { - mState.textureEnabled = false; - } - - if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { - glEnable(GL_BLEND); - glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, - GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } else { - glDisable(GL_BLEND); - } -} - -void GLESRenderEngine::setSourceY410BT2020(bool enable) { - mState.isY410BT2020 = enable; -} - -void GLESRenderEngine::setSourceDataSpace(Dataspace source) { - mDataSpace = source; -} - -void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) { - mOutputDataSpace = dataspace; -} - -void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) { - mState.displayMaxLuminance = maxLuminance; -} - -void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { - GLuint target = texture.getTextureTarget(); - glBindTexture(target, texture.getTextureName()); - GLenum filter = GL_NEAREST; - if (texture.getFiltering()) { - filter = GL_LINEAR; - } - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); - - mState.texture = texture; - mState.textureEnabled = true; -} - -void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { - mState.colorMatrix = colorTransform; -} - -void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) { - mState.displayColorMatrix = colorTransform; -} - -void GLESRenderEngine::disableTexturing() { - mState.textureEnabled = false; -} - -void GLESRenderEngine::disableBlending() { - glDisable(GL_BLEND); -} - -void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) { - mState.isPremultipliedAlpha = true; - mState.isOpaque = false; - mState.color = half4(r, g, b, a); - mState.textureEnabled = false; - glDisable(GL_BLEND); -} - -void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) { - mState.cropSize = half2(width, height); -} - -void GLESRenderEngine::drawMesh(const Mesh& mesh) { - ATRACE_CALL(); - if (mesh.getTexCoordsSize()) { - glEnableVertexAttribArray(Program::texCoords); - glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getTexCoords()); - } - - glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getPositions()); - - if (mState.cornerRadius > 0.0f) { - glEnableVertexAttribArray(Program::cropCoords); - glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getCropCoords()); - } - - if (mState.drawShadows) { - glEnableVertexAttribArray(Program::shadowColor); - glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getShadowColor()); - - glEnableVertexAttribArray(Program::shadowParams); - glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE, - mesh.getByteStride(), mesh.getShadowParams()); - } - - Description managedState = mState; - // By default, DISPLAY_P3 is the only supported wide color output. However, - // when HDR content is present, hardware composer may be able to handle - // BT2020 data space, in that case, the output data space is set to be - // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need - // to respect this and convert non-HDR content to HDR format. - if (mUseColorManagement) { - Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); - Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); - Dataspace outputStandard = - static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK); - Dataspace outputTransfer = - static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); - bool needsXYZConversion = needsXYZTransformMatrix(); - - // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or - // STANDARD_BT2020, it will be treated as STANDARD_BT709 - if (inputStandard != Dataspace::STANDARD_DCI_P3 && - inputStandard != Dataspace::STANDARD_BT2020) { - inputStandard = Dataspace::STANDARD_BT709; - } - - if (needsXYZConversion) { - // The supported input color spaces are standard RGB, Display P3 and BT2020. - switch (inputStandard) { - case Dataspace::STANDARD_DCI_P3: - managedState.inputTransformMatrix = mDisplayP3ToXyz; - break; - case Dataspace::STANDARD_BT2020: - managedState.inputTransformMatrix = mBt2020ToXyz; - break; - default: - managedState.inputTransformMatrix = mSrgbToXyz; - break; - } - - // The supported output color spaces are BT2020, Display P3 and standard RGB. - switch (outputStandard) { - case Dataspace::STANDARD_BT2020: - managedState.outputTransformMatrix = mXyzToBt2020; - break; - case Dataspace::STANDARD_DCI_P3: - managedState.outputTransformMatrix = mXyzToDisplayP3; - break; - default: - managedState.outputTransformMatrix = mXyzToSrgb; - break; - } - } else if (inputStandard != outputStandard) { - // At this point, the input data space and output data space could be both - // HDR data spaces, but they match each other, we do nothing in this case. - // In addition to the case above, the input data space could be - // - scRGB linear - // - scRGB non-linear - // - sRGB - // - Display P3 - // - BT2020 - // The output data spaces could be - // - sRGB - // - Display P3 - // - BT2020 - switch (outputStandard) { - case Dataspace::STANDARD_BT2020: - if (inputStandard == Dataspace::STANDARD_BT709) { - managedState.outputTransformMatrix = mSrgbToBt2020; - } else if (inputStandard == Dataspace::STANDARD_DCI_P3) { - managedState.outputTransformMatrix = mDisplayP3ToBt2020; - } - break; - case Dataspace::STANDARD_DCI_P3: - if (inputStandard == Dataspace::STANDARD_BT709) { - managedState.outputTransformMatrix = mSrgbToDisplayP3; - } else if (inputStandard == Dataspace::STANDARD_BT2020) { - managedState.outputTransformMatrix = mBt2020ToDisplayP3; - } - break; - default: - if (inputStandard == Dataspace::STANDARD_DCI_P3) { - managedState.outputTransformMatrix = mDisplayP3ToSrgb; - } else if (inputStandard == Dataspace::STANDARD_BT2020) { - managedState.outputTransformMatrix = mBt2020ToSrgb; - } - break; - } - } - - // we need to convert the RGB value to linear space and convert it back when: - // - there is a color matrix that is not an identity matrix, or - // - there is an output transform matrix that is not an identity matrix, or - // - the input transfer function doesn't match the output transfer function. - if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || - inputTransfer != outputTransfer) { - managedState.inputTransferFunction = - Description::dataSpaceToTransferFunction(inputTransfer); - managedState.outputTransferFunction = - Description::dataSpaceToTransferFunction(outputTransfer); - } - } - - ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext, - managedState); - - if (mState.drawShadows) { - glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT, - mesh.getIndices()); - } else { - glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); - } - - if (mUseColorManagement && outputDebugPPMs) { - static uint64_t managedColorFrameCount = 0; - std::ostringstream out; - out << "/data/texture_out" << managedColorFrameCount++; - writePPM(out.str().c_str(), mVpWidth, mVpHeight); - } - - if (mesh.getTexCoordsSize()) { - glDisableVertexAttribArray(Program::texCoords); - } - - if (mState.cornerRadius > 0.0f) { - glDisableVertexAttribArray(Program::cropCoords); - } - - if (mState.drawShadows) { - glDisableVertexAttribArray(Program::shadowColor); - glDisableVertexAttribArray(Program::shadowParams); - } -} - -size_t GLESRenderEngine::getMaxTextureSize() const { - return mMaxTextureSize; -} - -size_t GLESRenderEngine::getMaxViewportDims() const { - return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; -} - -void GLESRenderEngine::dump(std::string& result) { - const GLExtensions& extensions = GLExtensions::getInstance(); - ProgramCache& cache = ProgramCache::getInstance(); - - StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); - StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); - StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), - extensions.getVersion()); - StringAppendF(&result, "%s\n", extensions.getExtensions()); - StringAppendF(&result, "RenderEngine supports protected context: %d\n", - supportsProtectedContent()); - StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); - StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n", - cache.getSize(mEGLContext)); - StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n", - cache.getSize(mProtectedEGLContext)); - StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", - dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), - dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); - { - std::lock_guard<std::mutex> lock(mRenderingMutex); - StringAppendF(&result, "RenderEngine image cache size: %zu\n", mImageCache.size()); - StringAppendF(&result, "Dumping buffer ids...\n"); - for (const auto& [id, unused] : mImageCache) { - StringAppendF(&result, "0x%" PRIx64 "\n", id); - } - } - { - std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); - StringAppendF(&result, "RenderEngine framebuffer image cache size: %zu\n", - mFramebufferImageCache.size()); - StringAppendF(&result, "Dumping buffer ids...\n"); - for (const auto& [id, unused] : mFramebufferImageCache) { - StringAppendF(&result, "0x%" PRIx64 "\n", id); - } - } -} - -GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { - int major, minor; - if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { - if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { - ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); - return GLES_VERSION_1_0; - } - } - - if (major == 1 && minor == 0) return GLES_VERSION_1_0; - if (major == 1 && minor >= 1) return GLES_VERSION_1_1; - if (major == 2 && minor >= 0) return GLES_VERSION_2_0; - if (major == 3 && minor >= 0) return GLES_VERSION_3_0; - - ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); - return GLES_VERSION_1_0; -} - -EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, - EGLContext shareContext, - std::optional<ContextPriority> contextPriority, - Protection protection) { - EGLint renderableType = 0; - if (config == EGL_NO_CONFIG) { - renderableType = EGL_OPENGL_ES3_BIT; - } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { - LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); - } - EGLint contextClientVersion = 0; - if (renderableType & EGL_OPENGL_ES3_BIT) { - contextClientVersion = 3; - } else if (renderableType & EGL_OPENGL_ES2_BIT) { - contextClientVersion = 2; - } else if (renderableType & EGL_OPENGL_ES_BIT) { - contextClientVersion = 1; - } else { - LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); - } - - std::vector<EGLint> contextAttributes; - contextAttributes.reserve(7); - contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); - contextAttributes.push_back(contextClientVersion); - if (contextPriority) { - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); - switch (*contextPriority) { - case ContextPriority::REALTIME: - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_REALTIME_NV); - break; - case ContextPriority::MEDIUM: - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_MEDIUM_IMG); - break; - case ContextPriority::LOW: - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LOW_IMG); - break; - case ContextPriority::HIGH: - default: - contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); - break; - } - } - if (protection == Protection::PROTECTED) { - contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); - contextAttributes.push_back(EGL_TRUE); - } - contextAttributes.push_back(EGL_NONE); - - EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); - - if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { - // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus - // EGL_NO_CONTEXT so that we can abort. - if (config != EGL_NO_CONFIG) { - return context; - } - // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should - // try to fall back to GLES 2. - contextAttributes[1] = 2; - context = eglCreateContext(display, config, shareContext, contextAttributes.data()); - } - - return context; -} - -EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config, - int hwcFormat, Protection protection) { - EGLConfig stubConfig = config; - if (stubConfig == EGL_NO_CONFIG) { - stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); - } - std::vector<EGLint> attributes; - attributes.reserve(7); - attributes.push_back(EGL_WIDTH); - attributes.push_back(1); - attributes.push_back(EGL_HEIGHT); - attributes.push_back(1); - if (protection == Protection::PROTECTED) { - attributes.push_back(EGL_PROTECTED_CONTENT_EXT); - attributes.push_back(EGL_TRUE); - } - attributes.push_back(EGL_NONE); - - return eglCreatePbufferSurface(display, stubConfig, attributes.data()); -} - -bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { - const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); - const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); - return standard == Dataspace::STANDARD_BT2020 && - (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); -} - -// For convenience, we want to convert the input color space to XYZ color space first, -// and then convert from XYZ color space to output color space when -// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or -// HDR content will be tone-mapped to SDR; Or, -// - there are HDR PQ and HLG contents presented at the same time, where we want to convert -// HLG content to PQ content. -// In either case above, we need to operate the Y value in XYZ color space. Thus, when either -// input data space or output data space is HDR data space, and the input transfer function -// doesn't match the output transfer function, we would enable an intermediate transfrom to -// XYZ color space. -bool GLESRenderEngine::needsXYZTransformMatrix() const { - const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); - const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); - const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); - const Dataspace outputTransfer = - static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); - - return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; -} - -bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mRenderingMutex); - const auto& cachedImage = mImageCache.find(bufferId); - return cachedImage != mImageCache.end(); -} - -bool GLESRenderEngine::isTextureNameKnownForTesting(uint32_t texName) { - const auto& entry = mTextureView.find(texName); - return entry != mTextureView.end(); -} - -std::optional<uint64_t> GLESRenderEngine::getBufferIdForTextureNameForTesting(uint32_t texName) { - const auto& entry = mTextureView.find(texName); - return entry != mTextureView.end() ? entry->second : std::nullopt; -} - -bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { - std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); - return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), - [=](std::pair<uint64_t, EGLImageKHR> image) { - return image.first == bufferId; - }); -} - -// FlushTracer implementation -GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) { - mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this); -} - -GLESRenderEngine::FlushTracer::~FlushTracer() { - { - std::lock_guard<std::mutex> lock(mMutex); - mRunning = false; - } - mCondition.notify_all(); - if (mThread.joinable()) { - mThread.join(); - } -} - -void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) { - std::lock_guard<std::mutex> lock(mMutex); - char name[64]; - const uint64_t frameNum = mFramesQueued++; - snprintf(name, sizeof(name), "Queueing sync for frame: %lu", - static_cast<unsigned long>(frameNum)); - ATRACE_NAME(name); - mQueue.push({sync, frameNum}); - ATRACE_INT("GPU Frames Outstanding", mQueue.size()); - mCondition.notify_one(); -} - -void GLESRenderEngine::FlushTracer::loop() { - while (mRunning) { - QueueEntry entry; - { - std::lock_guard<std::mutex> lock(mMutex); - - mCondition.wait(mMutex, - [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); - - if (!mRunning) { - // if mRunning is false, then FlushTracer is being destroyed, so - // bail out now. - break; - } - entry = mQueue.front(); - mQueue.pop(); - } - { - char name[64]; - snprintf(name, sizeof(name), "waiting for frame %lu", - static_cast<unsigned long>(entry.mFrameNum)); - ATRACE_NAME(name); - mEngine->waitSync(entry.mSync, 0); - } - } -} - -void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius, - const ShadowSettings& settings) { - ATRACE_CALL(); - const float casterZ = settings.length / 2.0f; - const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ, - settings.casterIsTranslucent, settings.ambientColor, - settings.spotColor, settings.lightPos, - settings.lightRadius); - - // setup mesh for both shadows - Mesh mesh = Mesh::Builder() - .setPrimitive(Mesh::TRIANGLES) - .setVertices(shadows.getVertexCount(), 2 /* size */) - .setShadowAttrs() - .setIndices(shadows.getIndexCount()) - .build(); - - Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>(); - Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>(); - Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>(); - shadows.fillVertices(position, shadowColor, shadowParams); - shadows.fillIndices(mesh.getIndicesArray()); - - mState.cornerRadius = 0.0f; - mState.drawShadows = true; - setupLayerTexturing(mShadowTexture->getTexture()); - drawMesh(mesh); - mState.drawShadows = false; -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h deleted file mode 100644 index 402ff529d7..0000000000 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_GLESRENDERENGINE_H_ -#define SF_GLESRENDERENGINE_H_ - -#include <condition_variable> -#include <deque> -#include <mutex> -#include <queue> -#include <thread> -#include <unordered_map> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <android-base/thread_annotations.h> -#include <renderengine/RenderEngine.h> -#include <renderengine/private/Description.h> -#include <sys/types.h> -#include <ui/FenceResult.h> -#include "GLShadowTexture.h" -#include "ImageManager.h" - -#define EGL_NO_CONFIG ((EGLConfig)0) - -namespace android { - -namespace renderengine { - -class Mesh; -class Texture; - -namespace gl { - -class GLImage; -class BlurFilter; - -class GLESRenderEngine : public RenderEngine { -public: - static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args); - - GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config, - EGLContext ctxt, EGLSurface stub, EGLContext protectedContext, - EGLSurface protectedStub); - ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); - - std::future<void> primeCache() override; - void genTextures(size_t count, uint32_t* names) override; - void deleteTextures(size_t count, uint32_t const* names) override; - bool isProtected() const { return mInProtectedContext; } - bool supportsProtectedContent() const override; - void useProtectedContext(bool useProtectedContext) override; - void cleanupPostRender() override; - int getContextPriority() override; - bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } - void onActiveDisplaySizeChanged(ui::Size size) override {} - - EGLDisplay getEGLDisplay() const { return mEGLDisplay; } - // Creates an output image for rendering to - EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, - bool useFramebufferCache) - EXCLUDES(mFramebufferImageCacheMutex); - - // Test-only methods - // Returns true iff mImageCache contains an image keyed by bufferId - bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); - // Returns true iff texName was previously generated by RenderEngine and was - // not destroyed. - bool isTextureNameKnownForTesting(uint32_t texName); - // Returns the buffer ID of the content bound to texName, or nullopt if no - // such mapping exists. - std::optional<uint64_t> getBufferIdForTextureNameForTesting(uint32_t texName); - // Returns true iff mFramebufferImageCache contains an image keyed by bufferId - bool isFramebufferImageCachedForTesting(uint64_t bufferId) - EXCLUDES(mFramebufferImageCacheMutex); - // These are wrappers around public methods above, but exposing Barrier - // objects so that tests can block. - std::shared_ptr<ImageManager::Barrier> cacheExternalTextureBufferForTesting( - const sp<GraphicBuffer>& buffer); - std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId); - -protected: - Framebuffer* getFramebufferForDrawing(); - void dump(std::string& result) override EXCLUDES(mRenderingMutex) - EXCLUDES(mFramebufferImageCacheMutex); - size_t getMaxTextureSize() const override; - size_t getMaxViewportDims() const override; - void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) - EXCLUDES(mRenderingMutex); - void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex); - bool canSkipPostRenderCleanup() const override; - void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, - const DisplaySettings& display, - const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) override; - -private: - friend class BindNativeBufferAsFramebuffer; - - enum GlesVersion { - GLES_VERSION_1_0 = 0x10000, - GLES_VERSION_1_1 = 0x10001, - GLES_VERSION_2_0 = 0x20000, - GLES_VERSION_3_0 = 0x30000, - }; - - static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); - static GlesVersion parseGlesVersion(const char* str); - static EGLContext createEglContext(EGLDisplay display, EGLConfig config, - EGLContext shareContext, - std::optional<ContextPriority> contextPriority, - Protection protection); - static std::optional<RenderEngine::ContextPriority> createContextPriority( - const RenderEngineCreationArgs& args); - static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config, - int hwcFormat, Protection protection); - std::unique_ptr<Framebuffer> createFramebuffer(); - std::unique_ptr<Image> createImage(); - void checkErrors() const; - void checkErrors(const char* tag) const; - void setScissor(const Rect& region); - void disableScissor(); - bool waitSync(EGLSyncKHR sync, EGLint flags); - status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) - EXCLUDES(mRenderingMutex); - void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); - status_t bindFrameBuffer(Framebuffer* framebuffer); - void unbindFrameBuffer(Framebuffer* framebuffer); - void bindExternalTextureImage(uint32_t texName, const Image& image); - void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) EXCLUDES(mRenderingMutex); - void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override; - - // A data space is considered HDR data space if it has BT2020 color space - // with PQ or HLG transfer function. - bool isHdrDataSpace(const ui::Dataspace dataSpace) const; - bool needsXYZTransformMatrix() const; - // Defines the viewport, and sets the projection matrix to the projection - // defined by the clip. - void setViewportAndProjection(Rect viewport, Rect clip); - // Evicts stale images from the buffer cache. - void evictImages(const std::vector<LayerSettings>& layers); - // Computes the cropping window for the layer and sets up cropping - // coordinates for the mesh. - FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh); - - // We do a special handling for rounded corners when it's possible to turn off blending - // for the majority of the layer. The rounded corners needs to turn on blending such that - // we can set the alpha value correctly, however, only the corners need this, and since - // blending is an expensive operation, we want to turn off blending when it's not necessary. - void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer, - const Mesh& mesh); - base::unique_fd flush(); - bool finish(); - bool waitFence(base::unique_fd fenceFd); - void clearWithColor(float red, float green, float blue, float alpha); - void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha); - void handleShadow(const FloatRect& casterRect, float casterCornerRadius, - const ShadowSettings& shadowSettings); - void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color, float cornerRadius); - void setupLayerTexturing(const Texture& texture); - void setupFillWithColor(float r, float g, float b, float a); - void setColorTransform(const mat4& colorTransform); - void setDisplayColorTransform(const mat4& colorTransform); - void disableTexturing(); - void disableBlending(); - void setupCornerRadiusCropSize(float width, float height); - - // HDR and color management related functions and state - void setSourceY410BT2020(bool enable); - void setSourceDataSpace(ui::Dataspace source); - void setOutputDataSpace(ui::Dataspace dataspace); - void setDisplayMaxLuminance(const float maxLuminance); - - // drawing - void drawMesh(const Mesh& mesh); - - EGLDisplay mEGLDisplay; - EGLConfig mEGLConfig; - EGLContext mEGLContext; - EGLSurface mStubSurface; - EGLContext mProtectedEGLContext; - EGLSurface mProtectedStubSurface; - GLint mMaxViewportDims[2]; - GLint mMaxTextureSize; - GLuint mVpWidth; - GLuint mVpHeight; - Description mState; - std::unique_ptr<GLShadowTexture> mShadowTexture = nullptr; - - mat4 mSrgbToXyz; - mat4 mDisplayP3ToXyz; - mat4 mBt2020ToXyz; - mat4 mXyzToSrgb; - mat4 mXyzToDisplayP3; - mat4 mXyzToBt2020; - mat4 mSrgbToDisplayP3; - mat4 mSrgbToBt2020; - mat4 mDisplayP3ToSrgb; - mat4 mDisplayP3ToBt2020; - mat4 mBt2020ToSrgb; - mat4 mBt2020ToDisplayP3; - - bool mInProtectedContext = false; - // If set to true, then enables tracing flush() and finish() to systrace. - bool mTraceGpuCompletion = false; - // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately) - // the last recently used buffer should be kicked out. - uint32_t mFramebufferImageCacheSize = 0; - - // Cache of output images, keyed by corresponding GraphicBuffer ID. - std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache - GUARDED_BY(mFramebufferImageCacheMutex); - // The only reason why we have this mutex is so that we don't segfault when - // dumping info. - std::mutex mFramebufferImageCacheMutex; - - // Current dataspace of layer being rendered - ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; - - // Current output dataspace of the render engine - ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; - - // Whether device supports color management, currently color management - // supports sRGB, DisplayP3 color spaces. - const bool mUseColorManagement = false; - - // Whether only shaders performing tone mapping from HDR to SDR will be generated on - // primeCache(). - const bool mPrecacheToneMapperShaderOnly = false; - - // Cache of GL images that we'll store per GraphicBuffer ID - std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); - std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView; - - // Mutex guarding rendering operations, so that: - // 1. GL operations aren't interleaved, and - // 2. Internal state related to rendering that is potentially modified by - // multiple threads is guaranteed thread-safe. - std::mutex mRenderingMutex; - - std::unique_ptr<Framebuffer> mDrawingBuffer; - // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more - // memory or if it needs to satisfy alignment requirements. In this case: - // assume that each channel requires 4 bytes, and add 3 additional bytes to - // ensure that we align on a word. Allocating 16 bytes will provide a - // guarantee that we don't clobber memory. - uint32_t mPlaceholderDrawBuffer[4]; - // Placeholder buffer and image, similar to mPlaceholderDrawBuffer, but - // instead these are intended for cleaning up texture memory with the - // GL_TEXTURE_EXTERNAL_OES target. - ANativeWindowBuffer* mPlaceholderBuffer = nullptr; - EGLImage mPlaceholderImage = EGL_NO_IMAGE_KHR; - sp<Fence> mLastDrawFence; - // Store a separate boolean checking if prior resources were cleaned up, as - // devices that don't support native sync fences can't rely on a last draw - // fence that doesn't exist. - bool mPriorResourcesCleaned = true; - - // Blur effect processor, only instantiated when a layer requests it. - BlurFilter* mBlurFilter = nullptr; - - class FlushTracer { - public: - FlushTracer(GLESRenderEngine* engine); - ~FlushTracer(); - void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex); - - struct QueueEntry { - EGLSyncKHR mSync = nullptr; - uint64_t mFrameNum = 0; - }; - - private: - void loop(); - GLESRenderEngine* const mEngine; - std::thread mThread; - std::condition_variable_any mCondition; - std::mutex mMutex; - std::queue<QueueEntry> mQueue GUARDED_BY(mMutex); - uint64_t mFramesQueued GUARDED_BY(mMutex) = 0; - bool mRunning = true; - }; - friend class FlushTracer; - friend class ImageManager; - friend class GLFramebuffer; - friend class BlurFilter; - friend class GenericProgram; - std::unique_ptr<FlushTracer> mFlushTracer; - std::unique_ptr<ImageManager> mImageManager; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android - -#endif /* SF_GLESRENDERENGINE_H_ */ diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp deleted file mode 100644 index 58d6caa48a..0000000000 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "GLFramebuffer.h" - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> -#include <gui/DebugEGLImageTracker.h> -#include <nativebase/nativebase.h> -#include <utils/Trace.h> -#include "GLESRenderEngine.h" - -namespace android { -namespace renderengine { -namespace gl { - -GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) - : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { - glGenTextures(1, &mTextureName); - glGenFramebuffers(1, &mFramebufferName); -} - -GLFramebuffer::~GLFramebuffer() { - setNativeWindowBuffer(nullptr, false, false); - glDeleteFramebuffers(1, &mFramebufferName); - glDeleteTextures(1, &mTextureName); -} - -bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, - const bool useFramebufferCache) { - ATRACE_CALL(); - if (mEGLImage != EGL_NO_IMAGE_KHR) { - if (!usingFramebufferCache) { - eglDestroyImageKHR(mEGLDisplay, mEGLImage); - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - } - mEGLImage = EGL_NO_IMAGE_KHR; - mBufferWidth = 0; - mBufferHeight = 0; - } - - if (nativeBuffer) { - mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected, - useFramebufferCache); - if (mEGLImage == EGL_NO_IMAGE_KHR) { - return false; - } - usingFramebufferCache = useFramebufferCache; - mBufferWidth = nativeBuffer->width; - mBufferHeight = nativeBuffer->height; - } - return true; -} - -void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) { - ATRACE_CALL(); - - glBindTexture(GL_TEXTURE_2D, mTextureName); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - - mBufferHeight = height; - mBufferWidth = width; - mEngine.checkErrors("Allocating Fbo texture"); - - bind(); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0); - mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); - unbind(); - glBindTexture(GL_TEXTURE_2D, 0); - - if (mStatus != GL_FRAMEBUFFER_COMPLETE) { - ALOGE("Frame buffer is not complete. Error %d", mStatus); - } -} - -void GLFramebuffer::bind() const { - glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName); -} - -void GLFramebuffer::bindAsReadBuffer() const { - glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName); -} - -void GLFramebuffer::bindAsDrawBuffer() const { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName); -} - -void GLFramebuffer::unbind() const { - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h deleted file mode 100644 index 6757695ddb..0000000000 --- a/libs/renderengine/gl/GLFramebuffer.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <cstdint> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <renderengine/Framebuffer.h> - -struct ANativeWindowBuffer; - -namespace android { -namespace renderengine { -namespace gl { - -class GLESRenderEngine; - -class GLFramebuffer : public renderengine::Framebuffer { -public: - explicit GLFramebuffer(GLESRenderEngine& engine); - explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget); - ~GLFramebuffer() override; - - bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, - const bool useFramebufferCache) override; - void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr); - EGLImageKHR getEGLImage() const { return mEGLImage; } - uint32_t getTextureName() const { return mTextureName; } - uint32_t getFramebufferName() const { return mFramebufferName; } - int32_t getBufferHeight() const { return mBufferHeight; } - int32_t getBufferWidth() const { return mBufferWidth; } - GLenum getStatus() const { return mStatus; } - void bind() const; - void bindAsReadBuffer() const; - void bindAsDrawBuffer() const; - void unbind() const; - -private: - GLESRenderEngine& mEngine; - EGLDisplay mEGLDisplay; - EGLImageKHR mEGLImage; - bool usingFramebufferCache = false; - GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED; - uint32_t mTextureName, mFramebufferName; - - int32_t mBufferHeight = 0; - int32_t mBufferWidth = 0; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp deleted file mode 100644 index 8497721956..0000000000 --- a/libs/renderengine/gl/GLImage.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "GLImage.h" - -#include <vector> - -#include <gui/DebugEGLImageTracker.h> -#include <log/log.h> -#include <utils/Trace.h> -#include "GLESRenderEngine.h" -#include "GLExtensions.h" - -namespace android { -namespace renderengine { -namespace gl { - -static std::vector<EGLint> buildAttributeList(bool isProtected) { - std::vector<EGLint> attrs; - attrs.reserve(16); - - attrs.push_back(EGL_IMAGE_PRESERVED_KHR); - attrs.push_back(EGL_TRUE); - - if (isProtected && GLExtensions::getInstance().hasProtectedContent()) { - attrs.push_back(EGL_PROTECTED_CONTENT_EXT); - attrs.push_back(EGL_TRUE); - } - - attrs.push_back(EGL_NONE); - - return attrs; -} - -GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} - -GLImage::~GLImage() { - setNativeWindowBuffer(nullptr, false); -} - -bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) { - ATRACE_CALL(); - if (mEGLImage != EGL_NO_IMAGE_KHR) { - if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { - ALOGE("failed to destroy image: %#x", eglGetError()); - } - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - mEGLImage = EGL_NO_IMAGE_KHR; - } - - if (buffer) { - std::vector<EGLint> attrs = buildAttributeList(isProtected); - mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - static_cast<EGLClientBuffer>(buffer), attrs.data()); - if (mEGLImage == EGL_NO_IMAGE_KHR) { - ALOGE("failed to create EGLImage: %#x", eglGetError()); - return false; - } - DEBUG_EGL_IMAGE_TRACKER_CREATE(); - mProtected = isProtected; - } - - return true; -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h deleted file mode 100644 index 59d6ce3549..0000000000 --- a/libs/renderengine/gl/GLImage.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <cstdint> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <android-base/macros.h> -#include <renderengine/Image.h> - -struct ANativeWindowBuffer; - -namespace android { -namespace renderengine { -namespace gl { - -class GLESRenderEngine; - -class GLImage : public renderengine::Image { -public: - explicit GLImage(const GLESRenderEngine& engine); - ~GLImage() override; - - bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; - - EGLImageKHR getEGLImage() const { return mEGLImage; } - bool isProtected() const { return mProtected; } - -private: - EGLDisplay mEGLDisplay; - EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; - bool mProtected = false; - - DISALLOW_COPY_AND_ASSIGN(GLImage); -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp deleted file mode 100644 index 2423a3467e..0000000000 --- a/libs/renderengine/gl/GLShadowTexture.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <GLES3/gl3.h> - -#include "GLShadowTexture.h" -#include "GLSkiaShadowPort.h" - -namespace android { -namespace renderengine { -namespace gl { - -GLShadowTexture::GLShadowTexture() { - fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH); - - glGenTextures(1, &mName); - glBindTexture(GL_TEXTURE_2D, mName); - glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH, - SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData); - mTexture.init(Texture::TEXTURE_2D, mName); - mTexture.setFiltering(true); - mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1); -} - -GLShadowTexture::~GLShadowTexture() { - glDeleteTextures(1, &mName); -} - -const Texture& GLShadowTexture::getTexture() { - return mTexture; -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h deleted file mode 100644 index 250a9d77d0..0000000000 --- a/libs/renderengine/gl/GLShadowTexture.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <renderengine/Texture.h> -#include <cstdint> - -namespace android { -namespace renderengine { -namespace gl { - -class GLShadowTexture { -public: - GLShadowTexture(); - ~GLShadowTexture(); - - const Texture& getTexture(); - -private: - static constexpr int SHADOW_TEXTURE_WIDTH = 128; - static constexpr int SHADOW_TEXTURE_HEIGHT = 1; - - GLuint mName; - Texture mTexture; - uint8_t mTextureData[SHADOW_TEXTURE_WIDTH]; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp deleted file mode 100644 index 3181f9bebb..0000000000 --- a/libs/renderengine/gl/GLShadowVertexGenerator.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <renderengine/Mesh.h> - -#include <math/vec4.h> - -#include <ui/Rect.h> -#include <ui/Transform.h> - -#include "GLShadowVertexGenerator.h" - -namespace android { -namespace renderengine { -namespace gl { - -GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect, - float casterCornerRadius, float casterZ, - bool casterIsTranslucent, const vec4& ambientColor, - const vec4& spotColor, const vec3& lightPosition, - float lightRadius) { - mDrawAmbientShadow = ambientColor.a > 0.f; - mDrawSpotShadow = spotColor.a > 0.f; - - // Generate geometries and find number of vertices to generate - if (mDrawAmbientShadow) { - mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ, - casterIsTranslucent, ambientColor); - mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get()); - mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get()); - } else { - mAmbientShadowVertexCount = 0; - mAmbientShadowIndexCount = 0; - } - - if (mDrawSpotShadow) { - mSpotShadowGeometry = - getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent, - spotColor, lightPosition, lightRadius); - mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get()); - mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get()); - } else { - mSpotShadowVertexCount = 0; - mSpotShadowIndexCount = 0; - } -} - -size_t GLShadowVertexGenerator::getVertexCount() const { - return mAmbientShadowVertexCount + mSpotShadowVertexCount; -} - -size_t GLShadowVertexGenerator::getIndexCount() const { - return mAmbientShadowIndexCount + mSpotShadowIndexCount; -} - -void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position, - Mesh::VertexArray<vec4>& color, - Mesh::VertexArray<vec3>& params) const { - if (mDrawAmbientShadow) { - fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position, - color, params); - } - if (mDrawSpotShadow) { - fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount, - Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount), - Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount), - Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount)); - } -} - -void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const { - if (mDrawAmbientShadow) { - fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount, - 0 /* starting vertex offset */, indices); - } - if (mDrawSpotShadow) { - fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount, - mAmbientShadowVertexCount /* starting vertex offset */, - &(indices[mAmbientShadowIndexCount])); - } -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h deleted file mode 100644 index 112f97623b..0000000000 --- a/libs/renderengine/gl/GLShadowVertexGenerator.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <math/vec4.h> -#include <ui/Rect.h> - -#include "GLSkiaShadowPort.h" - -namespace android { -namespace renderengine { - -class Mesh; - -namespace gl { - -/** - * Generates gl attributes required to draw shadow spot and/or ambient shadows. - * - * Each shadow can support different colors. This class generates three vertex attributes for - * each shadow, its position, color and shadow params(offset and distance). These can be sent - * using a single glDrawElements call. - */ -class GLShadowVertexGenerator { -public: - GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ, - bool casterIsTranslucent, const vec4& ambientColor, - const vec4& spotColor, const vec3& lightPosition, float lightRadius); - ~GLShadowVertexGenerator() = default; - - size_t getVertexCount() const; - size_t getIndexCount() const; - void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color, - Mesh::VertexArray<vec3>& params) const; - void fillIndices(uint16_t* indices) const; - -private: - bool mDrawAmbientShadow; - std::unique_ptr<Geometry> mAmbientShadowGeometry; - int mAmbientShadowVertexCount = 0; - int mAmbientShadowIndexCount = 0; - - bool mDrawSpotShadow; - std::unique_ptr<Geometry> mSpotShadowGeometry; - int mSpotShadowVertexCount = 0; - int mSpotShadowIndexCount = 0; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp deleted file mode 100644 index da8b435854..0000000000 --- a/libs/renderengine/gl/GLSkiaShadowPort.cpp +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <math/vec4.h> - -#include <renderengine/Mesh.h> - -#include <ui/Rect.h> -#include <ui/Transform.h> - -#include <utils/Log.h> - -#include "GLSkiaShadowPort.h" - -namespace android { -namespace renderengine { -namespace gl { - -/** - * The shadow geometry logic and vertex generation code has been ported from skia shadow - * fast path OpenGL implementation to draw shadows around rects and rounded rects including - * circles. - * - * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow - * - * Modifications made: - * - Switched to using std lib math functions - * - Fall off function is implemented in vertex shader rather than a shadow texture - * - Removed transformations applied on the caster rect since the caster will be in local - * coordinate space and will be transformed by the vertex shader. - */ - -static inline float divide_and_pin(float numer, float denom, float min, float max) { - if (denom == 0.0f) return min; - return std::clamp(numer / denom, min, max); -} - -static constexpr auto SK_ScalarSqrt2 = 1.41421356f; -static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f; -static constexpr auto kAmbientGeomFactor = 64.0f; -// Assuming that we have a light height of 600 for the spot shadow, -// the spot values will reach their maximum at a height of approximately 292.3077. -// We'll round up to 300 to keep it simple. -static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor; - -inline float AmbientBlurRadius(float height) { - return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius); -} -inline float AmbientRecipAlpha(float height) { - return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f); -} - -////////////////////////////////////////////////////////////////////////////// -// Circle Data -// -// We have two possible cases for geometry for a circle: - -// In the case of a normal fill, we draw geometry for the circle as an octagon. -static const uint16_t gFillCircleIndices[] = { - // enter the octagon - // clang-format off - 0, 1, 8, 1, 2, 8, - 2, 3, 8, 3, 4, 8, - 4, 5, 8, 5, 6, 8, - 6, 7, 8, 7, 0, 8, - // clang-format on -}; - -// For stroked circles, we use two nested octagons. -static const uint16_t gStrokeCircleIndices[] = { - // enter the octagon - // clang-format off - 0, 1, 9, 0, 9, 8, - 1, 2, 10, 1, 10, 9, - 2, 3, 11, 2, 11, 10, - 3, 4, 12, 3, 12, 11, - 4, 5, 13, 4, 13, 12, - 5, 6, 14, 5, 14, 13, - 6, 7, 15, 6, 15, 14, - 7, 0, 8, 7, 8, 15, - // clang-format on -}; - -#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0])) -static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); -static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); -static const int kVertsPerStrokeCircle = 16; -static const int kVertsPerFillCircle = 9; - -static int circle_type_to_vert_count(bool stroked) { - return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; -} - -static int circle_type_to_index_count(bool stroked) { - return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; -} - -static const uint16_t* circle_type_to_indices(bool stroked) { - return stroked ? gStrokeCircleIndices : gFillCircleIndices; -} - -/////////////////////////////////////////////////////////////////////////////// -// RoundRect Data -// -// The geometry for a shadow roundrect is similar to a 9-patch: -// ____________ -// |_|________|_| -// | | | | -// | | | | -// | | | | -// |_|________|_| -// |_|________|_| -// -// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram -// shows the upper part of the upper left corner. The bottom triangle would similarly be split -// into two triangles.) -// ________ -// |\ \ | -// | \ \ | -// | \\ | -// | \| -// -------- -// -// The center of the fan handles the curve of the corner. For roundrects where the stroke width -// is greater than the corner radius, the outer triangles blend from the curve to the straight -// sides. Otherwise these triangles will be degenerate. -// -// In the case where the stroke width is greater than the corner radius and the -// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center. -// This rectangle extends the coverage values of the center edges of the 9-patch. -// ____________ -// |_|________|_| -// | |\ ____ /| | -// | | | | | | -// | | |____| | | -// |_|/______\|_| -// |_|________|_| -// -// For filled rrects we reuse the stroke geometry but add an additional quad to the center. - -static const uint16_t gRRectIndices[] = { - // clang-format off - // overstroke quads - // we place this at the beginning so that we can skip these indices when rendering as filled - 0, 6, 25, 0, 25, 24, - 6, 18, 27, 6, 27, 25, - 18, 12, 26, 18, 26, 27, - 12, 0, 24, 12, 24, 26, - - // corners - 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, - 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7, - 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13, - 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23, - - // edges - 0, 5, 11, 0, 11, 6, - 6, 7, 19, 6, 19, 18, - 18, 23, 17, 18, 17, 12, - 12, 13, 1, 12, 1, 0, - - // fill quad - // we place this at the end so that we can skip these indices when rendering as stroked - 0, 6, 18, 0, 18, 12, - // clang-format on -}; - -// overstroke count -static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; -// simple stroke count skips overstroke indices -static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4; -// fill count adds final quad to stroke count -static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6; -static const int kVertsPerStrokeRRect = 24; -static const int kVertsPerOverstrokeRRect = 28; -static const int kVertsPerFillRRect = 24; - -static int rrect_type_to_vert_count(RRectType type) { - switch (type) { - case kFill_RRectType: - return kVertsPerFillRRect; - case kStroke_RRectType: - return kVertsPerStrokeRRect; - case kOverstroke_RRectType: - return kVertsPerOverstrokeRRect; - } - ALOGE("Invalid rect type: %d", type); - return -1; -} - -static int rrect_type_to_index_count(RRectType type) { - switch (type) { - case kFill_RRectType: - return kIndicesPerFillRRect; - case kStroke_RRectType: - return kIndicesPerStrokeRRect; - case kOverstroke_RRectType: - return kIndicesPerOverstrokeRRect; - } - ALOGE("Invalid rect type: %d", type); - return -1; -} - -static const uint16_t* rrect_type_to_indices(RRectType type) { - switch (type) { - case kFill_RRectType: - case kStroke_RRectType: - return gRRectIndices + 6 * 4; - case kOverstroke_RRectType: - return gRRectIndices; - } - ALOGE("Invalid rect type: %d", type); - return nullptr; -} - -static void fillInCircleVerts(const Geometry& args, bool isStroked, - Mesh::VertexArray<vec2>& position, - Mesh::VertexArray<vec4>& shadowColor, - Mesh::VertexArray<vec3>& shadowParams) { - vec4 color = args.fColor; - float outerRadius = args.fOuterRadius; - float innerRadius = args.fInnerRadius; - float blurRadius = args.fBlurRadius; - float distanceCorrection = outerRadius / blurRadius; - - const FloatRect& bounds = args.fDevBounds; - - // The inner radius in the vertex data must be specified in normalized space. - innerRadius = innerRadius / outerRadius; - - vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f); - float halfWidth = 0.5f * bounds.getWidth(); - float octOffset = 0.41421356237f; // sqrt(2) - 1 - int vertexCount = 0; - - position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection); - vertexCount++; - - if (isStroked) { - // compute the inner ring - - // cosine and sine of pi/8 - float c = 0.923579533f; - float s = 0.382683432f; - float r = args.fInnerRadius; - - position[vertexCount] = center + vec2(-s * r, -c * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(s * r, -c * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(c * r, -s * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(c * r, s * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(s * r, c * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-s * r, c * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-c * r, s * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection); - vertexCount++; - - position[vertexCount] = center + vec2(-c * r, -s * r); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection); - vertexCount++; - } else { - // filled - position[vertexCount] = center; - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - } -} - -static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position, - Mesh::VertexArray<vec4>& shadowColor, - Mesh::VertexArray<vec3>& shadowParams) { - vec4 color = args.fColor; - float outerRadius = args.fOuterRadius; - - const FloatRect& bounds = args.fDevBounds; - - float umbraInset = args.fUmbraInset; - float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight()); - if (umbraInset > minDim) { - umbraInset = minDim; - } - - float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset, - bounds.left + umbraInset, bounds.right - umbraInset}; - float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius, - bounds.left + outerRadius, bounds.right - outerRadius}; - float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right}; - float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset, - bounds.bottom - umbraInset}; - float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius, - bounds.bottom - outerRadius, bounds.bottom - outerRadius}; - float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom}; - - float blurRadius = args.fBlurRadius; - - // In the case where we have to inset more for the umbra, our two triangles in the - // corner get skewed to a diamond rather than a square. To correct for that, - // we also skew the vectors we send to the shader that help define the circle. - // By doing so, we end up with a quarter circle in the corner rather than the - // elliptical curve. - - // This is a bit magical, but it gives us the correct results at extrema: - // a) umbraInset == outerRadius produces an orthogonal vector - // b) outerRadius == 0 produces a diagonal vector - // And visually the corner looks correct. - vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset); - outerVec = normalize(outerVec); - // We want the circle edge to fall fractionally along the diagonal at - // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset - // - // Setting the components of the diagonal offset to the following value will give us that. - float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius); - vec2 diagVec = vec2(diagVal, diagVal); - float distanceCorrection = umbraInset / blurRadius; - - int vertexCount = 0; - // build corner by corner - for (int i = 0; i < 4; ++i) { - // inner point - position[vertexCount] = vec2(xInner[i], yInner[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - - // outer points - position[vertexCount] = vec2(xOuter[i], yInner[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, -1, distanceCorrection); - vertexCount++; - - position[vertexCount] = vec2(xOuter[i], yMid[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection); - vertexCount++; - - position[vertexCount] = vec2(xOuter[i], yOuter[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection); - vertexCount++; - - position[vertexCount] = vec2(xMid[i], yOuter[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection); - vertexCount++; - - position[vertexCount] = vec2(xInner[i], yOuter[i]); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, -1, distanceCorrection); - vertexCount++; - } - - // Add the additional vertices for overstroked rrects. - // Effectively this is an additional stroked rrect, with its - // parameters equal to those in the center of the 9-patch. This will - // give constant values across this inner ring. - if (kOverstroke_RRectType == args.fType) { - float inset = umbraInset + args.fInnerRadius; - - // TL - position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - - // TR - position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - - // BL - position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - - // BR - position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset); - shadowColor[vertexCount] = color; - shadowParams[vertexCount] = vec3(0, 0, distanceCorrection); - vertexCount++; - } -} - -int getVertexCountForGeometry(const Geometry& shadowGeometry) { - if (shadowGeometry.fIsCircle) { - return circle_type_to_vert_count(shadowGeometry.fType); - } - - return rrect_type_to_vert_count(shadowGeometry.fType); -} - -int getIndexCountForGeometry(const Geometry& shadowGeometry) { - if (shadowGeometry.fIsCircle) { - return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType); - } - - return rrect_type_to_index_count(shadowGeometry.fType); -} - -void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */, - Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor, - Mesh::VertexArray<vec3> shadowParams) { - if (shadowGeometry.fIsCircle) { - fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor, - shadowParams); - } else { - fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams); - } -} - -void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount, - int startingVertexOffset, uint16_t* indices) { - if (shadowGeometry.fIsCircle) { - const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked); - for (int i = 0; i < indexCount; ++i) { - indices[i] = primIndices[i] + startingVertexOffset; - } - } else { - const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType); - for (int i = 0; i < indexCount; ++i) { - indices[i] = primIndices[i] + startingVertexOffset; - } - } -} - -inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ, - float lightRadius, float& blurRadius, float& scale, vec2& translate) { - float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f); - blurRadius = lightRadius * zRatio; - scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f); - translate.x = -zRatio * lightX; - translate.y = -zRatio * lightY; -} - -static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect, - float devRadius, float blurRadius, - float insetWidth) { - // An insetWidth > 1/2 rect width or height indicates a simple fill. - const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight())); - - FloatRect bounds = devRect; - float innerRadius = 0.0f; - float outerRadius = devRadius; - float umbraInset; - - RRectType type = kFill_RRectType; - if (isCircle) { - umbraInset = 0; - } else { - umbraInset = std::max(outerRadius, blurRadius); - } - - // If stroke is greater than width or height, this is still a fill, - // otherwise we compute stroke params. - if (isCircle) { - innerRadius = devRadius - insetWidth; - type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType; - } else { - if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) { - // We don't worry about a real inner radius, we just need to know if we - // need to create overstroke vertices. - innerRadius = std::max(insetWidth - umbraInset, 0.0f); - type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType; - } - } - const bool isStroked = (kStroke_RRectType == type); - return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius, - blurRadius, bounds, type, isCircle, isStroked}); -} - -std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect, - float casterCornerRadius, float casterZ, - bool casterIsTranslucent, - const vec4& ambientColor) { - float devSpaceInsetWidth = AmbientBlurRadius(casterZ); - const float umbraRecipAlpha = AmbientRecipAlpha(casterZ); - const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha; - - // Outset the shadow rrect to the border of the penumbra - float ambientPathOutset = devSpaceInsetWidth; - FloatRect outsetRect(casterRect); - outsetRect.left -= ambientPathOutset; - outsetRect.top -= ambientPathOutset; - outsetRect.right += ambientPathOutset; - outsetRect.bottom += ambientPathOutset; - - float outsetRad = casterCornerRadius + ambientPathOutset; - if (casterIsTranslucent) { - // set a large inset to force a fill - devSpaceInsetWidth = outsetRect.getWidth(); - } - - return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur, - std::abs(devSpaceInsetWidth)); -} - -std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect, - float casterCornerRadius, float casterZ, - bool casterIsTranslucent, const vec4& spotColor, - const vec3& lightPosition, float lightRadius) { - float devSpaceSpotBlur; - float spotScale; - vec2 spotOffset; - GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius, - devSpaceSpotBlur, spotScale, spotOffset); - // handle scale of radius due to CTM - const float srcSpaceSpotBlur = devSpaceSpotBlur; - - // Adjust translate for the effect of the scale. - spotOffset.x += spotScale; - spotOffset.y += spotScale; - - // Compute the transformed shadow rect - ui::Transform shadowTransform; - shadowTransform.set(spotOffset.x, spotOffset.y); - shadowTransform.set(spotScale, 0, 0, spotScale); - FloatRect spotShadowRect = shadowTransform.transform(casterRect); - float spotShadowRadius = casterCornerRadius * spotScale; - - // Compute the insetWidth - float blurOutset = srcSpaceSpotBlur; - float insetWidth = blurOutset; - if (casterIsTranslucent) { - // If transparent, just do a fill - insetWidth += spotShadowRect.getWidth(); - } else { - // For shadows, instead of using a stroke we specify an inset from the penumbra - // border. We want to extend this inset area so that it meets up with the caster - // geometry. The inset geometry will by default already be inset by the blur width. - // - // We compare the min and max corners inset by the radius between the original - // rrect and the shadow rrect. The distance between the two plus the difference - // between the scaled radius and the original radius gives the distance from the - // transformed shadow shape to the original shape in that corner. The max - // of these gives the maximum distance we need to cover. - // - // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to - // that to get the full insetWidth. - float maxOffset; - if (casterCornerRadius <= 0.f) { - // Manhattan distance works better for rects - maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left), - std::abs(spotShadowRect.top - casterRect.top)), - std::max(std::abs(spotShadowRect.right - casterRect.right), - std::abs(spotShadowRect.bottom - casterRect.bottom))); - } else { - float dr = spotShadowRadius - casterCornerRadius; - vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr, - spotShadowRect.top - casterRect.top + dr); - vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr, - spotShadowRect.bottom - casterRect.bottom - dr); - maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset), - dot(lowerRightOffset, lowerRightOffset))) + - dr; - } - insetWidth += std::max(blurOutset, maxOffset); - } - - // Outset the shadow rrect to the border of the penumbra - spotShadowRadius += blurOutset; - spotShadowRect.left -= blurOutset; - spotShadowRect.top -= blurOutset; - spotShadowRect.right += blurOutset; - spotShadowRect.bottom += blurOutset; - - return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius), - 2.0f * devSpaceSpotBlur, std::abs(insetWidth)); -} - -void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) { - for (int i = 0; i < shadowTextureWidth; i++) { - const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f); - data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255); - } -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h deleted file mode 100644 index 912c8bb7b3..0000000000 --- a/libs/renderengine/gl/GLSkiaShadowPort.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <math/vec4.h> -#include <renderengine/Mesh.h> -#include <ui/Rect.h> - -namespace android { -namespace renderengine { -namespace gl { - -/** - * The shadow geometry logic and vertex generation code has been ported from skia shadow - * fast path OpenGL implementation to draw shadows around rects and rounded rects including - * circles. - * - * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow - * - * Modifications made: - * - Switched to using std lib math functions - * - Fall off function is implemented in vertex shader rather than a shadow texture - * - Removed transformations applied on the caster rect since the caster will be in local - * coordinate space and will be transformed by the vertex shader. - */ - -enum RRectType { - kFill_RRectType, - kStroke_RRectType, - kOverstroke_RRectType, -}; - -struct Geometry { - vec4 fColor; - float fOuterRadius; - float fUmbraInset; - float fInnerRadius; - float fBlurRadius; - FloatRect fDevBounds; - RRectType fType; - bool fIsCircle; - bool fIsStroked; -}; - -std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect, - float casterCornerRadius, float casterZ, - bool casterIsTranslucent, const vec4& spotColor, - const vec3& lightPosition, float lightRadius); - -std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect, - float casterCornerRadius, float casterZ, - bool casterIsTranslucent, - const vec4& ambientColor); - -int getVertexCountForGeometry(const Geometry& shadowGeometry); - -int getIndexCountForGeometry(const Geometry& shadowGeometry); - -void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount, - Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor, - Mesh::VertexArray<vec3> shadowParams); - -void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount, - int startingVertexOffset, uint16_t* indices); - -/** - * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to - * darkness at that spot. Values are determined by an exponential falloff - * function provided by UX. - * - * The texture is used for quick lookup in theshadow shader. - * - * textureData - filled with shadow texture data that needs to be at least of - * size textureWidth - * - * textureWidth - width of the texture, height is always 1 - */ -void fillShadowTextureData(uint8_t* textureData, size_t textureWidth); - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp deleted file mode 100644 index e50c471b6d..0000000000 --- a/libs/renderengine/gl/GLVertexBuffer.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "GLVertexBuffer.h" - -#include <GLES/gl.h> -#include <GLES2/gl2.h> -#include <nativebase/nativebase.h> -#include <utils/Trace.h> - -namespace android { -namespace renderengine { -namespace gl { - -GLVertexBuffer::GLVertexBuffer() { - glGenBuffers(1, &mBufferName); -} - -GLVertexBuffer::~GLVertexBuffer() { - glDeleteBuffers(1, &mBufferName); -} - -void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) { - ATRACE_CALL(); - bind(); - glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW); - unbind(); -} - -void GLVertexBuffer::bind() const { - glBindBuffer(GL_ARRAY_BUFFER, mBufferName); -} - -void GLVertexBuffer::unbind() const { - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h deleted file mode 100644 index c0fd0c1b04..0000000000 --- a/libs/renderengine/gl/GLVertexBuffer.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <cstdint> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> - -struct ANativeWindowBuffer; - -namespace android { -namespace renderengine { -namespace gl { - -class GLESRenderEngine; - -class GLVertexBuffer { -public: - explicit GLVertexBuffer(); - ~GLVertexBuffer(); - - void allocateBuffers(const GLfloat data[], const GLuint size); - uint32_t getBufferName() const { return mBufferName; } - void bind() const; - void unbind() const; - -private: - uint32_t mBufferName; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp deleted file mode 100644 index 62566494f0..0000000000 --- a/libs/renderengine/gl/ImageManager.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -//#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "RenderEngine" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include <pthread.h> - -#include <processgroup/sched_policy.h> -#include <utils/Trace.h> -#include "GLESRenderEngine.h" -#include "ImageManager.h" - -namespace android { -namespace renderengine { -namespace gl { - -ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {} - -void ImageManager::initThread() { - mThread = std::thread([this]() { threadMain(); }); - pthread_setname_np(mThread.native_handle(), "ImageManager"); - // Use SCHED_FIFO to minimize jitter - struct sched_param param = {0}; - param.sched_priority = 2; - if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for ImageManager"); - } -} - -ImageManager::~ImageManager() { - { - std::lock_guard<std::mutex> lock(mMutex); - mRunning = false; - } - mCondition.notify_all(); - if (mThread.joinable()) { - mThread.join(); - } -} - -void ImageManager::cacheAsync(const sp<GraphicBuffer>& buffer, - const std::shared_ptr<Barrier>& barrier) { - if (buffer == nullptr) { - { - std::lock_guard<std::mutex> lock(barrier->mutex); - barrier->isOpen = true; - barrier->result = BAD_VALUE; - } - barrier->condition.notify_one(); - return; - } - ATRACE_CALL(); - QueueEntry entry = {QueueEntry::Operation::Insert, buffer, buffer->getId(), barrier}; - queueOperation(std::move(entry)); -} - -status_t ImageManager::cache(const sp<GraphicBuffer>& buffer) { - ATRACE_CALL(); - auto barrier = std::make_shared<Barrier>(); - cacheAsync(buffer, barrier); - std::lock_guard<std::mutex> lock(barrier->mutex); - barrier->condition.wait(barrier->mutex, - [&]() REQUIRES(barrier->mutex) { return barrier->isOpen; }); - return barrier->result; -} - -void ImageManager::releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) { - ATRACE_CALL(); - QueueEntry entry = {QueueEntry::Operation::Delete, nullptr, bufferId, barrier}; - queueOperation(std::move(entry)); -} - -void ImageManager::queueOperation(const QueueEntry&& entry) { - { - std::lock_guard<std::mutex> lock(mMutex); - mQueue.emplace(entry); - ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); - } - mCondition.notify_one(); -} - -void ImageManager::threadMain() { - set_sched_policy(0, SP_FOREGROUND); - bool run; - { - std::lock_guard<std::mutex> lock(mMutex); - run = mRunning; - } - while (run) { - QueueEntry entry; - { - std::lock_guard<std::mutex> lock(mMutex); - mCondition.wait(mMutex, - [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); - run = mRunning; - - if (!mRunning) { - // if mRunning is false, then ImageManager is being destroyed, so - // bail out now. - break; - } - - entry = mQueue.front(); - mQueue.pop(); - ATRACE_INT("ImageManagerQueueDepth", mQueue.size()); - } - - status_t result = NO_ERROR; - switch (entry.op) { - case QueueEntry::Operation::Delete: - mEngine->unbindExternalTextureBufferInternal(entry.bufferId); - break; - case QueueEntry::Operation::Insert: - result = mEngine->cacheExternalTextureBufferInternal(entry.buffer); - break; - } - if (entry.barrier != nullptr) { - { - std::lock_guard<std::mutex> entryLock(entry.barrier->mutex); - entry.barrier->result = result; - entry.barrier->isOpen = true; - } - entry.barrier->condition.notify_one(); - } - } - - ALOGD("Reached end of threadMain, terminating ImageManager thread!"); -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h deleted file mode 100644 index be67de8367..0000000000 --- a/libs/renderengine/gl/ImageManager.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <condition_variable> -#include <mutex> -#include <queue> -#include <thread> - -#include <ui/GraphicBuffer.h> - -namespace android { -namespace renderengine { -namespace gl { - -class GLESRenderEngine; - -class ImageManager { -public: - struct Barrier { - std::mutex mutex; - std::condition_variable_any condition; - bool isOpen GUARDED_BY(mutex) = false; - status_t result GUARDED_BY(mutex) = NO_ERROR; - }; - ImageManager(GLESRenderEngine* engine); - ~ImageManager(); - // Starts the background thread for the ImageManager - // We need this to guarantee that the class is fully-constructed before the - // thread begins running. - void initThread(); - void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier) - EXCLUDES(mMutex); - status_t cache(const sp<GraphicBuffer>& buffer); - void releaseAsync(uint64_t bufferId, const std::shared_ptr<Barrier>& barrier) EXCLUDES(mMutex); - -private: - struct QueueEntry { - enum class Operation { Delete, Insert }; - - Operation op = Operation::Delete; - sp<GraphicBuffer> buffer = nullptr; - uint64_t bufferId = 0; - std::shared_ptr<Barrier> barrier = nullptr; - }; - - void queueOperation(const QueueEntry&& entry); - void threadMain(); - GLESRenderEngine* const mEngine; - std::thread mThread; - std::condition_variable_any mCondition; - std::mutex mMutex; - std::queue<QueueEntry> mQueue GUARDED_BY(mMutex); - - bool mRunning GUARDED_BY(mMutex) = true; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp deleted file mode 100644 index 26f6166761..0000000000 --- a/libs/renderengine/gl/Program.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/*Gluint - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Program.h" - -#include <stdint.h> - -#include <log/log.h> -#include <math/mat4.h> -#include <utils/String8.h> -#include "ProgramCache.h" - -namespace android { -namespace renderengine { -namespace gl { - -Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment) - : mInitialized(false) { - GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER); - GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER); - GLuint programId = glCreateProgram(); - glAttachShader(programId, vertexId); - glAttachShader(programId, fragmentId); - glBindAttribLocation(programId, position, "position"); - glBindAttribLocation(programId, texCoords, "texCoords"); - glBindAttribLocation(programId, cropCoords, "cropCoords"); - glBindAttribLocation(programId, shadowColor, "shadowColor"); - glBindAttribLocation(programId, shadowParams, "shadowParams"); - glLinkProgram(programId); - - GLint status; - glGetProgramiv(programId, GL_LINK_STATUS, &status); - if (status != GL_TRUE) { - ALOGE("Error while linking shaders:"); - GLint infoLen = 0; - glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen); - if (infoLen > 1) { - GLchar log[infoLen]; - glGetProgramInfoLog(programId, infoLen, 0, &log[0]); - ALOGE("%s", log); - } - glDetachShader(programId, vertexId); - glDetachShader(programId, fragmentId); - glDeleteShader(vertexId); - glDeleteShader(fragmentId); - glDeleteProgram(programId); - } else { - mProgram = programId; - mVertexShader = vertexId; - mFragmentShader = fragmentId; - mInitialized = true; - mProjectionMatrixLoc = glGetUniformLocation(programId, "projection"); - mTextureMatrixLoc = glGetUniformLocation(programId, "texture"); - mSamplerLoc = glGetUniformLocation(programId, "sampler"); - mColorLoc = glGetUniformLocation(programId, "color"); - mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix"); - mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance"); - mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance"); - mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance"); - mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix"); - mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix"); - mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius"); - mCropCenterLoc = glGetUniformLocation(programId, "cropCenter"); - - // set-up the default values for our uniforms - glUseProgram(programId); - glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray()); - glEnableVertexAttribArray(0); - } -} - -Program::~Program() { - glDetachShader(mProgram, mVertexShader); - glDetachShader(mProgram, mFragmentShader); - glDeleteShader(mVertexShader); - glDeleteShader(mFragmentShader); - glDeleteProgram(mProgram); -} - -bool Program::isValid() const { - return mInitialized; -} - -void Program::use() { - glUseProgram(mProgram); -} - -GLuint Program::getAttrib(const char* name) const { - // TODO: maybe use a local cache - return glGetAttribLocation(mProgram, name); -} - -GLint Program::getUniform(const char* name) const { - // TODO: maybe use a local cache - return glGetUniformLocation(mProgram, name); -} - -GLuint Program::buildShader(const char* source, GLenum type) { - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &source, 0); - glCompileShader(shader); - GLint status; - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) { - // Some drivers return wrong values for GL_INFO_LOG_LENGTH - // use a fixed size instead - GLchar log[512]; - glGetShaderInfoLog(shader, sizeof(log), 0, log); - ALOGE("Error while compiling shader: \n%s\n%s", source, log); - glDeleteShader(shader); - return 0; - } - return shader; -} - -void Program::setUniforms(const Description& desc) { - // TODO: we should have a mechanism here to not always reset uniforms that - // didn't change for this program. - - if (mSamplerLoc >= 0) { - glUniform1i(mSamplerLoc, 0); - glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray()); - } - if (mColorLoc >= 0) { - const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a}; - glUniform4fv(mColorLoc, 1, color); - } - if (mDisplayColorMatrixLoc >= 0) { - glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray()); - } - if (mInputTransformMatrixLoc >= 0) { - mat4 inputTransformMatrix = desc.inputTransformMatrix; - glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray()); - } - if (mOutputTransformMatrixLoc >= 0) { - // The output transform matrix and color matrix can be combined as one matrix - // that is applied right before applying OETF. - mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix; - glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray()); - } - if (mDisplayMaxLuminanceLoc >= 0) { - glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance); - } - if (mMaxMasteringLuminanceLoc >= 0) { - glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance); - } - if (mMaxContentLuminanceLoc >= 0) { - glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance); - } - if (mCornerRadiusLoc >= 0) { - glUniform1f(mCornerRadiusLoc, desc.cornerRadius); - } - if (mCropCenterLoc >= 0) { - glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f); - } - // these uniforms are always present - glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray()); -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h deleted file mode 100644 index 41f1bf865e..0000000000 --- a/libs/renderengine/gl/Program.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDER_ENGINE_PROGRAM_H -#define SF_RENDER_ENGINE_PROGRAM_H - -#include <stdint.h> - -#include <GLES2/gl2.h> -#include <renderengine/private/Description.h> -#include "ProgramCache.h" - -namespace android { - -class String8; - -namespace renderengine { -namespace gl { - -/* - * Abstracts a GLSL program comprising a vertex and fragment shader - */ -class Program { -public: - // known locations for position and texture coordinates - enum { - /* position of each vertex for vertex shader */ - position = 0, - - /* UV coordinates for texture mapping */ - texCoords = 1, - - /* Crop coordinates, in pixels */ - cropCoords = 2, - - /* Shadow color */ - shadowColor = 3, - - /* Shadow params */ - shadowParams = 4, - }; - - Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); - ~Program(); - - /* whether this object is usable */ - bool isValid() const; - - /* Binds this program to the GLES context */ - void use(); - - /* Returns the location of the specified attribute */ - GLuint getAttrib(const char* name) const; - - /* Returns the location of the specified uniform */ - GLint getUniform(const char* name) const; - - /* set-up uniforms from the description */ - void setUniforms(const Description& desc); - -private: - GLuint buildShader(const char* source, GLenum type); - - // whether the initialization succeeded - bool mInitialized; - - // Name of the OpenGL program and shaders - GLuint mProgram; - GLuint mVertexShader; - GLuint mFragmentShader; - - /* location of the projection matrix uniform */ - GLint mProjectionMatrixLoc; - - /* location of the texture matrix uniform */ - GLint mTextureMatrixLoc; - - /* location of the sampler uniform */ - GLint mSamplerLoc; - - /* location of the color uniform */ - GLint mColorLoc; - - /* location of display luminance uniform */ - GLint mDisplayMaxLuminanceLoc; - /* location of max mastering luminance uniform */ - GLint mMaxMasteringLuminanceLoc; - /* location of max content luminance uniform */ - GLint mMaxContentLuminanceLoc; - - /* location of transform matrix */ - GLint mInputTransformMatrixLoc; - GLint mOutputTransformMatrixLoc; - GLint mDisplayColorMatrixLoc; - - /* location of corner radius uniform */ - GLint mCornerRadiusLoc; - - /* location of surface crop origin uniform, for rounded corner clipping */ - GLint mCropCenterLoc; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android - -#endif /* SF_RENDER_ENGINE_PROGRAM_H */ diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp deleted file mode 100644 index f7f2d54515..0000000000 --- a/libs/renderengine/gl/ProgramCache.cpp +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "ProgramCache.h" - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <log/log.h> -#include <renderengine/private/Description.h> -#include <utils/String8.h> -#include <utils/Trace.h> -#include "Program.h" - -ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache) - -namespace android { -namespace renderengine { -namespace gl { - -/* - * A simple formatter class to automatically add the endl and - * manage the indentation. - */ - -class Formatter; -static Formatter& indent(Formatter& f); -static Formatter& dedent(Formatter& f); - -class Formatter { - String8 mString; - int mIndent; - typedef Formatter& (*FormaterManipFunc)(Formatter&); - friend Formatter& indent(Formatter& f); - friend Formatter& dedent(Formatter& f); - -public: - Formatter() : mIndent(0) {} - - String8 getString() const { return mString; } - - friend Formatter& operator<<(Formatter& out, const char* in) { - for (int i = 0; i < out.mIndent; i++) { - out.mString.append(" "); - } - out.mString.append(in); - out.mString.append("\n"); - return out; - } - friend inline Formatter& operator<<(Formatter& out, const String8& in) { - return operator<<(out, in.string()); - } - friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { - return (*func)(to); - } -}; -Formatter& indent(Formatter& f) { - f.mIndent++; - return f; -} -Formatter& dedent(Formatter& f) { - f.mIndent--; - return f; -} - -void ProgramCache::primeCache( - EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) { - auto& cache = mCaches[context]; - uint32_t shaderCount = 0; - - if (toneMapperShaderOnly) { - Key shaderKey; - // base settings used by HDR->SDR tonemap only - shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK | - Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK | - Key::OPACITY_MASK | Key::ALPHA_MASK | - Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK, - Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON | - Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB | - Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE | - Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT); - for (int i = 0; i < 4; i++) { - // Cache input transfer for HLG & ST2084 - shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ? - Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084); - - // Cache Y410 input on or off - shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ? - Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); - if (cache.count(shaderKey) == 0) { - cache.emplace(shaderKey, generateProgram(shaderKey)); - shaderCount++; - } - } - return; - } - - uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK - | Key::ROUNDED_CORNERS_MASK; - // Prime the cache for all combinations of the above masks, - // leaving off the experimental color matrix mask options. - - nsecs_t timeBefore = systemTime(); - for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) { - Key shaderKey; - shaderKey.set(keyMask, keyVal); - uint32_t tex = shaderKey.getTextureTarget(); - if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { - continue; - } - if (cache.count(shaderKey) == 0) { - cache.emplace(shaderKey, generateProgram(shaderKey)); - shaderCount++; - } - } - - // Prime for sRGB->P3 conversion - if (useColorManagement) { - Key shaderKey; - shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK | - Key::OUTPUT_TF_MASK, - Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB | - Key::OUTPUT_TF_SRGB); - for (int i = 0; i < 16; i++) { - shaderKey.set(Key::OPACITY_MASK, - (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); - shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); - - // Cache rounded corners - shaderKey.set(Key::ROUNDED_CORNERS_MASK, - (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); - - // Cache texture off option for window transition - shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF); - if (cache.count(shaderKey) == 0) { - cache.emplace(shaderKey, generateProgram(shaderKey)); - shaderCount++; - } - } - } - - nsecs_t timeAfter = systemTime(); - float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; - ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs); -} - -ProgramCache::Key ProgramCache::computeKey(const Description& description) { - Key needs; - needs.set(Key::TEXTURE_MASK, - !description.textureEnabled - ? Key::TEXTURE_OFF - : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES - ? Key::TEXTURE_EXT - : description.texture.getTextureTarget() == GL_TEXTURE_2D - ? Key::TEXTURE_2D - : Key::TEXTURE_OFF) - .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) - .set(Key::BLEND_MASK, - description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) - .set(Key::OPACITY_MASK, - description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) - .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, - description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON - : Key::INPUT_TRANSFORM_MATRIX_OFF) - .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, - description.hasOutputTransformMatrix() || description.hasColorMatrix() - ? Key::OUTPUT_TRANSFORM_MATRIX_ON - : Key::OUTPUT_TRANSFORM_MATRIX_OFF) - .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK, - description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON - : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF) - .set(Key::ROUNDED_CORNERS_MASK, - description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF) - .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF); - needs.set(Key::Y410_BT2020_MASK, - description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); - - if (needs.hasTransformMatrix() || - (description.inputTransferFunction != description.outputTransferFunction)) { - switch (description.inputTransferFunction) { - case Description::TransferFunction::LINEAR: - default: - needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); - break; - case Description::TransferFunction::SRGB: - needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB); - break; - case Description::TransferFunction::ST2084: - needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084); - break; - case Description::TransferFunction::HLG: - needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG); - break; - } - - switch (description.outputTransferFunction) { - case Description::TransferFunction::LINEAR: - default: - needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); - break; - case Description::TransferFunction::SRGB: - needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB); - break; - case Description::TransferFunction::ST2084: - needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084); - break; - case Description::TransferFunction::HLG: - needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG); - break; - } - } - - return needs; -} - -// Generate EOTF that converts signal values to relative display light, -// both normalized to [0, 1]. -void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) { - switch (needs.getInputTF()) { - case Key::INPUT_TF_SRGB: - fs << R"__SHADER__( - float EOTF_sRGB(float srgb) { - return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); - } - - vec3 EOTF_sRGB(const vec3 srgb) { - return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); - } - - vec3 EOTF(const vec3 srgb) { - return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); - } - )__SHADER__"; - break; - case Key::INPUT_TF_ST2084: - fs << R"__SHADER__( - vec3 EOTF(const highp vec3 color) { - const highp float m1 = (2610.0 / 4096.0) / 4.0; - const highp float m2 = (2523.0 / 4096.0) * 128.0; - const highp float c1 = (3424.0 / 4096.0); - const highp float c2 = (2413.0 / 4096.0) * 32.0; - const highp float c3 = (2392.0 / 4096.0) * 32.0; - - highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2)); - tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); - return pow(tmp, 1.0 / vec3(m1)); - } - )__SHADER__"; - break; - case Key::INPUT_TF_HLG: - fs << R"__SHADER__( - highp float EOTF_channel(const highp float channel) { - const highp float a = 0.17883277; - const highp float b = 0.28466892; - const highp float c = 0.55991073; - return channel <= 0.5 ? channel * channel / 3.0 : - (exp((channel - c) / a) + b) / 12.0; - } - - vec3 EOTF(const highp vec3 color) { - return vec3(EOTF_channel(color.r), EOTF_channel(color.g), - EOTF_channel(color.b)); - } - )__SHADER__"; - break; - default: - fs << R"__SHADER__( - vec3 EOTF(const vec3 linear) { - return linear; - } - )__SHADER__"; - break; - } -} - -void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { - // Convert relative light to absolute light. - switch (needs.getInputTF()) { - case Key::INPUT_TF_ST2084: - fs << R"__SHADER__( - highp vec3 ScaleLuminance(highp vec3 color) { - return color * 10000.0; - } - )__SHADER__"; - break; - case Key::INPUT_TF_HLG: - fs << R"__SHADER__( - highp vec3 ScaleLuminance(highp vec3 color) { - // The formula is: - // alpha * pow(Y, gamma - 1.0) * color + beta; - // where alpha is 1000.0, gamma is 1.2, beta is 0.0. - return color * 1000.0 * pow(color.y, 0.2); - } - )__SHADER__"; - break; - default: - fs << R"__SHADER__( - highp vec3 ScaleLuminance(highp vec3 color) { - return color * displayMaxLuminance; - } - )__SHADER__"; - break; - } - - // Tone map absolute light to display luminance range. - switch (needs.getInputTF()) { - case Key::INPUT_TF_ST2084: - case Key::INPUT_TF_HLG: - switch (needs.getOutputTF()) { - case Key::OUTPUT_TF_HLG: - // Right now when mixed PQ and HLG contents are presented, - // HLG content will always be converted to PQ. However, for - // completeness, we simply clamp the value to [0.0, 1000.0]. - fs << R"__SHADER__( - highp vec3 ToneMap(highp vec3 color) { - return clamp(color, 0.0, 1000.0); - } - )__SHADER__"; - break; - case Key::OUTPUT_TF_ST2084: - fs << R"__SHADER__( - highp vec3 ToneMap(highp vec3 color) { - return color; - } - )__SHADER__"; - break; - default: - fs << R"__SHADER__( - highp vec3 ToneMap(highp vec3 color) { - float maxMasteringLumi = maxMasteringLuminance; - float maxContentLumi = maxContentLuminance; - float maxInLumi = min(maxMasteringLumi, maxContentLumi); - float maxOutLumi = displayMaxLuminance; - - float nits = color.y; - - // clamp to max input luminance - nits = clamp(nits, 0.0, maxInLumi); - - // scale [0.0, maxInLumi] to [0.0, maxOutLumi] - if (maxInLumi <= maxOutLumi) { - return color * (maxOutLumi / maxInLumi); - } else { - // three control points - const float x0 = 10.0; - const float y0 = 17.0; - float x1 = maxOutLumi * 0.75; - float y1 = x1; - float x2 = x1 + (maxInLumi - x1) / 2.0; - float y2 = y1 + (maxOutLumi - y1) * 0.75; - - // horizontal distances between the last three control points - float h12 = x2 - x1; - float h23 = maxInLumi - x2; - // tangents at the last three control points - float m1 = (y2 - y1) / h12; - float m3 = (maxOutLumi - y2) / h23; - float m2 = (m1 + m3) / 2.0; - - if (nits < x0) { - // scale [0.0, x0] to [0.0, y0] linearly - float slope = y0 / x0; - return color * slope; - } else if (nits < x1) { - // scale [x0, x1] to [y0, y1] linearly - float slope = (y1 - y0) / (x1 - x0); - nits = y0 + (nits - x0) * slope; - } else if (nits < x2) { - // scale [x1, x2] to [y1, y2] using Hermite interp - float t = (nits - x1) / h12; - nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + - (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; - } else { - // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp - float t = (nits - x2) / h23; - nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + - (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; - } - } - - // color.y is greater than x0 and is thus non-zero - return color * (nits / color.y); - } - )__SHADER__"; - break; - } - break; - default: - // inverse tone map; the output luminance can be up to maxOutLumi. - fs << R"__SHADER__( - highp vec3 ToneMap(highp vec3 color) { - const float maxOutLumi = 3000.0; - - const float x0 = 5.0; - const float y0 = 2.5; - float x1 = displayMaxLuminance * 0.7; - float y1 = maxOutLumi * 0.15; - float x2 = displayMaxLuminance * 0.9; - float y2 = maxOutLumi * 0.45; - float x3 = displayMaxLuminance; - float y3 = maxOutLumi; - - float c1 = y1 / 3.0; - float c2 = y2 / 2.0; - float c3 = y3 / 1.5; - - float nits = color.y; - - float scale; - if (nits <= x0) { - // scale [0.0, x0] to [0.0, y0] linearly - const float slope = y0 / x0; - return color * slope; - } else if (nits <= x1) { - // scale [x0, x1] to [y0, y1] using a curve - float t = (nits - x0) / (x1 - x0); - nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; - } else if (nits <= x2) { - // scale [x1, x2] to [y1, y2] using a curve - float t = (nits - x1) / (x2 - x1); - nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; - } else { - // scale [x2, x3] to [y2, y3] using a curve - float t = (nits - x2) / (x3 - x2); - nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; - } - - // color.y is greater than x0 and is thus non-zero - return color * (nits / color.y); - } - )__SHADER__"; - break; - } - - // convert absolute light to relative light. - switch (needs.getOutputTF()) { - case Key::OUTPUT_TF_ST2084: - fs << R"__SHADER__( - highp vec3 NormalizeLuminance(highp vec3 color) { - return color / 10000.0; - } - )__SHADER__"; - break; - case Key::OUTPUT_TF_HLG: - fs << R"__SHADER__( - highp vec3 NormalizeLuminance(highp vec3 color) { - return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2); - } - )__SHADER__"; - break; - default: - fs << R"__SHADER__( - highp vec3 NormalizeLuminance(highp vec3 color) { - return color / displayMaxLuminance; - } - )__SHADER__"; - break; - } -} - -// Generate OOTF that modifies the relative scence light to relative display light. -void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) { - if (!needs.needsToneMapping()) { - fs << R"__SHADER__( - highp vec3 OOTF(const highp vec3 color) { - return color; - } - )__SHADER__"; - } else { - generateToneMappingProcess(fs, needs); - fs << R"__SHADER__( - highp vec3 OOTF(const highp vec3 color) { - return NormalizeLuminance(ToneMap(ScaleLuminance(color))); - } - )__SHADER__"; - } -} - -// Generate OETF that converts relative display light to signal values, -// both normalized to [0, 1] -void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { - switch (needs.getOutputTF()) { - case Key::OUTPUT_TF_SRGB: - fs << R"__SHADER__( - float OETF_sRGB(const float linear) { - return linear <= 0.0031308 ? - linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; - } - - vec3 OETF_sRGB(const vec3 linear) { - return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); - } - - vec3 OETF(const vec3 linear) { - return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); - } - )__SHADER__"; - break; - case Key::OUTPUT_TF_ST2084: - fs << R"__SHADER__( - vec3 OETF(const vec3 linear) { - const highp float m1 = (2610.0 / 4096.0) / 4.0; - const highp float m2 = (2523.0 / 4096.0) * 128.0; - const highp float c1 = (3424.0 / 4096.0); - const highp float c2 = (2413.0 / 4096.0) * 32.0; - const highp float c3 = (2392.0 / 4096.0) * 32.0; - - highp vec3 tmp = pow(linear, vec3(m1)); - tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); - return pow(tmp, vec3(m2)); - } - )__SHADER__"; - break; - case Key::OUTPUT_TF_HLG: - fs << R"__SHADER__( - highp float OETF_channel(const highp float channel) { - const highp float a = 0.17883277; - const highp float b = 0.28466892; - const highp float c = 0.55991073; - return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : - a * log(12.0 * channel - b) + c; - } - - vec3 OETF(const highp vec3 color) { - return vec3(OETF_channel(color.r), OETF_channel(color.g), - OETF_channel(color.b)); - } - )__SHADER__"; - break; - default: - fs << R"__SHADER__( - vec3 OETF(const vec3 linear) { - return linear; - } - )__SHADER__"; - break; - } -} - -String8 ProgramCache::generateVertexShader(const Key& needs) { - Formatter vs; - if (needs.hasTextureCoords()) { - vs << "attribute vec4 texCoords;" - << "varying vec2 outTexCoords;"; - } - if (needs.hasRoundedCorners()) { - vs << "attribute lowp vec4 cropCoords;"; - vs << "varying lowp vec2 outCropCoords;"; - } - if (needs.drawShadows()) { - vs << "attribute lowp vec4 shadowColor;"; - vs << "varying lowp vec4 outShadowColor;"; - vs << "attribute lowp vec4 shadowParams;"; - vs << "varying lowp vec3 outShadowParams;"; - } - vs << "attribute vec4 position;" - << "uniform mat4 projection;" - << "uniform mat4 texture;" - << "void main(void) {" << indent << "gl_Position = projection * position;"; - if (needs.hasTextureCoords()) { - vs << "outTexCoords = (texture * texCoords).st;"; - } - if (needs.hasRoundedCorners()) { - vs << "outCropCoords = cropCoords.st;"; - } - if (needs.drawShadows()) { - vs << "outShadowColor = shadowColor;"; - vs << "outShadowParams = shadowParams.xyz;"; - } - vs << dedent << "}"; - return vs.getString(); -} - -String8 ProgramCache::generateFragmentShader(const Key& needs) { - Formatter fs; - if (needs.getTextureTarget() == Key::TEXTURE_EXT) { - fs << "#extension GL_OES_EGL_image_external : require"; - } - - // default precision is required-ish in fragment shaders - fs << "precision mediump float;"; - - if (needs.getTextureTarget() == Key::TEXTURE_EXT) { - fs << "uniform samplerExternalOES sampler;"; - } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { - fs << "uniform sampler2D sampler;"; - } - - if (needs.hasTextureCoords()) { - fs << "varying highp vec2 outTexCoords;"; - } - - if (needs.hasRoundedCorners()) { - // Rounded corners implementation using a signed distance function. - fs << R"__SHADER__( - uniform float cornerRadius; - uniform vec2 cropCenter; - varying vec2 outCropCoords; - - /** - * This function takes the current crop coordinates and calculates an alpha value based - * on the corner radius and distance from the crop center. - */ - float applyCornerRadius(vec2 cropCoords) - { - vec2 position = cropCoords - cropCenter; - // Scale down the dist vector here, as otherwise large corner - // radii can cause floating point issues when computing the norm - vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0; - // Once we've found the norm, then scale back up. - float plane = length(max(dist, vec2(0.0))) * 16.0; - return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0); - } - )__SHADER__"; - } - - if (needs.drawShadows()) { - fs << R"__SHADER__( - varying lowp vec4 outShadowColor; - varying lowp vec3 outShadowParams; - - /** - * Returns the shadow color. - */ - vec4 getShadowColor() - { - lowp float d = length(outShadowParams.xy); - vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5); - lowp float factor = texture2D(sampler, uv).a; - return outShadowColor * factor; - } - )__SHADER__"; - } - - if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) { - fs << "uniform vec4 color;"; - } - - if (needs.isY410BT2020()) { - fs << R"__SHADER__( - vec3 convertY410BT2020(const vec3 color) { - const vec3 offset = vec3(0.0625, 0.5, 0.5); - const mat3 transform = mat3( - vec3(1.1678, 1.1678, 1.1678), - vec3( 0.0, -0.1878, 2.1481), - vec3(1.6836, -0.6523, 0.0)); - // Y is in G, U is in R, and V is in B - return clamp(transform * (color.grb - offset), 0.0, 1.0); - } - )__SHADER__"; - } - - if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) || - needs.hasDisplayColorMatrix()) { - if (needs.needsToneMapping()) { - fs << "uniform float displayMaxLuminance;"; - fs << "uniform float maxMasteringLuminance;"; - fs << "uniform float maxContentLuminance;"; - } - - if (needs.hasInputTransformMatrix()) { - fs << "uniform mat4 inputTransformMatrix;"; - fs << R"__SHADER__( - highp vec3 InputTransform(const highp vec3 color) { - return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); - } - )__SHADER__"; - } else { - fs << R"__SHADER__( - highp vec3 InputTransform(const highp vec3 color) { - return color; - } - )__SHADER__"; - } - - // the transformation from a wider colorspace to a narrower one can - // result in >1.0 or <0.0 pixel values - if (needs.hasOutputTransformMatrix()) { - fs << "uniform mat4 outputTransformMatrix;"; - fs << R"__SHADER__( - highp vec3 OutputTransform(const highp vec3 color) { - return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); - } - )__SHADER__"; - } else { - fs << R"__SHADER__( - highp vec3 OutputTransform(const highp vec3 color) { - return clamp(color, 0.0, 1.0); - } - )__SHADER__"; - } - - if (needs.hasDisplayColorMatrix()) { - fs << "uniform mat4 displayColorMatrix;"; - fs << R"__SHADER__( - highp vec3 DisplayColorMatrix(const highp vec3 color) { - return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0); - } - )__SHADER__"; - } else { - fs << R"__SHADER__( - highp vec3 DisplayColorMatrix(const highp vec3 color) { - return color; - } - )__SHADER__"; - } - - generateEOTF(fs, needs); - generateOOTF(fs, needs); - generateOETF(fs, needs); - } - - fs << "void main(void) {" << indent; - if (needs.drawShadows()) { - fs << "gl_FragColor = getShadowColor();"; - } else { - if (needs.isTexturing()) { - fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; - if (needs.isY410BT2020()) { - fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);"; - } - } else { - fs << "gl_FragColor.rgb = color.rgb;"; - fs << "gl_FragColor.a = 1.0;"; - } - if (needs.isOpaque()) { - fs << "gl_FragColor.a = 1.0;"; - } - } - - if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) || - needs.hasDisplayColorMatrix()) { - if (!needs.isOpaque() && needs.isPremultiplied()) { - // un-premultiply if needed before linearization - // avoid divide by 0 by adding 0.5/256 to the alpha channel - fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; - } - fs << "gl_FragColor.rgb = " - "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))" - ")));"; - - if (!needs.isOpaque() && needs.isPremultiplied()) { - // and re-premultiply if needed after gamma correction - fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);"; - } - } - - /* - * Whether applying layer alpha before or after color transform doesn't matter, - * as long as we can undo premultiplication. But we cannot un-premultiply - * for color transform if the layer alpha = 0, e.g. 0 / (0 + 0.0019) = 0. - */ - if (!needs.drawShadows()) { - if (needs.hasAlpha()) { - // modulate the current alpha value with alpha set - if (needs.isPremultiplied()) { - // ... and the color too if we're premultiplied - fs << "gl_FragColor *= color.a;"; - } else { - fs << "gl_FragColor.a *= color.a;"; - } - } - } - - if (needs.hasRoundedCorners()) { - if (needs.isPremultiplied()) { - fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));"; - } else { - fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);"; - } - } - - fs << dedent << "}"; - return fs.getString(); -} - -std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { - ATRACE_CALL(); - - // vertex shader - String8 vs = generateVertexShader(needs); - - // fragment shader - String8 fs = generateFragmentShader(needs); - - return std::make_unique<Program>(needs, vs.string(), fs.string()); -} - -void ProgramCache::useProgram(EGLContext context, const Description& description) { - // generate the key for the shader based on the description - Key needs(computeKey(description)); - - // look-up the program in the cache - auto& cache = mCaches[context]; - auto it = cache.find(needs); - if (it == cache.end()) { - // we didn't find our program, so generate one... - nsecs_t time = systemTime(); - it = cache.emplace(needs, generateProgram(needs)).first; - time = systemTime() - time; - - ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)", - context, needs.mKey, uint32_t(ns2ms(time)), cache.size()); - } - - // here we have a suitable program for this description - std::unique_ptr<Program>& program = it->second; - if (program->isValid()) { - program->use(); - program->setUniforms(description); - } -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h deleted file mode 100644 index 535d21cd52..0000000000 --- a/libs/renderengine/gl/ProgramCache.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H -#define SF_RENDER_ENGINE_PROGRAMCACHE_H - -#include <memory> -#include <unordered_map> - -#include <EGL/egl.h> -#include <GLES2/gl2.h> -#include <renderengine/private/Description.h> -#include <utils/Singleton.h> -#include <utils/TypeHelpers.h> - -namespace android { - -class String8; - -namespace renderengine { - -struct Description; - -namespace gl { - -class Formatter; -class Program; - -/* - * This class generates GLSL programs suitable to handle a given - * Description. It's responsible for figuring out what to - * generate from a Description. - * It also maintains a cache of these Programs. - */ -class ProgramCache : public Singleton<ProgramCache> { -public: - /* - * Key is used to retrieve a Program in the cache. - * A Key is generated from a Description. - */ - class Key { - friend class ProgramCache; - typedef uint32_t key_t; - key_t mKey; - - public: - enum { - BLEND_SHIFT = 0, - BLEND_MASK = 1 << BLEND_SHIFT, - BLEND_PREMULT = 1 << BLEND_SHIFT, - BLEND_NORMAL = 0 << BLEND_SHIFT, - - OPACITY_SHIFT = 1, - OPACITY_MASK = 1 << OPACITY_SHIFT, - OPACITY_OPAQUE = 1 << OPACITY_SHIFT, - OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT, - - ALPHA_SHIFT = 2, - ALPHA_MASK = 1 << ALPHA_SHIFT, - ALPHA_LT_ONE = 1 << ALPHA_SHIFT, - ALPHA_EQ_ONE = 0 << ALPHA_SHIFT, - - TEXTURE_SHIFT = 3, - TEXTURE_MASK = 3 << TEXTURE_SHIFT, - TEXTURE_OFF = 0 << TEXTURE_SHIFT, - TEXTURE_EXT = 1 << TEXTURE_SHIFT, - TEXTURE_2D = 2 << TEXTURE_SHIFT, - - ROUNDED_CORNERS_SHIFT = 5, - ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT, - ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT, - ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT, - - INPUT_TRANSFORM_MATRIX_SHIFT = 6, - INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, - INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT, - INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, - - OUTPUT_TRANSFORM_MATRIX_SHIFT = 7, - OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, - OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT, - OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, - - INPUT_TF_SHIFT = 8, - INPUT_TF_MASK = 3 << INPUT_TF_SHIFT, - INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT, - INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT, - INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT, - INPUT_TF_HLG = 3 << INPUT_TF_SHIFT, - - OUTPUT_TF_SHIFT = 10, - OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT, - OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT, - OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT, - OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT, - OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT, - - Y410_BT2020_SHIFT = 12, - Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT, - Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT, - Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT, - - SHADOW_SHIFT = 13, - SHADOW_MASK = 1 << SHADOW_SHIFT, - SHADOW_OFF = 0 << SHADOW_SHIFT, - SHADOW_ON = 1 << SHADOW_SHIFT, - - DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14, - DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT, - DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT, - DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT, - }; - - inline Key() : mKey(0) {} - inline Key(const Key& rhs) : mKey(rhs.mKey) {} - - inline Key& set(key_t mask, key_t value) { - mKey = (mKey & ~mask) | value; - return *this; - } - - inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; } - inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); } - inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); } - inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; } - inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; } - inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; } - inline bool hasRoundedCorners() const { - return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON; - } - inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; } - inline bool hasInputTransformMatrix() const { - return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON; - } - inline bool hasOutputTransformMatrix() const { - return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON; - } - inline bool hasDisplayColorMatrix() const { - return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) == - DISPLAY_COLOR_TRANSFORM_MATRIX_ON; - } - inline bool hasTransformMatrix() const { - return hasInputTransformMatrix() || hasOutputTransformMatrix(); - } - inline int getInputTF() const { return (mKey & INPUT_TF_MASK); } - inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); } - - // When HDR and non-HDR contents are mixed, or different types of HDR contents are - // mixed, we will do a tone mapping process to tone map the input content to output - // content. Currently, the following conversions handled, they are: - // * SDR -> HLG - // * SDR -> PQ - // * HLG -> PQ - inline bool needsToneMapping() const { - int inputTF = getInputTF(); - int outputTF = getOutputTF(); - - // Return false when converting from SDR to SDR. - if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) { - return false; - } - if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) { - return false; - } - - inputTF >>= Key::INPUT_TF_SHIFT; - outputTF >>= Key::OUTPUT_TF_SHIFT; - return inputTF != outputTF; - } - inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; } - - // for use by std::unordered_map - - bool operator==(const Key& other) const { return mKey == other.mKey; } - - struct Hash { - size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); } - }; - }; - - ProgramCache() = default; - ~ProgramCache() = default; - - // Generate shaders to populate the cache - void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly); - - size_t getSize(const EGLContext context) { return mCaches[context].size(); } - - // useProgram lookup a suitable program in the cache or generates one - // if none can be found. - void useProgram(const EGLContext context, const Description& description); - - void purgeCaches() { mCaches.clear(); } - -private: - // compute a cache Key from a Description - static Key computeKey(const Description& description); - // Generate EOTF based from Key. - static void generateEOTF(Formatter& fs, const Key& needs); - // Generate necessary tone mapping methods for OOTF. - static void generateToneMappingProcess(Formatter& fs, const Key& needs); - // Generate OOTF based from Key. - static void generateOOTF(Formatter& fs, const Key& needs); - // Generate OETF based from Key. - static void generateOETF(Formatter& fs, const Key& needs); - // generates a program from the Key - static std::unique_ptr<Program> generateProgram(const Key& needs); - // generates the vertex shader from the Key - static String8 generateVertexShader(const Key& needs); - // generates the fragment shader from the Key - static String8 generateFragmentShader(const Key& needs); - - // Key/Value map used for caching Programs. Currently the cache - // is never shrunk (and the GL program objects are never deleted). - std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>> - mCaches; -}; - -} // namespace gl -} // namespace renderengine - -ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) - -} // namespace android - -#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */ diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp deleted file mode 100644 index 3455e08cfe..0000000000 --- a/libs/renderengine/gl/filters/BlurFilter.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "BlurFilter.h" -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> -#include <ui/GraphicTypes.h> -#include <cstdint> - -#include <utils/Trace.h> - -namespace android { -namespace renderengine { -namespace gl { - -BlurFilter::BlurFilter(GLESRenderEngine& engine) - : mEngine(engine), - mCompositionFbo(engine), - mPingFbo(engine), - mPongFbo(engine), - mMixProgram(engine), - mBlurProgram(engine) { - mMixProgram.compile(getVertexShader(), getMixFragShader()); - mMPosLoc = mMixProgram.getAttributeLocation("aPosition"); - mMUvLoc = mMixProgram.getAttributeLocation("aUV"); - mMTextureLoc = mMixProgram.getUniformLocation("uTexture"); - mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture"); - mMMixLoc = mMixProgram.getUniformLocation("uMix"); - - mBlurProgram.compile(getVertexShader(), getFragmentShader()); - mBPosLoc = mBlurProgram.getAttributeLocation("aPosition"); - mBUvLoc = mBlurProgram.getAttributeLocation("aUV"); - mBTextureLoc = mBlurProgram.getUniformLocation("uTexture"); - mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset"); - - static constexpr auto size = 2.0f; - static constexpr auto translation = 1.0f; - const GLfloat vboData[] = { - // Vertex data - translation - size, -translation - size, - translation - size, -translation + size, - translation + size, -translation + size, - // UV data - 0.0f, 0.0f - translation, - 0.0f, size - translation, - size, size - translation - }; - mMeshBuffer.allocateBuffers(vboData, 12 /* size */); -} - -status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) { - ATRACE_NAME("BlurFilter::setAsDrawTarget"); - mRadius = radius; - mDisplayX = display.physicalDisplay.left; - mDisplayY = display.physicalDisplay.top; - - if (mDisplayWidth < display.physicalDisplay.width() || - mDisplayHeight < display.physicalDisplay.height()) { - ATRACE_NAME("BlurFilter::allocatingTextures"); - - mDisplayWidth = display.physicalDisplay.width(); - mDisplayHeight = display.physicalDisplay.height(); - mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight); - - const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale); - const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale); - mPingFbo.allocateBuffers(fboWidth, fboHeight); - mPongFbo.allocateBuffers(fboWidth, fboHeight); - - if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { - ALOGE("Invalid ping buffer"); - return mPingFbo.getStatus(); - } - if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { - ALOGE("Invalid pong buffer"); - return mPongFbo.getStatus(); - } - if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) { - ALOGE("Invalid composition buffer"); - return mCompositionFbo.getStatus(); - } - if (!mBlurProgram.isValid()) { - ALOGE("Invalid shader"); - return GL_INVALID_OPERATION; - } - } - - mCompositionFbo.bind(); - glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight()); - return NO_ERROR; -} - -void BlurFilter::drawMesh(GLuint uv, GLuint position) { - - glEnableVertexAttribArray(uv); - glEnableVertexAttribArray(position); - mMeshBuffer.bind(); - glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, - 2 * sizeof(GLfloat) /* stride */, 0 /* offset */); - glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */, - (GLvoid*)(6 * sizeof(GLfloat)) /* offset */); - mMeshBuffer.unbind(); - - // draw mesh - glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */); -} - -status_t BlurFilter::prepare() { - ATRACE_NAME("BlurFilter::prepare"); - - // Kawase is an approximation of Gaussian, but it behaves differently from it. - // A radius transformation is required for approximating them, and also to introduce - // non-integer steps, necessary to smoothly interpolate large radii. - const auto radius = mRadius / 6.0f; - - // Calculate how many passes we'll do, based on the radius. - // Too many passes will make the operation expensive. - const auto passes = min(kMaxPasses, (uint32_t)ceil(radius)); - - const float radiusByPasses = radius / (float)passes; - const float stepX = radiusByPasses / (float)mCompositionFbo.getBufferWidth(); - const float stepY = radiusByPasses / (float)mCompositionFbo.getBufferHeight(); - - // Let's start by downsampling and blurring the composited frame simultaneously. - mBlurProgram.useProgram(); - glActiveTexture(GL_TEXTURE0); - glUniform1i(mBTextureLoc, 0); - glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName()); - glUniform2f(mBOffsetLoc, stepX, stepY); - glViewport(0, 0, mPingFbo.getBufferWidth(), mPingFbo.getBufferHeight()); - mPingFbo.bind(); - drawMesh(mBUvLoc, mBPosLoc); - - // And now we'll ping pong between our textures, to accumulate the result of various offsets. - GLFramebuffer* read = &mPingFbo; - GLFramebuffer* draw = &mPongFbo; - glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight()); - for (auto i = 1; i < passes; i++) { - ATRACE_NAME("BlurFilter::renderPass"); - draw->bind(); - - glBindTexture(GL_TEXTURE_2D, read->getTextureName()); - glUniform2f(mBOffsetLoc, stepX * i, stepY * i); - - drawMesh(mBUvLoc, mBPosLoc); - - // Swap buffers for next iteration - auto tmp = draw; - draw = read; - read = tmp; - } - mLastDrawTarget = read; - - return NO_ERROR; -} - -status_t BlurFilter::render(bool multiPass) { - ATRACE_NAME("BlurFilter::render"); - - // Now let's scale our blur up. It will be interpolated with the larger composited - // texture for the first frames, to hide downscaling artifacts. - GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius); - - // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll - // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer, - // as large as the screen size. - if (mix >= 1 || multiPass) { - mLastDrawTarget->bindAsReadBuffer(); - glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(), - mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth, - mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); - return NO_ERROR; - } - - mMixProgram.useProgram(); - glUniform1f(mMMixLoc, mix); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName()); - glUniform1i(mMTextureLoc, 0); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName()); - glUniform1i(mMCompositionTextureLoc, 1); - - drawMesh(mMUvLoc, mMPosLoc); - - glUseProgram(0); - glActiveTexture(GL_TEXTURE0); - mEngine.checkErrors("Drawing blur mesh"); - return NO_ERROR; -} - -string BlurFilter::getVertexShader() const { - return R"SHADER(#version 300 es - precision mediump float; - - in vec2 aPosition; - in highp vec2 aUV; - out highp vec2 vUV; - - void main() { - vUV = aUV; - gl_Position = vec4(aPosition, 0.0, 1.0); - } - )SHADER"; -} - -string BlurFilter::getFragmentShader() const { - return R"SHADER(#version 300 es - precision mediump float; - - uniform sampler2D uTexture; - uniform vec2 uOffset; - - in highp vec2 vUV; - out vec4 fragColor; - - void main() { - fragColor = texture(uTexture, vUV, 0.0); - fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0); - fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0); - fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0); - fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0); - - fragColor = vec4(fragColor.rgb * 0.2, 1.0); - } - )SHADER"; -} - -string BlurFilter::getMixFragShader() const { - string shader = R"SHADER(#version 300 es - precision mediump float; - - in highp vec2 vUV; - out vec4 fragColor; - - uniform sampler2D uCompositionTexture; - uniform sampler2D uTexture; - uniform float uMix; - - void main() { - vec4 blurred = texture(uTexture, vUV); - vec4 composition = texture(uCompositionTexture, vUV); - fragColor = mix(composition, blurred, uMix); - } - )SHADER"; - return shader; -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h deleted file mode 100644 index 593a8fd54e..0000000000 --- a/libs/renderengine/gl/filters/BlurFilter.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <ui/GraphicTypes.h> -#include "../GLESRenderEngine.h" -#include "../GLFramebuffer.h" -#include "../GLVertexBuffer.h" -#include "GenericProgram.h" - -using namespace std; - -namespace android { -namespace renderengine { -namespace gl { - -/** - * This is an implementation of a Kawase blur, as described in here: - * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/ - * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf - */ -class BlurFilter { -public: - // Downsample FBO to improve performance - static constexpr float kFboScale = 0.25f; - // Maximum number of render passes - static constexpr uint32_t kMaxPasses = 4; - // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited - // image, up to this radius. - static constexpr float kMaxCrossFadeRadius = 30.0f; - - explicit BlurFilter(GLESRenderEngine& engine); - virtual ~BlurFilter(){}; - - // Set up render targets, redirecting output to offscreen texture. - status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius); - // Execute blur passes, rendering to offscreen texture. - status_t prepare(); - // Render blur to the bound framebuffer (screen). - status_t render(bool multiPass); - -private: - uint32_t mRadius; - void drawMesh(GLuint uv, GLuint position); - string getVertexShader() const; - string getFragmentShader() const; - string getMixFragShader() const; - - GLESRenderEngine& mEngine; - // Frame buffer holding the composited background. - GLFramebuffer mCompositionFbo; - // Frame buffers holding the blur passes. - GLFramebuffer mPingFbo; - GLFramebuffer mPongFbo; - uint32_t mDisplayWidth = 0; - uint32_t mDisplayHeight = 0; - uint32_t mDisplayX = 0; - uint32_t mDisplayY = 0; - // Buffer holding the final blur pass. - GLFramebuffer* mLastDrawTarget; - - // VBO containing vertex and uv data of a fullscreen triangle. - GLVertexBuffer mMeshBuffer; - - GenericProgram mMixProgram; - GLuint mMPosLoc; - GLuint mMUvLoc; - GLuint mMMixLoc; - GLuint mMTextureLoc; - GLuint mMCompositionTextureLoc; - - GenericProgram mBlurProgram; - GLuint mBPosLoc; - GLuint mBUvLoc; - GLuint mBTextureLoc; - GLuint mBOffsetLoc; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp deleted file mode 100644 index bb35889665..0000000000 --- a/libs/renderengine/gl/filters/GenericProgram.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GenericProgram.h" - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -namespace android { -namespace renderengine { -namespace gl { - -GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {} - -GenericProgram::~GenericProgram() { - if (mVertexShaderHandle != 0) { - if (mProgramHandle != 0) { - glDetachShader(mProgramHandle, mVertexShaderHandle); - } - glDeleteShader(mVertexShaderHandle); - } - - if (mFragmentShaderHandle != 0) { - if (mProgramHandle != 0) { - glDetachShader(mProgramHandle, mFragmentShaderHandle); - } - glDeleteShader(mFragmentShaderHandle); - } - - if (mProgramHandle != 0) { - glDeleteProgram(mProgramHandle); - } -} - -void GenericProgram::compile(string vertexShader, string fragmentShader) { - mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader); - mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader); - if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) { - ALOGE("Aborting program creation."); - return; - } - mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle); - mEngine.checkErrors("Linking program"); -} - -void GenericProgram::useProgram() const { - glUseProgram(mProgramHandle); -} - -GLuint GenericProgram::compileShader(GLuint type, string src) const { - const GLuint shader = glCreateShader(type); - if (shader == 0) { - mEngine.checkErrors("Creating shader"); - return 0; - } - const GLchar* charSrc = (const GLchar*)src.c_str(); - glShaderSource(shader, 1, &charSrc, nullptr); - glCompileShader(shader); - - GLint isCompiled = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); - if (isCompiled == GL_FALSE) { - GLint maxLength = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); - string errorLog; - errorLog.reserve(maxLength); - glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data()); - glDeleteShader(shader); - ALOGE("Error compiling shader: %s", errorLog.c_str()); - return 0; - } - return shader; -} -GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const { - const GLuint program = glCreateProgram(); - mEngine.checkErrors("Creating program"); - - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - mEngine.checkErrors("Linking program"); - return program; -} - -GLuint GenericProgram::getUniformLocation(const string name) const { - if (mProgramHandle == 0) { - ALOGE("Can't get location of %s on an invalid program.", name.c_str()); - return -1; - } - return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str()); -} - -GLuint GenericProgram::getAttributeLocation(const string name) const { - if (mProgramHandle == 0) { - ALOGE("Can't get location of %s on an invalid program.", name.c_str()); - return -1; - } - return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str()); -} - -bool GenericProgram::isValid() const { - return mProgramHandle != 0; -} - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h deleted file mode 100644 index 6da2a5af58..0000000000 --- a/libs/renderengine/gl/filters/GenericProgram.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <ui/GraphicTypes.h> -#include "../GLESRenderEngine.h" -#include "../GLFramebuffer.h" - -using namespace std; - -namespace android { -namespace renderengine { -namespace gl { - -class GenericProgram { -public: - explicit GenericProgram(GLESRenderEngine& renderEngine); - ~GenericProgram(); - void compile(string vertexShader, string fragmentShader); - bool isValid() const; - void useProgram() const; - GLuint getAttributeLocation(const string name) const; - GLuint getUniformLocation(const string name) const; - -private: - GLuint compileShader(GLuint type, const string src) const; - GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const; - - GLESRenderEngine& mEngine; - GLuint mVertexShaderHandle = 0; - GLuint mFragmentShaderHandle = 0; - GLuint mProgramHandle = 0; -}; - -} // namespace gl -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h deleted file mode 100644 index 65111278e0..0000000000 --- a/libs/renderengine/include/renderengine/Framebuffer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <cstdint> - -struct ANativeWindowBuffer; - -namespace android { -namespace renderengine { - -class Framebuffer { -public: - virtual ~Framebuffer() = default; - - virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, - const bool useFramebufferCache) = 0; -}; - -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index b3a617c04b..28aa4dd71d 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -46,10 +46,6 @@ struct Buffer { // Fence that will fire when the buffer is ready to be bound. sp<Fence> fence = nullptr; - // Texture identifier to bind the external texture to. - // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. - uint32_t textureName = 0; - // Whether to use filtering when rendering the texture. bool useTextureFiltering = false; @@ -64,9 +60,6 @@ struct Buffer { // overrides the alpha channel of the buffer. bool isOpaque = false; - // HDR color-space setting for Y410. - bool isY410BT2020 = false; - float maxLuminanceNits = 0.0; }; @@ -185,12 +178,10 @@ struct LayerSettings { // compositionengine/impl/ClientCompositionRequestCache.cpp static inline bool operator==(const Buffer& lhs, const Buffer& rhs) { return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence && - lhs.textureName == rhs.textureName && lhs.useTextureFiltering == rhs.useTextureFiltering && lhs.textureTransform == rhs.textureTransform && lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha && - lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 && - lhs.maxLuminanceNits == rhs.maxLuminanceNits; + lhs.isOpaque == rhs.isOpaque && lhs.maxLuminanceNits == rhs.maxLuminanceNits; } static inline bool operator==(const Geometry& lhs, const Geometry& rhs) { @@ -241,13 +232,11 @@ static inline void PrintTo(const Buffer& settings, ::std::ostream* os) { << (settings.buffer.get() ? decodePixelFormat(settings.buffer->getPixelFormat()).c_str() : ""); *os << "\n .fence = " << settings.fence.get(); - *os << "\n .textureName = " << settings.textureName; *os << "\n .useTextureFiltering = " << settings.useTextureFiltering; *os << "\n .textureTransform = "; PrintMatrix(settings.textureTransform, os); *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha; *os << "\n .isOpaque = " << settings.isOpaque; - *os << "\n .isY410BT2020 = " << settings.isY410BT2020; *os << "\n .maxLuminanceNits = " << settings.maxLuminanceNits; *os << "\n}"; } diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h deleted file mode 100644 index 167f13f1bc..0000000000 --- a/libs/renderengine/include/renderengine/Mesh.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDER_ENGINE_MESH_H -#define SF_RENDER_ENGINE_MESH_H - -#include <vector> - -#include <stdint.h> - -namespace android { -namespace renderengine { - -class Mesh { -public: - class Builder; - - enum Primitive { - TRIANGLES = 0x0004, // GL_TRIANGLES - TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP - TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN - }; - - ~Mesh() = default; - - /* - * VertexArray handles the stride automatically. - */ - template <typename TYPE> - class VertexArray { - friend class Mesh; - float* mData; - size_t mStride; - size_t mOffset = 0; - VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {} - - public: - // Returns a vertex array at an offset so its easier to append attributes from - // multiple sources. - VertexArray(VertexArray<TYPE>& other, size_t offset) - : mData(other.mData), mStride(other.mStride), mOffset(offset) {} - - TYPE& operator[](size_t index) { - return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]); - } - TYPE const& operator[](size_t index) const { - return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]); - } - }; - - template <typename TYPE> - VertexArray<TYPE> getPositionArray() { - return VertexArray<TYPE>(getPositions(), mStride); - } - - template <typename TYPE> - VertexArray<TYPE> getTexCoordArray() { - return VertexArray<TYPE>(getTexCoords(), mStride); - } - - template <typename TYPE> - VertexArray<TYPE> getCropCoordArray() { - return VertexArray<TYPE>(getCropCoords(), mStride); - } - - template <typename TYPE> - VertexArray<TYPE> getShadowColorArray() { - return VertexArray<TYPE>(getShadowColor(), mStride); - } - - template <typename TYPE> - VertexArray<TYPE> getShadowParamsArray() { - return VertexArray<TYPE>(getShadowParams(), mStride); - } - - uint16_t* getIndicesArray() { return getIndices(); } - - Primitive getPrimitive() const; - - // returns a pointer to the vertices positions - float const* getPositions() const; - - // returns a pointer to the vertices texture coordinates - float const* getTexCoords() const; - - // returns a pointer to the vertices crop coordinates - float const* getCropCoords() const; - - // returns a pointer to colors - float const* getShadowColor() const; - - // returns a pointer to the shadow params - float const* getShadowParams() const; - - // returns a pointer to indices - uint16_t const* getIndices() const; - - // number of vertices in this mesh - size_t getVertexCount() const; - - // dimension of vertices - size_t getVertexSize() const; - - // dimension of texture coordinates - size_t getTexCoordsSize() const; - - size_t getShadowParamsSize() const; - - size_t getShadowColorSize() const; - - size_t getIndexCount() const; - - // return stride in bytes - size_t getByteStride() const; - - // return stride in floats - size_t getStride() const; - -private: - Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize, - size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount); - Mesh(const Mesh&); - Mesh& operator=(const Mesh&); - Mesh const& operator=(const Mesh&) const; - - float* getPositions(); - float* getTexCoords(); - float* getCropCoords(); - float* getShadowColor(); - float* getShadowParams(); - uint16_t* getIndices(); - - std::vector<float> mVertices; - size_t mVertexCount; - size_t mVertexSize; - size_t mTexCoordsSize; - size_t mCropCoordsSize; - size_t mShadowColorSize; - size_t mShadowParamsSize; - size_t mStride; - Primitive mPrimitive; - std::vector<uint16_t> mIndices; - size_t mIndexCount; -}; - -class Mesh::Builder { -public: - Builder& setPrimitive(Primitive primitive) { - mPrimitive = primitive; - return *this; - }; - Builder& setVertices(size_t vertexCount, size_t vertexSize) { - mVertexCount = vertexCount; - mVertexSize = vertexSize; - return *this; - }; - Builder& setTexCoords(size_t texCoordsSize) { - mTexCoordsSize = texCoordsSize; - return *this; - }; - Builder& setCropCoords(size_t cropCoordsSize) { - mCropCoordsSize = cropCoordsSize; - return *this; - }; - Builder& setShadowAttrs() { - mShadowParamsSize = 3; - mShadowColorSize = 4; - return *this; - }; - Builder& setIndices(size_t indexCount) { - mIndexCount = indexCount; - return *this; - }; - Mesh build() const { - return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize, - mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount}; - } - -private: - size_t mVertexCount = 0; - size_t mVertexSize = 0; - size_t mTexCoordsSize = 0; - size_t mCropCoordsSize = 0; - size_t mShadowColorSize = 0; - size_t mShadowParamsSize = 0; - size_t mIndexCount = 0; - Primitive mPrimitive; -}; - -} // namespace renderengine -} // namespace android -#endif /* SF_RENDER_ENGINE_MESH_H */ diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 0d910c9b29..b992d82fdb 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -22,8 +22,6 @@ #include <math/mat4.h> #include <renderengine/DisplaySettings.h> #include <renderengine/ExternalTexture.h> -#include <renderengine/Framebuffer.h> -#include <renderengine/Image.h> #include <renderengine/LayerSettings.h> #include <stdint.h> #include <sys/types.h> @@ -95,8 +93,6 @@ public: }; enum class RenderEngineType { - GLES = 1, - THREADED = 2, SKIA_GL = 3, SKIA_GL_THREADED = 4, SKIA_VK = 5, @@ -116,9 +112,6 @@ public: // dump the extension strings. always call the base class. virtual void dump(std::string& result) = 0; - virtual void genTextures(size_t count, uint32_t* names) = 0; - virtual void deleteTextures(size_t count, uint32_t const* names) = 0; - // queries that are required to be thread safe virtual size_t getMaxTextureSize() const = 0; virtual size_t getMaxViewportDims() const = 0; @@ -152,9 +145,6 @@ public: // @param layers The layers to draw onto the display, in Z-order. // @param buffer The buffer which will be drawn to. This buffer will be // ready once drawFence fires. - // @param useFramebufferCache True if the framebuffer cache should be used. - // If an implementation does not cache output framebuffers, then this - // parameter does nothing. // @param bufferFence Fence signalling that the buffer is ready to be drawn // to. // @return A future object of FenceResult indicating whether drawing was @@ -162,7 +152,6 @@ public: virtual ftl::Future<FenceResult> drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence); // Clean-up method that should be called on the main thread after the @@ -171,8 +160,6 @@ public: // being drawn, then the implementation is free to silently ignore this call. virtual void cleanupPostRender() = 0; - virtual void cleanFramebufferCache() = 0; - // Returns the priority this context was actually created with. Note: this // may not be the same as specified at context creation time, due to // implementation limits on the number of contexts that can be created at a @@ -204,7 +191,7 @@ public: virtual void setEnableTracing(bool /*tracingEnabled*/) {} protected: - RenderEngine() : RenderEngine(RenderEngineType::GLES) {} + RenderEngine() : RenderEngine(RenderEngineType::SKIA_GL) {} RenderEngine(RenderEngineType type) : mRenderEngineType(type) {} @@ -253,8 +240,7 @@ protected: virtual void drawLayersInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) = 0; + const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0; }; struct RenderEngineCreationArgs { @@ -271,14 +257,13 @@ struct RenderEngineCreationArgs { private: // must be created by Builder via constructor with full argument list - RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement, + RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, bool _supportsBackgroundBlur, RenderEngine::ContextPriority _contextPriority, RenderEngine::RenderEngineType _renderEngineType) : pixelFormat(_pixelFormat), imageCacheSize(_imageCacheSize), - useColorManagement(_useColorManagement), enableProtectedContext(_enableProtectedContext), precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), supportsBackgroundBlur(_supportsBackgroundBlur), @@ -298,10 +283,6 @@ struct RenderEngineCreationArgs::Builder { this->imageCacheSize = imageCacheSize; return *this; } - Builder& setUseColorManagerment(bool useColorManagement) { - this->useColorManagement = useColorManagement; - return *this; - } Builder& setEnableProtectedContext(bool enableProtectedContext) { this->enableProtectedContext = enableProtectedContext; return *this; @@ -323,16 +304,15 @@ struct RenderEngineCreationArgs::Builder { return *this; } RenderEngineCreationArgs build() const { - return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement, - enableProtectedContext, precacheToneMapperShaderOnly, - supportsBackgroundBlur, contextPriority, renderEngineType); + return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext, + precacheToneMapperShaderOnly, supportsBackgroundBlur, + contextPriority, renderEngineType); } private: // 1 means RGBA_8888 int pixelFormat = 1; uint32_t imageCacheSize = 0; - bool useColorManagement = true; bool enableProtectedContext = false; bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h deleted file mode 100644 index c69ace0603..0000000000 --- a/libs/renderengine/include/renderengine/Texture.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDER_ENGINE_TEXTURE_H -#define SF_RENDER_ENGINE_TEXTURE_H - -#include <stdint.h> - -#include <math/mat4.h> - -namespace android { -namespace renderengine { - -class Texture { -public: - enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; - - Texture(); - Texture(Target textureTarget, uint32_t textureName); - ~Texture(); - - void init(Target textureTarget, uint32_t textureName); - - void setMatrix(float const* matrix); - void setFiltering(bool enabled); - void setDimensions(size_t width, size_t height); - - uint32_t getTextureName() const; - uint32_t getTextureTarget() const; - - const mat4& getMatrix() const; - bool getFiltering() const; - size_t getWidth() const; - size_t getHeight() const; - -private: - uint32_t mTextureName; - uint32_t mTextureTarget; - size_t mWidth; - size_t mHeight; - bool mFiltering; - mat4 mTextureMatrix; -}; - -} // namespace renderengine -} // namespace android -#endif /* SF_RENDER_ENGINE_TEXTURE_H */ diff --git a/libs/renderengine/include/renderengine/mock/Framebuffer.h b/libs/renderengine/include/renderengine/mock/Framebuffer.h deleted file mode 100644 index dfb6a4e41e..0000000000 --- a/libs/renderengine/include/renderengine/mock/Framebuffer.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <gmock/gmock.h> -#include <renderengine/Framebuffer.h> - -namespace android { -namespace renderengine { -namespace mock { - -class Framebuffer : public renderengine::Framebuffer { -public: - Framebuffer(); - ~Framebuffer() override; - - MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool)); -}; - -} // namespace mock -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/include/renderengine/mock/Image.h b/libs/renderengine/include/renderengine/mock/Image.h deleted file mode 100644 index 2b0eed1173..0000000000 --- a/libs/renderengine/include/renderengine/mock/Image.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <gmock/gmock.h> -#include <renderengine/Image.h> - -namespace android { -namespace renderengine { -namespace mock { - -class Image : public renderengine::Image { -public: - Image(); - ~Image() override; - - MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected)); -}; - -} // namespace mock -} // namespace renderengine -} // namespace android diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index d3035e24a5..160006d5e6 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -19,9 +19,7 @@ #include <gmock/gmock.h> #include <renderengine/DisplaySettings.h> #include <renderengine/LayerSettings.h> -#include <renderengine/Mesh.h> #include <renderengine/RenderEngine.h> -#include <renderengine/Texture.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <ui/Region.h> @@ -37,9 +35,6 @@ public: MOCK_METHOD0(primeCache, std::future<void>()); MOCK_METHOD1(dump, void(std::string&)); - MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); - MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); - MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); MOCK_CONST_METHOD0(isProtected, bool()); @@ -47,15 +42,14 @@ public: MOCK_METHOD1(useProtectedContext, void(bool)); MOCK_METHOD0(cleanupPostRender, void()); MOCK_CONST_METHOD0(canSkipPostRenderCleanup, bool()); - MOCK_METHOD5(drawLayers, + MOCK_METHOD4(drawLayers, ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, - const std::shared_ptr<ExternalTexture>&, const bool, + const std::shared_ptr<ExternalTexture>&, base::unique_fd&&)); - MOCK_METHOD6(drawLayersInternal, + MOCK_METHOD5(drawLayersInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, - const bool, base::unique_fd&&)); - MOCK_METHOD0(cleanFramebufferCache, void()); + base::unique_fd&&)); MOCK_METHOD0(getContextPriority, int()); MOCK_METHOD0(supportsBackgroundBlur, bool()); MOCK_METHOD1(onActiveDisplaySizeChanged, void(ui::Size)); diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h deleted file mode 100644 index fa6ec10b6e..0000000000 --- a/libs/renderengine/include/renderengine/private/Description.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_ -#define SF_RENDER_ENGINE_DESCRIPTION_H_ - -#include <renderengine/Texture.h> -#include <ui/GraphicTypes.h> - -namespace android { -namespace renderengine { - -/* - * This is the structure that holds the state of the rendering engine. - * This class is used to generate a corresponding GLSL program and set the - * appropriate uniform. - */ -struct Description { - enum class TransferFunction : int { - LINEAR, - SRGB, - ST2084, - HLG, // Hybrid Log-Gamma for HDR. - }; - - static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace); - - Description() = default; - ~Description() = default; - - bool hasInputTransformMatrix() const; - bool hasOutputTransformMatrix() const; - bool hasColorMatrix() const; - bool hasDisplayColorMatrix() const; - - // whether textures are premultiplied - bool isPremultipliedAlpha = false; - // whether this layer is marked as opaque - bool isOpaque = true; - - // corner radius of the layer - float cornerRadius = 0; - - // Size of the rounded rectangle we are cropping to - half2 cropSize; - - // Texture this layer uses - Texture texture; - bool textureEnabled = false; - - // color used when texturing is disabled or when setting alpha. - half4 color; - - // true if the sampled pixel values are in Y410/BT2020 rather than RGBA - bool isY410BT2020 = false; - - // transfer functions for the input/output - TransferFunction inputTransferFunction = TransferFunction::LINEAR; - TransferFunction outputTransferFunction = TransferFunction::LINEAR; - - float displayMaxLuminance; - float maxMasteringLuminance; - float maxContentLuminance; - - // projection matrix - mat4 projectionMatrix; - - // The color matrix will be applied in linear space right before OETF. - mat4 colorMatrix; - // The display color matrix will be applied in gamma space after OETF - mat4 displayColorMatrix; - mat4 inputTransformMatrix; - mat4 outputTransformMatrix; - - // True if this layer will draw a shadow. - bool drawShadows = false; -}; - -} // namespace renderengine -} // namespace android - -#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */ diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index dad3c1993b..90dcae4369 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -23,7 +23,7 @@ #include <SkImage.h> #include <include/gpu/ganesh/SkImageGanesh.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> - +#include <include/gpu/ganesh/gl/GrGLBackendSurface.h> #include <android/hardware_buffer.h> #include "ColorSpaces.h" #include "log/log_main.h" @@ -40,13 +40,44 @@ AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); - GrBackendFormat backendFormat = - GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); - mBackendTexture = - GrAHardwareBufferUtils::MakeBackendTexture(context, buffer, desc.width, desc.height, - &mDeleteProc, &mUpdateProc, &mImageCtx, - createProtectedImage, backendFormat, - isOutputBuffer); + GrBackendFormat backendFormat; + + GrBackendApi backend = context->backend(); + if (backend == GrBackendApi::kOpenGL) { + backendFormat = + GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false); + mBackendTexture = + GrAHardwareBufferUtils::MakeGLBackendTexture(context, + buffer, + desc.width, + desc.height, + &mDeleteProc, + &mUpdateProc, + &mImageCtx, + createProtectedImage, + backendFormat, + isOutputBuffer); + } else if (backend == GrBackendApi::kVulkan) { + backendFormat = + GrAHardwareBufferUtils::GetVulkanBackendFormat(context, + buffer, + desc.format, + false); + mBackendTexture = + GrAHardwareBufferUtils::MakeVulkanBackendTexture(context, + buffer, + desc.width, + desc.height, + &mDeleteProc, + &mUpdateProc, + &mImageCtx, + createProtectedImage, + backendFormat, + isOutputBuffer); + } else { + LOG_ALWAYS_FATAL("Unexpected backend %d", backend); + } + mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); if (!mBackendTexture.isValid() || !desc.width || !desc.height) { LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d " @@ -94,7 +125,7 @@ void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace switch (tex.backend()) { case GrBackendApi::kOpenGL: { GrGLTextureInfo textureInfo; - bool retrievedTextureInfo = tex.getGLTextureInfo(&textureInfo); + bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo); LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d" "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u" diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index f6b91839a3..e1887a882a 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -29,8 +29,6 @@ namespace android::renderengine::skia { namespace { -// Warming shader cache, not framebuffer cache. -constexpr bool kUseFrameBufferCache = false; // clang-format off // Any non-identity matrix will do. @@ -116,8 +114,7 @@ static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettin caster.geometry.positionTransform = transform; auto layers = std::vector<LayerSettings>{layer, caster}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } @@ -154,8 +151,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting for (auto alpha : {half(.2f), half(1.0f)}) { layer.alpha = alpha; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } @@ -183,8 +179,7 @@ static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySetting for (float roundedCornersRadius : {0.0f, 50.f}) { layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius}; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } @@ -207,8 +202,7 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings for (int radius : {9, 60}) { layer.backgroundBlurRadius = radius; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } @@ -254,8 +248,7 @@ static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySetti for (float alpha : {0.5f, 1.f}) { layer.alpha = alpha; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } @@ -292,7 +285,7 @@ static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySetti }; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, @@ -321,7 +314,7 @@ static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySett }; auto layers = std::vector<LayerSettings>{layer}; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } // @@ -436,9 +429,7 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { }; auto layers = std::vector<LayerSettings>{layer}; // call get() to make it synchronous - renderengine - ->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, base::unique_fd()) - .get(); + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/skia/GLExtensions.cpp index 3dd534e602..1d4d35fb54 100644 --- a/libs/renderengine/gl/GLExtensions.cpp +++ b/libs/renderengine/skia/GLExtensions.cpp @@ -23,11 +23,11 @@ #include <stdio.h> #include <stdlib.h> -ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions) +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::skia::GLExtensions) namespace android { namespace renderengine { -namespace gl { +namespace skia { namespace { @@ -68,19 +68,19 @@ void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* rende } char const* GLExtensions::getVendor() const { - return mVendor.string(); + return mVendor.c_str(); } char const* GLExtensions::getRenderer() const { - return mRenderer.string(); + return mRenderer.c_str(); } char const* GLExtensions::getVersion() const { - return mVersion.string(); + return mVersion.c_str(); } char const* GLExtensions::getExtensions() const { - return mExtensions.string(); + return mExtensions.c_str(); } void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { @@ -127,13 +127,13 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt } char const* GLExtensions::getEGLVersion() const { - return mEGLVersion.string(); + return mEGLVersion.c_str(); } char const* GLExtensions::getEGLExtensions() const { - return mEGLExtensions.string(); + return mEGLExtensions.c_str(); } -} // namespace gl +} // namespace skia } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/skia/GLExtensions.h index e415ff304a..0cb1bda0df 100644 --- a/libs/renderengine/gl/GLExtensions.h +++ b/libs/renderengine/skia/GLExtensions.h @@ -29,7 +29,7 @@ namespace android { namespace renderengine { -namespace gl { +namespace skia { class GLExtensions : public Singleton<GLExtensions> { public: @@ -81,7 +81,7 @@ private: GLExtensions& operator=(const GLExtensions&); }; -} // namespace gl +} // namespace skia } // namespace renderengine } // namespace android diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index ff598e7ab5..e253ad596e 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -36,17 +36,26 @@ #include <memory> #include <numeric> -#include "../gl/GLExtensions.h" +#include "GLExtensions.h" #include "log/log_main.h" -bool checkGlError(const char* op, int lineNumber); - namespace android { namespace renderengine { namespace skia { using base::StringAppendF; +static bool checkGlError(const char* op, int lineNumber) { + bool errorFound = false; + GLint error = glGetError(); + while (error != GL_NO_ERROR) { + errorFound = true; + error = glGetError(); + ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); + } + return errorFound; +} + static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, EGLint wanted, EGLConfig* outConfig) { EGLint numConfigs = -1, n = 0; @@ -149,7 +158,7 @@ std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed"); } - auto& extensions = gl::GLExtensions::getInstance(); + auto& extensions = GLExtensions::getInstance(); extensions.initWithEGLStrings(eglVersion, eglExtensions); // The code assumes that ES2 or later is available if this extension is @@ -251,14 +260,13 @@ EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bo SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, EGLSurface protectedPlaceholder) - : SkiaRenderEngine(args.renderEngineType, - static_cast<PixelFormat>(args.pixelFormat), - args.useColorManagement, args.supportsBackgroundBlur), + : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), + args.supportsBackgroundBlur), mEGLDisplay(display), mEGLContext(ctxt), mPlaceholderSurface(placeholder), mProtectedEGLContext(protectedContext), - mProtectedPlaceholderSurface(protectedPlaceholder) { } + mProtectedPlaceholderSurface(protectedPlaceholder) {} SkiaGLRenderEngine::~SkiaGLRenderEngine() { finishRenderingAndAbandonContext(); @@ -343,8 +351,8 @@ base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) { } bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { - if (!gl::GLExtensions::getInstance().hasNativeFenceSync() || - !gl::GLExtensions::getInstance().hasWaitSync()) { + if (!GLExtensions::getInstance().hasNativeFenceSync() || + !GLExtensions::getInstance().hasWaitSync()) { return false; } @@ -379,7 +387,7 @@ bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { base::unique_fd SkiaGLRenderEngine::flush() { ATRACE_CALL(); - if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) { + if (!GLExtensions::getInstance().hasNativeFenceSync()) { return base::unique_fd(); } @@ -470,13 +478,13 @@ EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig co std::optional<RenderEngine::ContextPriority> SkiaGLRenderEngine::createContextPriority( const RenderEngineCreationArgs& args) { - if (!gl::GLExtensions::getInstance().hasContextPriority()) { + if (!GLExtensions::getInstance().hasContextPriority()) { return std::nullopt; } switch (args.contextPriority) { case RenderEngine::ContextPriority::REALTIME: - if (gl::GLExtensions::getInstance().hasRealtimePriority()) { + if (GLExtensions::getInstance().hasRealtimePriority()) { return RenderEngine::ContextPriority::REALTIME; } else { ALOGI("Realtime priority unsupported, degrading gracefully to high priority"); @@ -520,7 +528,7 @@ int SkiaGLRenderEngine::getContextPriority() { } void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { - const gl::GLExtensions& extensions = gl::GLExtensions::getInstance(); + const GLExtensions& extensions = GLExtensions::getInstance(); StringAppendF(&result, "\n ------------RE GLES------------\n"); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 8ea9ee7478..3d0d827068 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -269,10 +269,8 @@ void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { } SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, - bool useColorManagement, bool supportsBackgroundBlur) - : RenderEngine(type), - mDefaultPixelFormat(pixelFormat), - mUseColorManagement(useColorManagement) { + bool supportsBackgroundBlur) + : RenderEngine(type), mDefaultPixelFormat(pixelFormat) { if (supportsBackgroundBlur) { ALOGD("Background Blurs Enabled"); mBlurFilter = new KawaseBlurFilter(); @@ -511,7 +509,8 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( auto effect = shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace, .outputDataspace = parameters.outputDataSpace, - .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha}; + .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha, + .fakeOutputDataspace = parameters.fakeOutputDataspace}; auto effectIter = mRuntimeEffects.find(effect); sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; @@ -651,8 +650,7 @@ private: void SkiaRenderEngine::drawLayersInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/, - base::unique_fd&& bufferFence) { + const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str()); std::lock_guard<std::mutex> lock(mRenderingMutex); @@ -907,12 +905,14 @@ void SkiaRenderEngine::drawLayersInternal( (display.outputDataspace & ui::Dataspace::TRANSFER_MASK) == static_cast<int32_t>(ui::Dataspace::TRANSFER_SRGB); - const ui::Dataspace runtimeEffectDataspace = !dimInLinearSpace && isExtendedHdr + const bool useFakeOutputDataspaceForRuntimeEffect = !dimInLinearSpace && isExtendedHdr; + + const ui::Dataspace fakeDataspace = useFakeOutputDataspaceForRuntimeEffect ? static_cast<ui::Dataspace>( (display.outputDataspace & ui::Dataspace::STANDARD_MASK) | ui::Dataspace::TRANSFER_GAMMA2_2 | (display.outputDataspace & ui::Dataspace::RANGE_MASK)) - : display.outputDataspace; + : ui::Dataspace::UNKNOWN; // If the input dataspace is range extended, the output dataspace transfer is sRGB // and dimmingStage is GAMMA_OETF, dim in linear space instead, and @@ -923,8 +923,7 @@ void SkiaRenderEngine::drawLayersInternal( // luminance in linear space, which color pipelines request GAMMA_OETF break // without a gamma 2.2 fixup. const bool requiresLinearEffect = layer.colorTransform != mat4() || - (mUseColorManagement && - needsToneMapping(layer.sourceDataspace, display.outputDataspace)) || + (needsToneMapping(layer.sourceDataspace, display.outputDataspace)) || (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)) || (!dimInLinearSpace && isExtendedHdr); @@ -935,10 +934,7 @@ void SkiaRenderEngine::drawLayersInternal( continue; } - // If color management is disabled, then mark the source image with the same colorspace as - // the destination surface so that Skia's color management is a no-op. - const ui::Dataspace layerDataspace = - !mUseColorManagement ? display.outputDataspace : layer.sourceDataspace; + const ui::Dataspace layerDataspace = layer.sourceDataspace; SkPaint paint; if (layer.source.buffer.buffer) { @@ -1019,7 +1015,8 @@ void SkiaRenderEngine::drawLayersInternal( .layerDimmingRatio = dimInLinearSpace ? layerDimmingRatio : 1.f, - .outputDataSpace = runtimeEffectDataspace})); + .outputDataSpace = display.outputDataspace, + .fakeOutputDataspace = fakeDataspace})); // Turn on dithering when dimming beyond this (arbitrary) threshold... static constexpr float kDimmingThreshold = 0.2f; @@ -1086,7 +1083,8 @@ void SkiaRenderEngine::drawLayersInternal( .undoPremultipliedAlpha = false, .requiresLinearEffect = requiresLinearEffect, .layerDimmingRatio = layerDimmingRatio, - .outputDataSpace = runtimeEffectDataspace})); + .outputDataSpace = display.outputDataspace, + .fakeOutputDataspace = fakeDataspace})); } if (layer.disableBlending) { diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 6457bfa9eb..3db0c1b58b 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -59,23 +59,17 @@ class BlurFilter; class SkiaRenderEngine : public RenderEngine { public: static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); - SkiaRenderEngine(RenderEngineType type, - PixelFormat pixelFormat, - bool useColorManagement, - bool supportsBackgroundBlur); + SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, bool supportsBackgroundBlur); ~SkiaRenderEngine() override; std::future<void> primeCache() override final; void cleanupPostRender() override final; - void cleanFramebufferCache() override final{ } bool supportsBackgroundBlur() override final { return mBlurFilter != nullptr; } void onActiveDisplaySizeChanged(ui::Size size) override final; int reportShadersCompiled(); - virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override final{}; - virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override final{}; virtual void setEnableTracing(bool tracingEnabled) override final; void useProtectedContext(bool useProtectedContext) override; @@ -142,7 +136,6 @@ private: const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) override final; void dump(std::string& result) override final; @@ -157,11 +150,11 @@ private: bool requiresLinearEffect; float layerDimmingRatio; const ui::Dataspace outputDataSpace; + const ui::Dataspace fakeOutputDataspace; }; sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&); const PixelFormat mDefaultPixelFormat; - const bool mUseColorManagement; // Identifier used for various mappings of layers to various // textures or shaders diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index c16586bb6b..6ecc6ab362 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -592,7 +592,7 @@ std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create( SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaRenderEngine(args.renderEngineType, static_cast<PixelFormat>(args.pixelFormat), - args.useColorManagement, args.supportsBackgroundBlur) {} + args.supportsBackgroundBlur) {} SkiaVkRenderEngine::~SkiaVkRenderEngine() { finishRenderingAndAbandonContext(); diff --git a/libs/renderengine/skia/debug/record.sh b/libs/renderengine/skia/debug/record.sh index e99b7ae390..c818c40371 100755 --- a/libs/renderengine/skia/debug/record.sh +++ b/libs/renderengine/skia/debug/record.sh @@ -16,7 +16,6 @@ elif [ "$1" == "rootandsetup" ]; then # first time use requires these changes adb root adb shell setenforce 0 - adb shell setprop debug.renderengine.backend "skiaglthreaded" adb shell stop adb shell start exit 1; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index f3f2da8a0e..7dde71632b 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -109,7 +109,6 @@ public: virtual renderengine::RenderEngine::RenderEngineType type() = 0; virtual std::unique_ptr<renderengine::RenderEngine> createRenderEngine() = 0; virtual bool typeSupported() = 0; - virtual bool useColorManagement() const = 0; }; class SkiaVkRenderEngineFactory : public RenderEngineFactory { @@ -130,13 +129,11 @@ public: renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) .setImageCacheSize(1) - .setUseColorManagerment(false) .setEnableProtectedContext(false) .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) .setRenderEngineType(type()) - .setUseColorManagerment(useColorManagement()) .build(); return renderengine::skia::SkiaVkRenderEngine::create(reCreationArgs); } @@ -144,14 +141,9 @@ public: bool typeSupported() override { return skia::SkiaVkRenderEngine::canSupportSkiaVkRenderEngine(); } - bool useColorManagement() const override { return false; } void skip() { GTEST_SKIP(); } }; -class SkiaVkCMRenderEngineFactory : public SkiaVkRenderEngineFactory { -public: - bool useColorManagement() const override { return true; } -}; class SkiaGLESRenderEngineFactory : public RenderEngineFactory { public: std::string name() override { return "SkiaGLRenderEngineFactory"; } @@ -170,13 +162,11 @@ public: .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) .setRenderEngineType(type()) - .setUseColorManagerment(useColorManagement()) .build(); return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } bool typeSupported() override { return true; } - bool useColorManagement() const override { return false; } }; class SkiaGLESCMRenderEngineFactory : public RenderEngineFactory { @@ -197,13 +187,11 @@ public: .setSupportsBackgroundBlur(true) .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) .setRenderEngineType(type()) - .setUseColorManagerment(useColorManagement()) .build(); return renderengine::skia::SkiaGLRenderEngine::create(reCreationArgs); } bool typeSupported() override { return true; } - bool useColorManagement() const override { return true; } }; class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> { @@ -290,9 +278,6 @@ public: if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) { writeBufferToFile("/data/texture_out_"); } - for (uint32_t texName : mTexNames) { - mRE->deleteTextures(1, &texName); - } const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); @@ -505,7 +490,7 @@ public: void invokeDraw(const renderengine::DisplaySettings& settings, const std::vector<renderengine::LayerSettings>& layers) { ftl::Future<FenceResult> future = - mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); + mRE->drawLayers(settings, layers, mBuffer, base::unique_fd()); ASSERT_TRUE(future.valid()); auto result = future.get(); @@ -635,8 +620,6 @@ public: std::unique_ptr<renderengine::RenderEngine> mRE; std::shared_ptr<renderengine::ExternalTexture> mBuffer; - - std::vector<uint32_t> mTexNames; }; void RenderEngineTest::initializeRenderEngine() { @@ -680,9 +663,6 @@ struct BufferSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, RenderEngineTest* fixture) { const auto buf = fixture->allocateSourceBuffer(1, 1); - uint32_t texName; - fixture->mRE->genTextures(1, &texName); - fixture->mTexNames.push_back(texName); uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -702,7 +682,6 @@ struct BufferSourceVariant { buf->getBuffer()->unlock(); layer.source.buffer.buffer = buf; - layer.source.buffer.textureName = texName; layer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; OpaquenessVariant::setOpaqueBit(layer); } @@ -1251,9 +1230,6 @@ void RenderEngineTest::fillRedBufferTextureTransform() { // Here will allocate a checker board texture, but transform texture // coordinates so that only the upper left is applied. const auto buf = allocateSourceBuffer(2, 2); - uint32_t texName; - RenderEngineTest::mRE->genTextures(1, &texName); - this->mTexNames.push_back(texName); uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -1274,7 +1250,6 @@ void RenderEngineTest::fillRedBufferTextureTransform() { buf->getBuffer()->unlock(); layer.source.buffer.buffer = buf; - layer.source.buffer.textureName = texName; // Transform coordinates to only be inside the red quadrant. layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f)); layer.alpha = 1.0f; @@ -1300,9 +1275,6 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { renderengine::LayerSettings layer; const auto buf = allocateSourceBuffer(1, 1); - uint32_t texName; - RenderEngineTest::mRE->genTextures(1, &texName); - this->mTexNames.push_back(texName); uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -1314,7 +1286,6 @@ void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { buf->getBuffer()->unlock(); layer.source.buffer.buffer = buf; - layer.source.buffer.textureName = texName; layer.source.buffer.usePremultipliedAlpha = true; layer.alpha = 0.5f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); @@ -1339,9 +1310,6 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { renderengine::LayerSettings layer; const auto buf = allocateSourceBuffer(1, 1); - uint32_t texName; - RenderEngineTest::mRE->genTextures(1, &texName); - this->mTexNames.push_back(texName); uint8_t* pixels; buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -1353,7 +1321,6 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { buf->getBuffer()->unlock(); layer.source.buffer.buffer = buf; - layer.source.buffer.textureName = texName; layer.source.buffer.usePremultipliedAlpha = false; layer.alpha = 0.5f; layer.geometry.boundaries = Rect(1, 1).toFloatRect(); @@ -1559,9 +1526,7 @@ void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3 INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(), - std::make_shared<SkiaGLESCMRenderEngineFactory>(), - std::make_shared<SkiaVkRenderEngineFactory>(), - std::make_shared<SkiaVkCMRenderEngineFactory>())); + std::make_shared<SkiaVkRenderEngineFactory>())); TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { if (!GetParam()->typeSupported()) { @@ -1645,8 +1610,7 @@ TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { layer.geometry.boundaries = fullscreenRect().toFloatRect(); BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); layers.push_back(layer); - ftl::Future<FenceResult> future = - mRE->drawLayers(settings, layers, nullptr, true, base::unique_fd()); + ftl::Future<FenceResult> future = mRE->drawLayers(settings, layers, nullptr, base::unique_fd()); ASSERT_TRUE(future.valid()); auto result = future.get(); @@ -1745,7 +1709,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -1756,7 +1720,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -1895,7 +1859,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -1906,7 +1870,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_o TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -2045,7 +2009,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -2056,7 +2020,7 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_b TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) { const auto& renderEngineFactory = GetParam(); // skip for non color management - if (!renderEngineFactory->typeSupported() || !renderEngineFactory->useColorManagement()) { + if (!renderEngineFactory->typeSupported()) { GTEST_SKIP(); } @@ -2298,14 +2262,14 @@ TEST_P(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layers.push_back(layer); ftl::Future<FenceResult> futureOne = - mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd()); + mRE->drawLayers(settings, layers, mBuffer, base::unique_fd()); ASSERT_TRUE(futureOne.valid()); auto resultOne = futureOne.get(); ASSERT_TRUE(resultOne.ok()); auto fenceOne = resultOne.value(); ftl::Future<FenceResult> futureTwo = - mRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(fenceOne->dup())); + mRE->drawLayers(settings, layers, mBuffer, base::unique_fd(fenceOne->dup())); ASSERT_TRUE(futureTwo.valid()); auto resultTwo = futureTwo.get(); ASSERT_TRUE(resultTwo.ok()); @@ -2592,10 +2556,6 @@ TEST_P(RenderEngineTest, testBorder) { GTEST_SKIP(); } - if (!GetParam()->useColorManagement()) { - GTEST_SKIP(); - } - initializeRenderEngine(); const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB; @@ -3017,15 +2977,11 @@ TEST_P(RenderEngineTest, test_isOpaque) { std::vector<renderengine::LayerSettings> layers{greenLayer}; invokeDraw(display, layers); - if (GetParam()->useColorManagement()) { - expectBufferColor(rect, 117, 251, 76, 255); - } else { - expectBufferColor(rect, 0, 255, 0, 255); - } + expectBufferColor(rect, 117, 251, 76, 255); } TEST_P(RenderEngineTest, test_tonemapPQMatches) { - if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } @@ -3042,7 +2998,7 @@ TEST_P(RenderEngineTest, test_tonemapPQMatches) { } TEST_P(RenderEngineTest, test_tonemapHLGMatches) { - if (!GetParam()->typeSupported() || !GetParam()->useColorManagement()) { + if (!GetParam()->typeSupported()) { GTEST_SKIP(); } @@ -3262,9 +3218,9 @@ TEST_P(RenderEngineTest, primeShaderCache) { fut.wait(); } - const int minimumExpectedShadersCompiled = GetParam()->useColorManagement() ? 60 : 30; + static constexpr int kMinimumExpectedShadersCompiled = 60; ASSERT_GT(static_cast<skia::SkiaGLRenderEngine*>(mRE.get())->reportShadersCompiled(), - minimumExpectedShadersCompiled); + kMinimumExpectedShadersCompiled); } } // namespace renderengine } // namespace android diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index fe3a16d4bf..475dc15788 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -36,7 +36,7 @@ struct RenderEngineThreadedTest : public ::testing::Test { void SetUp() override { mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }, - renderengine::RenderEngine::RenderEngineType::THREADED); + renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED); } std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; @@ -57,18 +57,6 @@ TEST_F(RenderEngineThreadedTest, primeCache) { mThreadedRE->getContextPriority(); } -TEST_F(RenderEngineThreadedTest, genTextures) { - uint32_t texName; - EXPECT_CALL(*mRenderEngine, genTextures(1, &texName)); - mThreadedRE->genTextures(1, &texName); -} - -TEST_F(RenderEngineThreadedTest, deleteTextures) { - uint32_t texName; - EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName)); - mThreadedRE->deleteTextures(1, &texName); -} - TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) { size_t size = 20; EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); @@ -155,11 +143,11 @@ TEST_F(RenderEngineThreadedTest, drawLayers) { .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); ftl::Future<FenceResult> future = - mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence)); ASSERT_TRUE(future.valid()); auto result = future.get(); ASSERT_TRUE(result.ok()); @@ -188,11 +176,11 @@ TEST_F(RenderEngineThreadedTest, drawLayers_protectedLayer) { .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); ftl::Future<FenceResult> future = - mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence)); ASSERT_TRUE(future.valid()); auto result = future.get(); ASSERT_TRUE(result.ok()); @@ -216,11 +204,11 @@ TEST_F(RenderEngineThreadedTest, drawLayers_protectedOutputBuffer) { .WillOnce([&](const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) { resultPromise->set_value(Fence::NO_FENCE); }); ftl::Future<FenceResult> future = - mThreadedRE->drawLayers(settings, layers, buffer, false, std::move(bufferFence)); + mThreadedRE->drawLayers(settings, layers, buffer, std::move(bufferFence)); ASSERT_TRUE(future.valid()); auto result = future.get(); ASSERT_TRUE(result.ok()); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 6a1561abcd..2cb66cbebc 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -27,8 +27,6 @@ #include <processgroup/processgroup.h> #include <utils/Trace.h> -#include "gl/GLESRenderEngine.h" - using namespace std::chrono_literals; namespace android { @@ -175,46 +173,6 @@ void RenderEngineThreaded::dump(std::string& result) { result.assign(resultFuture.get()); } -void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { - ATRACE_CALL(); - // This is a no-op in SkiaRenderEngine. - if (getRenderEngineType() != RenderEngineType::THREADED) { - return; - } - std::promise<void> resultPromise; - std::future<void> resultFuture = resultPromise.get_future(); - { - std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::genTextures"); - instance.genTextures(count, names); - resultPromise.set_value(); - }); - } - mCondition.notify_one(); - resultFuture.wait(); -} - -void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { - ATRACE_CALL(); - // This is a no-op in SkiaRenderEngine. - if (getRenderEngineType() != RenderEngineType::THREADED) { - return; - } - std::promise<void> resultPromise; - std::future<void> resultFuture = resultPromise.get_future(); - { - std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::deleteTextures"); - instance.deleteTextures(count, names); - resultPromise.set_value(); - }); - } - mCondition.notify_one(); - resultFuture.wait(); -} - void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { ATRACE_CALL(); @@ -285,48 +243,32 @@ bool RenderEngineThreaded::canSkipPostRenderCleanup() const { void RenderEngineThreaded::drawLayersInternal( const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) { + const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { resultPromise->set_value(Fence::NO_FENCE); return; } ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( const DisplaySettings& display, const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer, const bool useFramebufferCache, - base::unique_fd&& bufferFence) { + const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { ATRACE_CALL(); const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); int fd = bufferFence.release(); { std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([resultPromise, display, layers, buffer, useFramebufferCache, - fd](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::drawLayers"); - instance.updateProtectedContext(layers, buffer); - instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, - useFramebufferCache, base::unique_fd(fd)); - }); + mFunctionCalls.push( + [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::drawLayers"); + instance.updateProtectedContext(layers, buffer); + instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, + base::unique_fd(fd)); + }); } mCondition.notify_one(); return resultFuture; } -void RenderEngineThreaded::cleanFramebufferCache() { - ATRACE_CALL(); - // This function is designed so it can run asynchronously, so we do not need to wait - // for the futures. - { - std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::cleanFramebufferCache"); - instance.cleanFramebufferCache(); - }); - } - mCondition.notify_one(); -} - int RenderEngineThreaded::getContextPriority() { std::promise<int> resultPromise; std::future<int> resultFuture = resultPromise.get_future(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 6eb108e064..43ec011b73 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -46,8 +46,6 @@ public: void dump(std::string& result) override; - void genTextures(size_t count, uint32_t* names) override; - void deleteTextures(size_t count, uint32_t const* names) override; size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; @@ -57,10 +55,8 @@ public: ftl::Future<FenceResult> drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) override; - void cleanFramebufferCache() override; int getContextPriority() override; bool supportsBackgroundBlur() override; void onActiveDisplaySizeChanged(ui::Size size) override; @@ -75,7 +71,7 @@ protected: const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, - const bool useFramebufferCache, base::unique_fd&& bufferFence) override; + base::unique_fd&& bufferFence) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp index c85517a976..ef039e5c36 100644 --- a/libs/shaders/shaders.cpp +++ b/libs/shaders/shaders.cpp @@ -168,8 +168,8 @@ void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, void generateOETF(std::string& shader) { // Only support gamma 2.2 for now shader.append(R"( - float OETF(float3 linear) { - return sign(linear) * pow(abs(linear), (1.0 / 2.2)); + float3 OETF(float3 linear) { + return sign(linear) * pow(abs(linear), float3(1.0 / 2.2)); } )"); } diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index cc96f83578..4be0a3a9d9 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -115,7 +115,7 @@ sp<Fence> Fence::merge(const char* name, const sp<Fence>& f1, sp<Fence> Fence::merge(const String8& name, const sp<Fence>& f1, const sp<Fence>& f2) { - return merge(name.string(), f1, f2); + return merge(name.c_str(), f1, f2); } int Fence::dup() const { diff --git a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp index ad1d57aaee..f1f403548d 100644 --- a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp +++ b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp @@ -54,7 +54,7 @@ void UltraHdrDecFuzzer::process() { std::cout << "input buffer size " << jpegImgR.length << std::endl; std::cout << "image dimensions " << info.width << " x " << info.width << std::endl; #endif - size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8); + size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4); jpegr_uncompressed_struct decodedJpegR; auto decodedRaw = std::make_unique<uint8_t[]>(outSize); decodedJpegR.data = decodedRaw.get(); diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp index 1dce57c7c5..2d59e8bb88 100644 --- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp +++ b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp @@ -23,8 +23,8 @@ // User include files #include "ultrahdr/gainmapmath.h" -#include "ultrahdr/jpegencoderhelper.h" #include "ultrahdr/jpegdecoderhelper.h" +#include "ultrahdr/jpegencoderhelper.h" #include "utils/Log.h" using namespace android::ultrahdr; @@ -50,7 +50,7 @@ public: UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; void process(); void fillP010Buffer(uint16_t* data, int width, int height, int stride); - void fill420Buffer(uint8_t* data, int size); + void fill420Buffer(uint8_t* data, int width, int height, int stride); private: FuzzedDataProvider mFdp; @@ -60,11 +60,12 @@ void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, in uint16_t* tmp = data; std::vector<uint16_t> buffer(16); for (int i = 0; i < buffer.size(); i++) { - buffer[i] = mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1); + buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6; } for (int j = 0; j < height; j++) { for (int i = 0; i < width; i += buffer.size()) { - memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (width - i))); + memcpy(tmp + i, buffer.data(), + std::min((int)buffer.size(), (width - i)) * sizeof(*data)); std::shuffle(buffer.begin(), buffer.end(), std::default_random_engine(std::random_device{}())); } @@ -72,13 +73,18 @@ void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, in } } -void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int size) { +void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) { + uint8_t* tmp = data; std::vector<uint8_t> buffer(16); mFdp.ConsumeData(buffer.data(), buffer.size()); - for (int i = 0; i < size; i += buffer.size()) { - memcpy(data + i, buffer.data(), std::min((int)buffer.size(), (size - i))); - std::shuffle(buffer.begin(), buffer.end(), - std::default_random_engine(std::random_device{}())); + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i += buffer.size()) { + memcpy(tmp + i, buffer.data(), + std::min((int)buffer.size(), (width - i)) * sizeof(*data)); + std::shuffle(buffer.begin(), buffer.end(), + std::default_random_engine(std::random_device{}())); + } + tmp += stride; } } @@ -119,9 +125,10 @@ void UltraHdrEncFuzzer::process() { int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight); height = (height >> 1) << 1; - std::unique_ptr<uint16_t[]> bufferY = nullptr; - std::unique_ptr<uint16_t[]> bufferUV = nullptr; - std::unique_ptr<uint8_t[]> yuv420ImgRaw = nullptr; + std::unique_ptr<uint16_t[]> bufferYHdr = nullptr; + std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr; + std::unique_ptr<uint8_t[]> bufferYSdr = nullptr; + std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr; std::unique_ptr<uint8_t[]> grayImgRaw = nullptr; if (muxSwitch != 4) { // init p010 image @@ -135,30 +142,27 @@ void UltraHdrEncFuzzer::process() { int bppP010 = 2; if (isUVContiguous) { size_t p010Size = yStride * height * 3 / 2; - bufferY = std::make_unique<uint16_t[]>(p010Size); - p010Img.data = bufferY.get(); + bufferYHdr = std::make_unique<uint16_t[]>(p010Size); + p010Img.data = bufferYHdr.get(); p010Img.chroma_data = nullptr; p010Img.chroma_stride = 0; - fillP010Buffer(bufferY.get(), width, height, yStride); - fillP010Buffer(bufferY.get() + yStride * height, width, height / 2, yStride); + fillP010Buffer(bufferYHdr.get(), width, height, yStride); + fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride); } else { int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128); size_t p010YSize = yStride * height; - bufferY = std::make_unique<uint16_t[]>(p010YSize); - p010Img.data = bufferY.get(); - fillP010Buffer(bufferY.get(), width, height, yStride); + bufferYHdr = std::make_unique<uint16_t[]>(p010YSize); + p010Img.data = bufferYHdr.get(); + fillP010Buffer(bufferYHdr.get(), width, height, yStride); size_t p010UVSize = uvStride * p010Img.height / 2; - bufferUV = std::make_unique<uint16_t[]>(p010UVSize); - p010Img.chroma_data = bufferUV.get(); + bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize); + p010Img.chroma_data = bufferUVHdr.get(); p010Img.chroma_stride = uvStride; - fillP010Buffer(bufferUV.get(), width, height / 2, uvStride); + fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride); } } else { - int map_width = width / kMapDimensionScaleFactor; - int map_height = height / kMapDimensionScaleFactor; - map_width = static_cast<size_t>(floor((map_width + kJpegBlock - 1) / kJpegBlock)) * - kJpegBlock; - map_height = ((map_height + 1) >> 1) << 1; + size_t map_width = width / kMapDimensionScaleFactor; + size_t map_height = height / kMapDimensionScaleFactor; // init 400 image grayImg.width = map_width; grayImg.height = map_height; @@ -167,7 +171,7 @@ void UltraHdrEncFuzzer::process() { const size_t graySize = map_width * map_height; grayImgRaw = std::make_unique<uint8_t[]>(graySize); grayImg.data = grayImgRaw.get(); - fill420Buffer(grayImgRaw.get(), graySize); + fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width); grayImg.chroma_data = nullptr; grayImg.luma_stride = 0; grayImg.chroma_stride = 0; @@ -175,17 +179,38 @@ void UltraHdrEncFuzzer::process() { if (muxSwitch > 0) { // init 420 image + bool isUVContiguous = mFdp.ConsumeBool(); + bool hasYStride = mFdp.ConsumeBool(); + int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width; yuv420Img.width = width; yuv420Img.height = height; yuv420Img.colorGamut = yuv420Cg; - - const size_t yuv420Size = (yuv420Img.width * yuv420Img.height * 3) / 2; - yuv420ImgRaw = std::make_unique<uint8_t[]>(yuv420Size); - yuv420Img.data = yuv420ImgRaw.get(); - fill420Buffer(yuv420ImgRaw.get(), yuv420Size); - yuv420Img.chroma_data = nullptr; - yuv420Img.luma_stride = 0; - yuv420Img.chroma_stride = 0; + yuv420Img.luma_stride = hasYStride ? yStride : 0; + if (isUVContiguous) { + size_t yuv420Size = yStride * height * 3 / 2; + bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size); + yuv420Img.data = bufferYSdr.get(); + yuv420Img.chroma_data = nullptr; + yuv420Img.chroma_stride = 0; + fill420Buffer(bufferYSdr.get(), width, height, yStride); + fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2, + yStride / 2); + fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2, + yStride / 2); + } else { + int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128); + size_t yuv420YSize = yStride * height; + bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize); + yuv420Img.data = bufferYSdr.get(); + fill420Buffer(bufferYSdr.get(), width, height, yStride); + size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2; + bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize); + yuv420Img.chroma_data = bufferUVSdr.get(); + yuv420Img.chroma_stride = uvStride; + fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride); + fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2, + uvStride); + } } // dest @@ -202,6 +227,8 @@ void UltraHdrEncFuzzer::process() { std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl; std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl; std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl; + std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl; + std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl; std::cout << "quality factor " << quality << std::endl; #endif @@ -216,8 +243,19 @@ void UltraHdrEncFuzzer::process() { } else { // compressed img JpegEncoderHelper encoder; - if (encoder.compressImage(yuv420Img.data, yuv420Img.width, yuv420Img.height, quality, - nullptr, 0)) { + struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img; + if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width; + if (!yuv420ImgCopy.chroma_data) { + uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data); + yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height; + yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1; + } + + if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data), + reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data), + yuv420ImgCopy.width, yuv420ImgCopy.height, + yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride, + quality, nullptr, 0)) { jpegImg.length = encoder.getCompressedImageSize(); jpegImg.maxLength = jpegImg.length; jpegImg.data = encoder.getCompressedImagePtr(); @@ -232,8 +270,9 @@ void UltraHdrEncFuzzer::process() { } else if (muxSwitch == 4) { // api 4 jpegImgR.length = 0; JpegEncoderHelper gainMapEncoder; - if (gainMapEncoder.compressImage(grayImg.data, grayImg.width, grayImg.height, - quality, nullptr, 0, true)) { + if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data), + nullptr, grayImg.width, grayImg.height, + grayImg.width, 0, quality, nullptr, 0)) { jpegGainMap.length = gainMapEncoder.getCompressedImageSize(); jpegGainMap.maxLength = jpegImg.length; jpegGainMap.data = gainMapEncoder.getCompressedImagePtr(); @@ -264,7 +303,8 @@ void UltraHdrEncFuzzer::process() { jpegr_info_struct info{0, 0, &iccData, &exifData}; status = jpegHdr.getJPEGRInfo(&jpegImgR, &info); if (status == android::OK) { - size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_SDR) ? 4 : 8); + size_t outSize = + info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4); jpegr_uncompressed_struct decodedJpegR; auto decodedRaw = std::make_unique<uint8_t[]>(outSize); decodedJpegR.data = decodedRaw.get(); diff --git a/libs/ultrahdr/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp index e1c5085b14..ae9c4ca338 100644 --- a/libs/ultrahdr/gainmapmath.cpp +++ b/libs/ultrahdr/gainmapmath.cpp @@ -168,7 +168,7 @@ Color srgbInvOetf(Color e_gamma) { // See IEC 61966-2-1, Equations F.5 and F.6. float srgbInvOetfLUT(float e_gamma) { - uint32_t value = static_cast<uint32_t>(e_gamma * kSrgbInvOETFNumEntries); + uint32_t value = static_cast<uint32_t>(e_gamma * (kSrgbInvOETFNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1); return kSrgbInvOETF[value]; @@ -288,7 +288,7 @@ Color hlgOetf(Color e) { } float hlgOetfLUT(float e) { - uint32_t value = static_cast<uint32_t>(e * kHlgOETFNumEntries); + uint32_t value = static_cast<uint32_t>(e * (kHlgOETFNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgOETFNumEntries - 1); @@ -315,7 +315,7 @@ Color hlgInvOetf(Color e_gamma) { } float hlgInvOetfLUT(float e_gamma) { - uint32_t value = static_cast<uint32_t>(e_gamma * kHlgInvOETFNumEntries); + uint32_t value = static_cast<uint32_t>(e_gamma * (kHlgInvOETFNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1); @@ -344,7 +344,7 @@ Color pqOetf(Color e) { } float pqOetfLUT(float e) { - uint32_t value = static_cast<uint32_t>(e * kPqOETFNumEntries); + uint32_t value = static_cast<uint32_t>(e * (kPqOETFNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqOETFNumEntries - 1); @@ -376,7 +376,7 @@ Color pqInvOetf(Color e_gamma) { } float pqInvOetfLUT(float e_gamma) { - uint32_t value = static_cast<uint32_t>(e_gamma * kPqInvOETFNumEntries); + uint32_t value = static_cast<uint32_t>(e_gamma * (kPqInvOETFNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place value = CLIP3(value, 0, kPqInvOETFNumEntries - 1); diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h index 50b4d2fab1..9f1238f718 100644 --- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h +++ b/libs/ultrahdr/include/ultrahdr/gainmapmath.h @@ -172,7 +172,7 @@ struct GainLUT { } float getGainFactor(float gain) { - uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1)); + uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1) + 0.5); //TODO() : Remove once conversion modules have appropriate clamping in place idx = CLIP3(idx, 0, kGainFactorNumEntries - 1); return mGainTable[idx]; diff --git a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h index 2c6778e299..9d06415cb3 100644 --- a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h +++ b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h @@ -19,6 +19,7 @@ // We must include cstdio before jpeglib.h. It is a requirement of libjpeg. #include <cstdio> +#include <vector> extern "C" { #include <jerror.h> @@ -26,10 +27,11 @@ extern "C" { } #include <utils/Errors.h> -#include <vector> namespace android::ultrahdr { +#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m)) + /* * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format. * This class is not thread-safe. @@ -46,8 +48,9 @@ public: * ICC segment which will be added to the compressed image. * Returns false if errors occur during compression. */ - bool compressImage(const void* image, int width, int height, int quality, - const void* iccBuffer, unsigned int iccSize, bool isSingleChannel = false); + bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height, + int lumaStride, int chromaStride, int quality, const void* iccBuffer, + unsigned int iccSize); /* * Returns the compressed JPEG buffer pointer. This method must be called only after calling @@ -66,6 +69,7 @@ public: * We must pass at least 16 scanlines according to libjpeg documentation. */ static const int kCompressBatchSize = 16; + private: // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be // passed into jpeg library. @@ -75,15 +79,16 @@ private: static void outputErrorMessage(j_common_ptr cinfo); // Returns false if errors occur. - bool encode(const void* inYuv, int width, int height, int jpegQuality, - const void* iccBuffer, unsigned int iccSize, bool isSingleChannel); + bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height, + int lumaStride, int chromaStride, int quality, const void* iccBuffer, + unsigned int iccSize); void setJpegDestination(jpeg_compress_struct* cinfo); void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo, bool isSingleChannel); // Returns false if errors occur. - bool compress(jpeg_compress_struct* cinfo, const uint8_t* image, bool isSingleChannel); - bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv); - bool compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image); + bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer, + int lumaStride, int chromaStride); + bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride); // The block size for encoded jpeg image buffer. static const int kBlockSize = 16384; diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp index 9394a83e46..13ae7424d5 100644 --- a/libs/ultrahdr/jpegencoderhelper.cpp +++ b/libs/ultrahdr/jpegencoderhelper.cpp @@ -23,8 +23,6 @@ namespace android::ultrahdr { -#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m)) - // The destination manager that can access |mResultBuffer| in JpegEncoderHelper. struct destination_mgr { struct jpeg_destination_mgr mgr; @@ -35,11 +33,12 @@ JpegEncoderHelper::JpegEncoderHelper() {} JpegEncoderHelper::~JpegEncoderHelper() {} -bool JpegEncoderHelper::compressImage(const void* image, int width, int height, int quality, - const void* iccBuffer, unsigned int iccSize, - bool isSingleChannel) { +bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, + int height, int lumaStride, int chromaStride, int quality, + const void* iccBuffer, unsigned int iccSize) { mResultBuffer.clear(); - if (!encode(image, width, height, quality, iccBuffer, iccSize, isSingleChannel)) { + if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer, + iccSize)) { return false; } ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height, @@ -87,25 +86,24 @@ void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) { ALOGE("%s\n", buffer); } -bool JpegEncoderHelper::encode(const void* image, int width, int height, int jpegQuality, - const void* iccBuffer, unsigned int iccSize, bool isSingleChannel) { +bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, + int height, int lumaStride, int chromaStride, int quality, + const void* iccBuffer, unsigned int iccSize) { jpeg_compress_struct cinfo; jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); - // Override output_message() to print error log with ALOGE(). cinfo.err->output_message = &outputErrorMessage; jpeg_create_compress(&cinfo); setJpegDestination(&cinfo); - - setJpegCompressStruct(width, height, jpegQuality, &cinfo, isSingleChannel); + setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr); jpeg_start_compress(&cinfo, TRUE); - if (iccBuffer != nullptr && iccSize > 0) { jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize); } - - bool status = compress(&cinfo, static_cast<const uint8_t*>(image), isSingleChannel); + bool status = cinfo.num_components == 1 + ? compressY(&cinfo, yBuffer, lumaStride) + : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride); jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); @@ -141,27 +139,23 @@ void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality } } -bool JpegEncoderHelper::compress(jpeg_compress_struct* cinfo, const uint8_t* image, - bool isSingleChannel) { - return isSingleChannel ? compressSingleChannel(cinfo, image) : compressYuv(cinfo, image); -} - -bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yuv) { +bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, + const uint8_t* uvBuffer, int lumaStride, int chromaStride) { JSAMPROW y[kCompressBatchSize]; JSAMPROW cb[kCompressBatchSize / 2]; JSAMPROW cr[kCompressBatchSize / 2]; JSAMPARRAY planes[3]{y, cb, cr}; - size_t y_plane_size = cinfo->image_width * cinfo->image_height; - size_t uv_plane_size = y_plane_size / 4; - uint8_t* y_plane = const_cast<uint8_t*>(yuv); - uint8_t* u_plane = const_cast<uint8_t*>(yuv + y_plane_size); - uint8_t* v_plane = const_cast<uint8_t*>(yuv + y_plane_size + uv_plane_size); + size_t y_plane_size = lumaStride * cinfo->image_height; + size_t u_plane_size = chromaStride * cinfo->image_height / 2; + uint8_t* y_plane = const_cast<uint8_t*>(yBuffer); + uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer); + uint8_t* v_plane = const_cast<uint8_t*>(u_plane + u_plane_size); std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width); memset(empty.get(), 0, cinfo->image_width); const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize); - const bool is_width_aligned = (aligned_width == cinfo->image_width); + const bool need_padding = (lumaStride < aligned_width); std::unique_ptr<uint8_t[]> buffer_intrm = nullptr; uint8_t* y_plane_intrm = nullptr; uint8_t* u_plane_intrm = nullptr; @@ -170,7 +164,7 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* JSAMPROW cb_intrm[kCompressBatchSize / 2]; JSAMPROW cr_intrm[kCompressBatchSize / 2]; JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm}; - if (!is_width_aligned) { + if (need_padding) { size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2; buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size); y_plane_intrm = buffer_intrm.get(); @@ -195,11 +189,11 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* for (int i = 0; i < kCompressBatchSize; ++i) { size_t scanline = cinfo->next_scanline + i; if (scanline < cinfo->image_height) { - y[i] = y_plane + scanline * cinfo->image_width; + y[i] = y_plane + scanline * lumaStride; } else { y[i] = empty.get(); } - if (!is_width_aligned) { + if (need_padding) { memcpy(y_intrm[i], y[i], cinfo->image_width); } } @@ -207,18 +201,18 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* for (int i = 0; i < kCompressBatchSize / 2; ++i) { size_t scanline = cinfo->next_scanline / 2 + i; if (scanline < cinfo->image_height / 2) { - int offset = scanline * (cinfo->image_width / 2); + int offset = scanline * chromaStride; cb[i] = u_plane + offset; cr[i] = v_plane + offset; } else { cb[i] = cr[i] = empty.get(); } - if (!is_width_aligned) { + if (need_padding) { memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2); memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2); } } - int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, + int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes, kCompressBatchSize); if (processed != kCompressBatchSize) { ALOGE("Number of processed lines does not equal input lines."); @@ -228,22 +222,23 @@ bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* return true; } -bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const uint8_t* image) { +bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, + int lumaStride) { JSAMPROW y[kCompressBatchSize]; JSAMPARRAY planes[1]{y}; - uint8_t* y_plane = const_cast<uint8_t*>(image); + uint8_t* y_plane = const_cast<uint8_t*>(yBuffer); std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width); memset(empty.get(), 0, cinfo->image_width); const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize); - bool is_width_aligned = (aligned_width == cinfo->image_width); + const bool need_padding = (lumaStride < aligned_width); std::unique_ptr<uint8_t[]> buffer_intrm = nullptr; uint8_t* y_plane_intrm = nullptr; uint8_t* u_plane_intrm = nullptr; JSAMPROW y_intrm[kCompressBatchSize]; JSAMPARRAY planes_intrm[]{y_intrm}; - if (!is_width_aligned) { + if (need_padding) { size_t mcu_row_size = aligned_width * kCompressBatchSize; buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size); y_plane_intrm = buffer_intrm.get(); @@ -257,15 +252,15 @@ bool JpegEncoderHelper::compressSingleChannel(jpeg_compress_struct* cinfo, const for (int i = 0; i < kCompressBatchSize; ++i) { size_t scanline = cinfo->next_scanline + i; if (scanline < cinfo->image_height) { - y[i] = y_plane + scanline * cinfo->image_width; + y[i] = y_plane + scanline * lumaStride; } else { y[i] = empty.get(); } - if (!is_width_aligned) { + if (need_padding) { memcpy(y_intrm[i], y[i], cinfo->image_width); } } - int processed = jpeg_write_raw_data(cinfo, is_width_aligned ? planes : planes_intrm, + int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes, kCompressBatchSize); if (processed != kCompressBatchSize / 2) { ALOGE("Number of processed lines does not equal input lines."); diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp index fdfbb9cec2..ae8184547a 100644 --- a/libs/ultrahdr/jpegr.cpp +++ b/libs/ultrahdr/jpegr.cpp @@ -185,21 +185,18 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe p010_image.chroma_stride = p010_image.luma_stride; } + const int yu420_luma_stride = ALIGNM(p010_image.width, kJpegBlock); unique_ptr<uint8_t[]> yuv420_image_data = - make_unique<uint8_t[]>(p010_image.width * p010_image.height * 3 / 2); + make_unique<uint8_t[]>(yu420_luma_stride * p010_image.height * 3 / 2); jpegr_uncompressed_struct yuv420_image = {.data = yuv420_image_data.get(), .width = p010_image.width, .height = p010_image.height, .colorGamut = p010_image.colorGamut, - .luma_stride = 0, + .luma_stride = yu420_luma_stride, .chroma_data = nullptr, - .chroma_stride = 0}; - if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width; - if (!yuv420_image.chroma_data) { - uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data); - yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height; - yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1; - } + .chroma_stride = yu420_luma_stride >> 1}; + uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data); + yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height; // tone map JPEGR_CHECK(toneMap(&p010_image, &yuv420_image)); @@ -230,7 +227,10 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfe // compress 420 image JpegEncoderHelper jpeg_enc_obj_yuv420; - if (!jpeg_enc_obj_yuv420.compressImage(yuv420_image.data, yuv420_image.width, yuv420_image.height, + if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_image.data), + reinterpret_cast<uint8_t*>(yuv420_image.chroma_data), + yuv420_image.width, yuv420_image.height, + yuv420_image.luma_stride, yuv420_image.chroma_stride, quality, icc->getData(), icc->getLength())) { return ERROR_JPEGR_ENCODE_ERROR; } @@ -305,13 +305,15 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, unique_ptr<uint8_t[]> yuv_420_bt601_data; // Convert to bt601 YUV encoding for JPEG encode if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) { - yuv_420_bt601_data = make_unique<uint8_t[]>(yuv420_image.width * yuv420_image.height * 3 / 2); + const int yuv_420_bt601_luma_stride = ALIGNM(yuv420_image.width, kJpegBlock); + yuv_420_bt601_data = + make_unique<uint8_t[]>(yuv_420_bt601_luma_stride * yuv420_image.height * 3 / 2); yuv420_bt601_image.data = yuv_420_bt601_data.get(); yuv420_bt601_image.colorGamut = yuv420_image.colorGamut; - yuv420_bt601_image.luma_stride = yuv420_image.width; + yuv420_bt601_image.luma_stride = yuv_420_bt601_luma_stride; uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_bt601_image.data); - yuv420_bt601_image.chroma_data = data + yuv420_bt601_image.luma_stride * yuv420_image.height; - yuv420_bt601_image.chroma_stride = yuv420_bt601_image.luma_stride >> 1; + yuv420_bt601_image.chroma_data = data + yuv_420_bt601_luma_stride * yuv420_image.height; + yuv420_bt601_image.chroma_stride = yuv_420_bt601_luma_stride >> 1; { // copy luma @@ -322,6 +324,10 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, } else { for (size_t i = 0; i < yuv420_image.height; i++) { memcpy(y_dst, y_src, yuv420_image.width); + if (yuv420_image.width != yuv420_bt601_image.luma_stride) { + memset(y_dst + yuv420_image.width, 0, + yuv420_bt601_image.luma_stride - yuv420_image.width); + } y_dst += yuv420_bt601_image.luma_stride; y_src += yuv420_image.luma_stride; } @@ -342,6 +348,12 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, for (size_t i = 0; i < yuv420_image.height / 2; i++) { memcpy(cb_dst, cb_src, yuv420_image.width / 2); memcpy(cr_dst, cr_src, yuv420_image.width / 2); + if (yuv420_bt601_image.width / 2 != yuv420_bt601_image.chroma_stride) { + memset(cb_dst + yuv420_image.width / 2, 0, + yuv420_bt601_image.chroma_stride - yuv420_image.width / 2); + memset(cr_dst + yuv420_image.width / 2, 0, + yuv420_bt601_image.chroma_stride - yuv420_image.width / 2); + } cb_dst += yuv420_bt601_image.chroma_stride; cb_src += yuv420_image.chroma_stride; cr_dst += yuv420_bt601_image.chroma_stride; @@ -353,8 +365,11 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, // compress 420 image JpegEncoderHelper jpeg_enc_obj_yuv420; - if (!jpeg_enc_obj_yuv420.compressImage(yuv420_bt601_image.data, yuv420_bt601_image.width, - yuv420_bt601_image.height, quality, icc->getData(), + if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_bt601_image.data), + reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data), + yuv420_bt601_image.width, yuv420_bt601_image.height, + yuv420_bt601_image.luma_stride, + yuv420_bt601_image.chroma_stride, quality, icc->getData(), icc->getLength())) { return ERROR_JPEGR_ENCODE_ERROR; } @@ -697,9 +712,10 @@ status_t JpegR::compressGainMap(jr_uncompressed_ptr gainmap_image_ptr, } // Don't need to convert YUV to Bt601 since single channel - if (!jpeg_enc_obj_ptr->compressImage(gainmap_image_ptr->data, gainmap_image_ptr->width, - gainmap_image_ptr->height, kMapCompressQuality, nullptr, 0, - true /* isSingleChannel */)) { + if (!jpeg_enc_obj_ptr->compressImage(reinterpret_cast<uint8_t*>(gainmap_image_ptr->data), nullptr, + gainmap_image_ptr->width, gainmap_image_ptr->height, + gainmap_image_ptr->luma_stride, 0, kMapCompressQuality, + nullptr, 0)) { return ERROR_JPEGR_ENCODE_ERROR; } @@ -769,7 +785,9 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr, ultrahdr_transfer_function hdr_tf, ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest, bool sdr_is_601) { if (yuv420_image_ptr == nullptr || p010_image_ptr == nullptr || metadata == nullptr || - dest == nullptr) { + dest == nullptr || yuv420_image_ptr->data == nullptr || + yuv420_image_ptr->chroma_data == nullptr || p010_image_ptr->data == nullptr || + p010_image_ptr->chroma_data == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } if (yuv420_image_ptr->width != p010_image_ptr->width || @@ -783,10 +801,8 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr, size_t image_width = yuv420_image_ptr->width; size_t image_height = yuv420_image_ptr->height; - size_t map_width = static_cast<size_t>( - floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor)); - size_t map_height = static_cast<size_t>( - floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor)); + size_t map_width = image_width / kMapDimensionScaleFactor; + size_t map_height = image_height / kMapDimensionScaleFactor; dest->data = new uint8_t[map_width * map_height]; dest->width = map_width; @@ -940,7 +956,8 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, ultrahdr_output_format output_format, float max_display_boost, jr_uncompressed_ptr dest) { if (yuv420_image_ptr == nullptr || gainmap_image_ptr == nullptr || metadata == nullptr || - dest == nullptr) { + dest == nullptr || yuv420_image_ptr->data == nullptr || + yuv420_image_ptr->chroma_data == nullptr || gainmap_image_ptr->data == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } if (metadata->version.compare(kJpegrVersion)) { @@ -965,12 +982,12 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr, // TODO: remove once map scaling factor is computed based on actual map dims size_t image_width = yuv420_image_ptr->width; size_t image_height = yuv420_image_ptr->height; - size_t map_width = static_cast<size_t>( - floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor)); - size_t map_height = static_cast<size_t>( - floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor)); + size_t map_width = image_width / kMapDimensionScaleFactor; + size_t map_height = image_height / kMapDimensionScaleFactor; if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) { - ALOGE("gain map dimensions and primary image dimensions are not to scale"); + ALOGE("gain map dimensions and primary image dimensions are not to scale, computed gain map " + "resolution is %dx%d, received gain map resolution is %dx%d", + (int)map_width, (int)map_height, gainmap_image_ptr->width, gainmap_image_ptr->height); return ERROR_JPEGR_INVALID_INPUT_TYPE; } @@ -1314,27 +1331,35 @@ status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) { return ERROR_JPEGR_INVALID_INPUT_TYPE; } uint16_t* src_y_data = reinterpret_cast<uint16_t*>(src->data); - uint16_t* src_uv_data = reinterpret_cast<uint16_t*>(src->chroma_data); uint8_t* dst_y_data = reinterpret_cast<uint8_t*>(dest->data); - uint8_t* dst_u_data = reinterpret_cast<uint8_t*>(dest->chroma_data); - size_t v_offset = (dest->chroma_stride * dest->height / 2); - uint8_t* dst_v_data = dst_u_data + v_offset; for (size_t y = 0; y < src->height; ++y) { + uint16_t* src_y_row = src_y_data + y * src->luma_stride; + uint8_t* dst_y_row = dst_y_data + y * dest->luma_stride; for (size_t x = 0; x < src->width; ++x) { - size_t src_y_idx = y * src->luma_stride + x; - size_t src_u_idx = (y >> 1) * src->chroma_stride + (x & ~0x1); - size_t src_v_idx = src_u_idx + 1; - - uint16_t y_uint = src_y_data[src_y_idx] >> 6; - uint16_t u_uint = src_uv_data[src_u_idx] >> 6; - uint16_t v_uint = src_uv_data[src_v_idx] >> 6; - - size_t dest_y_idx = x + y * dest->luma_stride; - size_t dest_chroma_idx = (x / 2) + (y / 2) * (dest->chroma_stride); - - dst_y_data[dest_y_idx] = static_cast<uint8_t>((y_uint >> 2) & 0xff); - dst_u_data[dest_chroma_idx] = static_cast<uint8_t>((u_uint >> 2) & 0xff); - dst_v_data[dest_chroma_idx] = static_cast<uint8_t>((v_uint >> 2) & 0xff); + uint16_t y_uint = src_y_row[x] >> 6; + dst_y_row[x] = static_cast<uint8_t>((y_uint >> 2) & 0xff); + } + if (dest->width != dest->luma_stride) { + memset(dst_y_row + dest->width, 0, dest->luma_stride - dest->width); + } + } + uint16_t* src_uv_data = reinterpret_cast<uint16_t*>(src->chroma_data); + uint8_t* dst_u_data = reinterpret_cast<uint8_t*>(dest->chroma_data); + size_t dst_v_offset = (dest->chroma_stride * dest->height / 2); + uint8_t* dst_v_data = dst_u_data + dst_v_offset; + for (size_t y = 0; y < src->height / 2; ++y) { + uint16_t* src_uv_row = src_uv_data + y * src->chroma_stride; + uint8_t* dst_u_row = dst_u_data + y * dest->chroma_stride; + uint8_t* dst_v_row = dst_v_data + y * dest->chroma_stride; + for (size_t x = 0; x < src->width / 2; ++x) { + uint16_t u_uint = src_uv_row[x << 1] >> 6; + uint16_t v_uint = src_uv_row[(x << 1) + 1] >> 6; + dst_u_row[x] = static_cast<uint8_t>((u_uint >> 2) & 0xff); + dst_v_row[x] = static_cast<uint8_t>((v_uint >> 2) & 0xff); + } + if (dest->width / 2 != dest->chroma_stride) { + memset(dst_u_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2); + memset(dst_v_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2); } } dest->colorGamut = src->colorGamut; diff --git a/libs/ultrahdr/tests/gainmapmath_test.cpp b/libs/ultrahdr/tests/gainmapmath_test.cpp index 69cd36cd46..7c2d076992 100644 --- a/libs/ultrahdr/tests/gainmapmath_test.cpp +++ b/libs/ultrahdr/tests/gainmapmath_test.cpp @@ -120,7 +120,7 @@ public: 0xB0, 0xB1, 0xB2, 0xB3, }; - return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 }; + return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2 }; } Color (*Yuv420Colors())[4] { @@ -153,7 +153,7 @@ public: 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6, 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6, }; - return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709 }; + return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4 }; } Color (*P010Colors())[4] { @@ -625,7 +625,7 @@ TEST_F(GainMapMathTest, Bt2100ToBt601YuvConversion) { EXPECT_YUV_NEAR(yuv2100To601(yuv_b), P3YuvBlue()); } -TEST_F(GainMapMathTest, DISABLED_TransformYuv420) { +TEST_F(GainMapMathTest, TransformYuv420) { ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100, yuv2100To709, yuv2100To601 }; for (const ColorTransformFn& transform : transforms) { @@ -636,6 +636,9 @@ TEST_F(GainMapMathTest, DISABLED_TransformYuv420) { memcpy(out_buf.get(), input.data, out_buf_size); jpegr_uncompressed_struct output = Yuv420Image(); output.data = out_buf.get(); + output.chroma_data = out_buf.get() + input.width * input.height; + output.luma_stride = input.width; + output.chroma_stride = input.width / 2; transformYuv420(&output, 1, 1, transform); @@ -1042,7 +1045,7 @@ TEST_F(GainMapMathTest, ApplyGain) { applyGain(e, 1.0f, &metadata, displayBoost)); } -TEST_F(GainMapMathTest, DISABLED_GetYuv420Pixel) { +TEST_F(GainMapMathTest, GetYuv420Pixel) { jpegr_uncompressed_struct image = Yuv420Image(); Color (*colors)[4] = Yuv420Colors(); @@ -1053,7 +1056,7 @@ TEST_F(GainMapMathTest, DISABLED_GetYuv420Pixel) { } } -TEST_F(GainMapMathTest, DISABLED_GetP010Pixel) { +TEST_F(GainMapMathTest, GetP010Pixel) { jpegr_uncompressed_struct image = P010Image(); Color (*colors)[4] = P010Colors(); @@ -1064,7 +1067,7 @@ TEST_F(GainMapMathTest, DISABLED_GetP010Pixel) { } } -TEST_F(GainMapMathTest, DISABLED_SampleYuv420) { +TEST_F(GainMapMathTest, SampleYuv420) { jpegr_uncompressed_struct image = Yuv420Image(); Color (*colors)[4] = Yuv420Colors(); @@ -1090,7 +1093,7 @@ TEST_F(GainMapMathTest, DISABLED_SampleYuv420) { } } -TEST_F(GainMapMathTest, DISABLED_SampleP010) { +TEST_F(GainMapMathTest, SampleP010) { jpegr_uncompressed_struct image = P010Image(); Color (*colors)[4] = P010Colors(); diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp index f0e1fa4968..33cb9f658f 100644 --- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp +++ b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp @@ -42,6 +42,7 @@ public: }; JpegEncoderHelperTest(); ~JpegEncoderHelperTest(); + protected: virtual void SetUp(); virtual void TearDown(); @@ -103,24 +104,32 @@ void JpegEncoderHelperTest::TearDown() {} TEST_F(JpegEncoderHelperTest, encodeAlignedImage) { JpegEncoderHelper encoder; - EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(), mAlignedImage.width, - mAlignedImage.height, JPEG_QUALITY, NULL, 0)); + EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(), + mAlignedImage.buffer.get() + + mAlignedImage.width * mAlignedImage.height, + mAlignedImage.width, mAlignedImage.height, + mAlignedImage.width, mAlignedImage.width / 2, JPEG_QUALITY, + NULL, 0)); ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); } TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) { JpegEncoderHelper encoder; - EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), mUnalignedImage.width, - mUnalignedImage.height, JPEG_QUALITY, NULL, 0)); + EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(), + mUnalignedImage.buffer.get() + + mUnalignedImage.width * mUnalignedImage.height, + mUnalignedImage.width, mUnalignedImage.height, + mUnalignedImage.width, mUnalignedImage.width / 2, + JPEG_QUALITY, NULL, 0)); ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); } TEST_F(JpegEncoderHelperTest, encodeSingleChannelImage) { JpegEncoderHelper encoder; - EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), mSingleChannelImage.width, - mSingleChannelImage.height, JPEG_QUALITY, NULL, 0, true)); + EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), nullptr, + mSingleChannelImage.width, mSingleChannelImage.height, + mSingleChannelImage.width, 0, JPEG_QUALITY, NULL, 0)); ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0)); } -} // namespace android::ultrahdr - +} // namespace android::ultrahdr diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp index e69c50964a..a75086755a 100644 --- a/libs/ultrahdr/tests/jpegr_test.cpp +++ b/libs/ultrahdr/tests/jpegr_test.cpp @@ -1375,11 +1375,21 @@ TEST(JpegRTest, writeXmpThenRead) { EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax); } +class JpegRAPIEncodeAndDecodeTest + : public ::testing::TestWithParam<std::tuple<ultrahdr_color_gamut, ultrahdr_color_gamut>> { +public: + JpegRAPIEncodeAndDecodeTest() + : mP010ColorGamut(std::get<0>(GetParam())), mYuv420ColorGamut(std::get<1>(GetParam())){}; + + const ultrahdr_color_gamut mP010ColorGamut; + const ultrahdr_color_gamut mYuv420ColorGamut; +}; + /* Test Encode API-0 and Decode */ -TEST(JpegRTest, EncodeAPI0AndDecodeTest) { +TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) { // reference encode UhdrUnCompressedStructWrapper rawImg(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg.allocateMemory()); ASSERT_TRUE(rawImg.loadRawResource(kYCbCrP010FileName)); UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight); @@ -1392,8 +1402,8 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) { // encode with luma stride set { UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); - ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, 0)); + ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut)); + ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, 0)); ASSERT_TRUE(rawImg2.allocateMemory()); ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName)); UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight); @@ -1410,8 +1420,8 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) { // encode with luma and chroma stride set { UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); - ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 128, kImageWidth + 256)); + ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut)); + ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, kImageWidth + 28)); ASSERT_TRUE(rawImg2.setChromaMode(false)); ASSERT_TRUE(rawImg2.allocateMemory()); ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName)); @@ -1429,8 +1439,8 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) { // encode with chroma stride set { UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); - ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 64)); + ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut)); + ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 34)); ASSERT_TRUE(rawImg2.setChromaMode(false)); ASSERT_TRUE(rawImg2.allocateMemory()); ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName)); @@ -1448,8 +1458,8 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) { // encode with luma and chroma stride set but no chroma ptr { UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); - ASSERT_TRUE(rawImg2.setImageStride(kImageWidth, kImageWidth + 256)); + ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut)); + ASSERT_TRUE(rawImg2.setImageStride(kImageWidth, kImageWidth + 38)); ASSERT_TRUE(rawImg2.allocateMemory()); ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName)); UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight); @@ -1475,13 +1485,13 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) { } /* Test Encode API-1 and Decode */ -TEST(JpegRTest, EncodeAPI1AndDecodeTest) { +TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) { UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImgP010.allocateMemory()); ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName)); UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); + ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut)); ASSERT_TRUE(rawImg420.allocateMemory()); ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName)); UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight); @@ -1494,7 +1504,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma stride set p010 { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0)); ASSERT_TRUE(rawImg2P010.allocateMemory()); ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName)); @@ -1512,7 +1522,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma and chroma stride set p010 { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1531,7 +1541,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with chroma stride set p010 { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1550,7 +1560,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma and chroma stride set but no chroma ptr p010 { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 64, kImageWidth + 256)); ASSERT_TRUE(rawImg2P010.allocateMemory()); ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName)); @@ -1568,8 +1578,8 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma stride set 420 { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); - ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); + ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 14, 0)); ASSERT_TRUE(rawImg2420.allocateMemory()); ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName)); UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight); @@ -1586,8 +1596,8 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma and chroma stride set 420 { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); - ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth / 2 + 256)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); + ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 46, kImageWidth / 2 + 34)); ASSERT_TRUE(rawImg2420.setChromaMode(false)); ASSERT_TRUE(rawImg2420.allocateMemory()); ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName)); @@ -1605,8 +1615,8 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with chroma stride set 420 { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); - ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth / 2 + 64)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); + ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth / 2 + 38)); ASSERT_TRUE(rawImg2420.setChromaMode(false)); ASSERT_TRUE(rawImg2420.allocateMemory()); ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName)); @@ -1624,8 +1634,8 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { // encode with luma and chroma stride set but no chroma ptr 420 { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); - ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth / 2 + 64)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); + ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 26, kImageWidth / 2 + 44)); ASSERT_TRUE(rawImg2420.allocateMemory()); ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName)); UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight); @@ -1652,13 +1662,13 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) { } /* Test Encode API-2 and Decode */ -TEST(JpegRTest, EncodeAPI2AndDecodeTest) { +TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) { UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImgP010.allocateMemory()); ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName)); UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); + ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut)); ASSERT_TRUE(rawImg420.allocateMemory()); ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName)); UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight); @@ -1675,7 +1685,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with luma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0)); ASSERT_TRUE(rawImg2P010.allocateMemory()); ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName)); @@ -1693,7 +1703,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with luma and chroma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1712,7 +1722,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with chroma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1731,7 +1741,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with luma stride set { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0)); ASSERT_TRUE(rawImg2420.allocateMemory()); ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName)); @@ -1749,7 +1759,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with luma and chroma stride set { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth + 256)); ASSERT_TRUE(rawImg2420.setChromaMode(false)); ASSERT_TRUE(rawImg2420.allocateMemory()); @@ -1768,7 +1778,7 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { // encode with chroma stride set { UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420); - ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709)); + ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut)); ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth + 64)); ASSERT_TRUE(rawImg2420.setChromaMode(false)); ASSERT_TRUE(rawImg2420.allocateMemory()); @@ -1797,9 +1807,9 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) { } /* Test Encode API-3 and Decode */ -TEST(JpegRTest, EncodeAPI3AndDecodeTest) { +TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) { UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImgP010.allocateMemory()); ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName)); UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight); @@ -1816,7 +1826,7 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) { // encode with luma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0)); ASSERT_TRUE(rawImg2P010.allocateMemory()); ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName)); @@ -1834,7 +1844,7 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) { // encode with luma and chroma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1853,7 +1863,7 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) { // encode with chroma stride set { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64)); ASSERT_TRUE(rawImg2P010.setChromaMode(false)); ASSERT_TRUE(rawImg2P010.allocateMemory()); @@ -1872,7 +1882,7 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) { // encode with luma and chroma stride set and no chroma ptr { UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010); - ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); + ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut)); ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 32, kImageWidth + 256)); ASSERT_TRUE(rawImg2P010.allocateMemory()); ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName)); @@ -1899,6 +1909,13 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) { ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api3_output.rgb")); } +INSTANTIATE_TEST_SUITE_P( + JpegRAPIParameterizedTests, JpegRAPIEncodeAndDecodeTest, + ::testing::Combine(::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3, + ULTRAHDR_COLORGAMUT_BT2100), + ::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3, + ULTRAHDR_COLORGAMUT_BT2100))); + // ============================================================================ // Profiling // ============================================================================ @@ -1966,7 +1983,7 @@ void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_u profileRecMap.elapsedTime() / (kProfileCount * 1000.f)); } -TEST(JpegRTest, DISABLED_ProfileGainMapFuncs) { +TEST(JpegRTest, ProfileGainMapFuncs) { UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010); ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100)); ASSERT_TRUE(rawImgP010.allocateMemory()); @@ -1980,6 +1997,25 @@ TEST(JpegRTest, DISABLED_ProfileGainMapFuncs) { .width = 0, .height = 0, .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED}; + { + auto rawImg = rawImgP010.getImageHandle(); + if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width; + if (!rawImg->chroma_data) { + uint16_t* data = reinterpret_cast<uint16_t*>(rawImg->data); + rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height; + rawImg->chroma_stride = rawImg->luma_stride; + } + } + { + auto rawImg = rawImg420.getImageHandle(); + if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width; + if (!rawImg->chroma_data) { + uint8_t* data = reinterpret_cast<uint8_t*>(rawImg->data); + rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height; + rawImg->chroma_stride = rawImg->luma_stride / 2; + } + } + JpegRBenchmark benchmark; ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(), rawImgP010.getImageHandle(), &metadata, diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 4a08c11c14..48d793a4d4 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -143,7 +143,7 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri ALOGV("shellCommand"); for (size_t i = 0, n = args.size(); i < n; i++) - ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string()); + ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).c_str()); if (args.size() >= 1) { if (args[0] == String16("vkjson")) return cmdVkjson(out, err); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 18f6dbc1e1..d5512132be 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -87,6 +87,7 @@ cc_defaults { srcs: [":libinputflinger_sources"], shared_libs: [ "android.hardware.input.processor-V1-ndk", + "com.android.server.inputflinger-ndk", "libbase", "libbinder", "libbinder_ndk", @@ -107,6 +108,14 @@ cc_defaults { "libpalmrejection", "libui-types", ], + generated_headers: [ + "cxx-bridge-header", + "inputflinger_rs_bootstrap_bridge_header", + ], + header_libs: ["inputflinger_rs_bootstrap_cxx_headers"], + generated_sources: ["inputflinger_rs_bootstrap_bridge_code"], + whole_static_libs: ["libinputflinger_rs"], + export_shared_lib_headers: ["com.android.server.inputflinger-ndk"], target: { android: { shared_libs: [ diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index c90356459f..da79ae3a48 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -23,22 +23,22 @@ #include "InputReaderFactory.h" #include "UnwantedInteractionBlocker.h" +#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> +#include <android/binder_interface_utils.h> #include <android/sysprop/InputProperties.sysprop.h> #include <binder/IPCThreadState.h> - +#include <inputflinger_bootstrap.rs.h> #include <log/log.h> -#include <unordered_map> - #include <private/android_filesystem_config.h> namespace android { -static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = - sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true); +namespace { -using gui::FocusRequest; +const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = + sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true); -static int32_t exceptionCodeFromStatusT(status_t status) { +int32_t exceptionCodeFromStatusT(status_t status) { switch (status) { case OK: return binder::Status::EX_NONE; @@ -57,6 +57,58 @@ static int32_t exceptionCodeFromStatusT(status_t status) { } } +// Convert a binder interface into a raw pointer to an AIBinder. +using IInputFlingerRustBootstrapCallback = aidl::com::android::server::inputflinger:: + IInputFlingerRust::IInputFlingerRustBootstrapCallback; +IInputFlingerRustBootstrapCallbackAIBinder* binderToPointer( + IInputFlingerRustBootstrapCallback& interface) { + ndk::SpAIBinder spAIBinder = interface.asBinder(); + auto* ptr = spAIBinder.get(); + AIBinder_incStrong(ptr); + return ptr; +} + +// Create the Rust component of InputFlinger that uses AIDL interfaces as a the foreign function +// interface (FFI). The bootstraping process for IInputFlingerRust is as follows: +// - Create BnInputFlingerRustBootstrapCallback in C++. +// - Use the cxxbridge ffi interface to call the Rust function `create_inputflinger_rust()`, and +// pass the callback binder object as a raw pointer. +// - The Rust implementation will create the implementation of IInputFlingerRust, and pass it +// to C++ through the callback. +// - After the Rust function returns, the binder interface provided to the callback will be the +// only strong reference to the IInputFlingerRust. +std::shared_ptr<IInputFlingerRust> createInputFlingerRust() { + using namespace aidl::com::android::server::inputflinger; + + class Callback : public IInputFlingerRust::BnInputFlingerRustBootstrapCallback { + ndk::ScopedAStatus onProvideInputFlingerRust( + const std::shared_ptr<IInputFlingerRust>& inputFlingerRust) override { + mService = inputFlingerRust; + return ndk::ScopedAStatus::ok(); + } + + public: + std::shared_ptr<IInputFlingerRust> consumeInputFlingerRust() { + auto service = mService; + mService.reset(); + return service; + } + + private: + std::shared_ptr<IInputFlingerRust> mService; + }; + + auto callback = ndk::SharedRefBase::make<Callback>(); + create_inputflinger_rust(binderToPointer(*callback)); + auto service = callback->consumeInputFlingerRust(); + LOG_ALWAYS_FATAL_IF(!service, + "create_inputflinger_rust did not provide the IInputFlingerRust " + "implementation through the callback."); + return service; +} + +} // namespace + /** * The event flow is via the "InputListener" interface, as follows: * InputReader @@ -67,6 +119,8 @@ static int32_t exceptionCodeFromStatusT(status_t status) { */ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy) { + mInputFlingerRust = createInputFlingerRust(); + mDispatcher = createInputDispatcher(dispatcherPolicy); if (ENABLE_INPUT_DEVICE_USAGE_METRICS) { @@ -190,7 +244,7 @@ status_t InputManager::dump(int fd, const Vector<String16>& args) { return NO_ERROR; } -binder::Status InputManager::setFocusedWindow(const FocusRequest& request) { +binder::Status InputManager::setFocusedWindow(const gui::FocusRequest& request) { mDispatcher->setFocusedWindow(request); return binder::Status::ok(); } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 9dc285f2c0..528d2aa5ca 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -30,6 +30,7 @@ #include <input/Input.h> #include <input/InputTransport.h> +#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <android/os/BnInputFlinger.h> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -37,6 +38,8 @@ using android::os::BnInputFlinger; +using aidl::com::android::server::inputflinger::IInputFlingerRust; + namespace android { class InputChannel; class InputDispatcherThread; @@ -132,6 +135,8 @@ private: std::unique_ptr<InputDeviceMetricsCollectorInterface> mCollector; std::unique_ptr<InputDispatcherInterface> mDispatcher; + + std::shared_ptr<IInputFlingerRust> mInputFlingerRust; }; } // namespace android diff --git a/services/inputflinger/aidl/Android.bp b/services/inputflinger/aidl/Android.bp new file mode 100644 index 0000000000..314c43356d --- /dev/null +++ b/services/inputflinger/aidl/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +aidl_interface { + name: "com.android.server.inputflinger", + srcs: ["**/*.aidl"], + unstable: true, + host_supported: true, + backend: { + cpp: { + enabled: false, + }, + java: { + enabled: false, + }, + rust: { + enabled: true, + }, + }, +} diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl new file mode 100644 index 0000000000..8e826fda44 --- /dev/null +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFlingerRust.aidl @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.inputflinger; + +/** + * A local AIDL interface used as a foreign function interface (ffi) to + * communicate with the Rust component of inputflinger. + * + * NOTE: Since we use this as a local interface, all processing happens on the + * calling thread. + */ +interface IInputFlingerRust { + + /** + * An interface used to get a strong reference to IInputFlingerRust on boot. + */ + interface IInputFlingerRustBootstrapCallback { + void onProvideInputFlingerRust(in IInputFlingerRust inputFlingerRust); + } +} diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index ec0388d500..de99fc73ec 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -256,14 +256,14 @@ input_property_t* InputDriver::inputGetDeviceProperty(input_property_map_t* map, const char* InputDriver::inputGetPropertyKey(input_property_t* property) { if (property != nullptr) { - return property->key.string(); + return property->key.c_str(); } return nullptr; } const char* InputDriver::inputGetPropertyValue(input_property_t* property) { if (property != nullptr) { - return property->value.string(); + return property->value.c_str(); } return nullptr; } @@ -281,7 +281,7 @@ void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) { } void InputDriver::dump(String8& result) { - result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.string()); + result.appendFormat(INDENT2 "HAL Input Driver (%s)\n", mName.c_str()); } } // namespace android diff --git a/services/inputflinger/host/InputFlinger.cpp b/services/inputflinger/host/InputFlinger.cpp index 2da2a70c03..d974c43d78 100644 --- a/services/inputflinger/host/InputFlinger.cpp +++ b/services/inputflinger/host/InputFlinger.cpp @@ -57,7 +57,7 @@ status_t InputFlinger::dump(int fd, const Vector<String16>& args) { } else { dumpInternal(result); } - write(fd, result.string(), result.size()); + write(fd, result.c_str(), result.size()); return OK; } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 08600b2db5..7f63355387 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -77,7 +77,7 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, : mContext(this), mEventHub(eventHub), mPolicy(policy), - mQueuedListener(listener), + mNextListener(listener), mGlobalMetaState(AMETA_NONE), mLedMetaState(AMETA_NONE), mGeneration(1), @@ -140,7 +140,7 @@ void InputReader::loopOnce() { mReaderIsAliveCondition.notify_all(); if (!events.empty()) { - notifyArgs += processEventsLocked(events.data(), events.size()); + mPendingArgs += processEventsLocked(events.data(), events.size()); } if (mNextTimeout != LLONG_MAX) { @@ -150,16 +150,18 @@ void InputReader::loopOnce() { ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); } mNextTimeout = LLONG_MAX; - notifyArgs += timeoutExpiredLocked(now); + mPendingArgs += timeoutExpiredLocked(now); } } if (oldGeneration != mGeneration) { inputDevicesChanged = true; inputDevices = getInputDevicesLocked(); - notifyArgs.emplace_back( + mPendingArgs.emplace_back( NotifyInputDevicesChangedArgs{mContext.getNextId(), inputDevices}); } + + std::swap(notifyArgs, mPendingArgs); } // release lock // Send out a message that the describes the changed input devices. @@ -175,8 +177,6 @@ void InputReader::loopOnce() { } } - notifyAll(std::move(notifyArgs)); - // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -184,7 +184,9 @@ void InputReader::loopOnce() { // resulting in a deadlock. This situation is actually quite plausible because the // listener is actually the input dispatcher, which calls into the window manager, // which occasionally calls into the input reader. - mQueuedListener.flush(); + for (const NotifyArgs& args : notifyArgs) { + mNextListener.notify(args); + } } std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { @@ -236,8 +238,8 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId); std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier); - notifyAll(device->configure(when, mConfig, /*changes=*/{})); - notifyAll(device->reset(when)); + mPendingArgs += device->configure(when, mConfig, /*changes=*/{}); + mPendingArgs += device->reset(when); if (device->isIgnored()) { ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' " @@ -310,12 +312,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { notifyExternalStylusPresenceChangedLocked(); } - std::list<NotifyArgs> resetEvents; if (device->hasEventHubDevices()) { - resetEvents += device->configure(when, mConfig, /*changes=*/{}); + mPendingArgs += device->configure(when, mConfig, /*changes=*/{}); } - resetEvents += device->reset(when); - notifyAll(std::move(resetEvents)); + mPendingArgs += device->reset(when); } std::shared_ptr<InputDevice> InputReader::createDeviceLocked( @@ -387,7 +387,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { updateGlobalMetaStateLocked(); // Enqueue configuration changed. - mQueuedListener.notifyConfigurationChanged({mContext.getNextId(), when}); + mPendingArgs.emplace_back(NotifyConfigurationChangedArgs{mContext.getNextId(), when}); } void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) { @@ -409,7 +409,7 @@ void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) { } else { for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; - notifyAll(device->configure(now, mConfig, changes)); + mPendingArgs += device->configure(now, mConfig, changes); } } @@ -419,18 +419,13 @@ void InputReader::refreshConfigurationLocked(ConfigurationChanges changes) { "There was no change in the pointer capture state."); } else { mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest; - mQueuedListener.notifyPointerCaptureChanged( - {mContext.getNextId(), now, mCurrentPointerCaptureRequest}); + mPendingArgs.emplace_back( + NotifyPointerCaptureChangedArgs{mContext.getNextId(), now, + mCurrentPointerCaptureRequest}); } } } -void InputReader::notifyAll(std::list<NotifyArgs>&& argsList) { - for (const NotifyArgs& args : argsList) { - mQueuedListener.notify(args); - } -} - void InputReader::updateGlobalMetaStateLocked() { mGlobalMetaState = 0; @@ -690,7 +685,7 @@ void InputReader::vibrate(int32_t deviceId, const VibrationSequence& sequence, s InputDevice* device = findInputDeviceLocked(deviceId); if (device) { - notifyAll(device->vibrate(sequence, repeat, token)); + mPendingArgs += device->vibrate(sequence, repeat, token); } } @@ -699,7 +694,7 @@ void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { InputDevice* device = findInputDeviceLocked(deviceId); if (device) { - notifyAll(device->cancelVibrate(token)); + mPendingArgs += device->cancelVibrate(token); } } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 01ec7c1303..e21715eb27 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -174,7 +174,14 @@ private: // in parallel to passing it to the InputReader. std::shared_ptr<EventHubInterface> mEventHub; sp<InputReaderPolicyInterface> mPolicy; - QueuedInputListener mQueuedListener; + + // The next stage that should receive the events generated inside InputReader. + InputListenerInterface& mNextListener; + // As various events are generated inside InputReader, they are stored inside this list. The + // list can only be accessed with the lock, so the events inside it are well-ordered. + // Once the reader is done working, these events will be swapped into a temporary storage and + // sent to the 'mNextListener' without holding the lock. + std::list<NotifyArgs> mPendingArgs GUARDED_BY(mLock); InputReaderConfiguration mConfig GUARDED_BY(mLock); @@ -242,8 +249,6 @@ private: ConfigurationChanges mConfigurationChangesToRefresh GUARDED_BY(mLock); void refreshConfigurationLocked(ConfigurationChanges changes) REQUIRES(mLock); - void notifyAll(std::list<NotifyArgs>&& argsList); - PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock); // state queries diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp new file mode 100644 index 0000000000..23c1691b28 --- /dev/null +++ b/services/inputflinger/rust/Android.bp @@ -0,0 +1,52 @@ +// Copyright 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generate the C++ code that Rust calls into. +genrule { + name: "inputflinger_rs_bootstrap_bridge_code", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) >> $(out)", + srcs: ["lib.rs"], + out: ["inputflinger_rs_bootstrap_cxx_generated.cc"], +} + +// Generate a C++ header containing the C++ bindings +// to the Rust exported functions in lib.rs. +genrule { + name: "inputflinger_rs_bootstrap_bridge_header", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) --header >> $(out)", + srcs: ["lib.rs"], + out: ["inputflinger_bootstrap.rs.h"], +} + +rust_ffi_static { + name: "libinputflinger_rs", + crate_name: "inputflinger", + srcs: ["lib.rs"], + rustlibs: [ + "libcxx", + "com.android.server.inputflinger-rust", + "libbinder_rs", + "liblog_rust", + "liblogger", + ], + host_supported: true, +} + +cc_library_headers { + name: "inputflinger_rs_bootstrap_cxx_headers", + host_supported: true, + export_include_dirs: ["ffi"], +} diff --git a/libs/renderengine/include/renderengine/Image.h b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h index 3bb47318ef..eb79ab50da 100644 --- a/libs/renderengine/include/renderengine/Image.h +++ b/services/inputflinger/rust/ffi/InputFlingerBootstrap.h @@ -1,5 +1,5 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,6 @@ #pragma once -struct ANativeWindowBuffer; +#include <android/binder_parcel.h> -namespace android { -namespace renderengine { - -class Image { -public: - virtual ~Image() = default; - virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; -}; - -} // namespace renderengine -} // namespace android +using IInputFlingerRustBootstrapCallbackAIBinder = AIBinder; diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs new file mode 100644 index 0000000000..501e4359cd --- /dev/null +++ b/services/inputflinger/rust/lib.rs @@ -0,0 +1,102 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! # The rust component of InputFlinger +//! +//! We use cxxbridge to create IInputFlingerRust - the Rust component of inputflinger - and +//! pass it back to C++ as a local AIDL interface. + +use binder::{ + unstable_api::{AIBinder, new_spibinder,}, + BinderFeatures, Interface, StatusCode, Strong, +}; +use com_android_server_inputflinger::aidl::com::android::server::inputflinger::IInputFlingerRust::{ + BnInputFlingerRust, IInputFlingerRust, + IInputFlingerRustBootstrapCallback::IInputFlingerRustBootstrapCallback, +}; +use log::debug; + +const LOG_TAG: &str = "inputflinger_bootstrap"; + +#[cxx::bridge] +#[allow(unsafe_op_in_unsafe_fn)] +mod ffi { + extern "C++" { + include!("InputFlingerBootstrap.h"); + type IInputFlingerRustBootstrapCallbackAIBinder; + } + + extern "Rust" { + unsafe fn create_inputflinger_rust( + callback: *mut IInputFlingerRustBootstrapCallbackAIBinder, + ); + } +} + +/// Create the IInputFlingerRust implementation. +/// This is the singular entry point from C++ into Rust. +/// The `callback` parameter must be a valid pointer to an AIBinder implementation of +/// the `IInputFlingerRustBootstrapCallback` interface. The IInputFlingerRust implementation that +/// is created will be passed back through the callback from within this function. +/// NOTE: This function must not hold a strong reference to the callback beyond its scope. +/// +/// # Safety +/// +/// This function is safe iff `callback` is a valid pointer to an `AIBinder` interface of type +/// `IInputFlingerRustBootstrapCallback`. The pointer must have had its reference count manually +/// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`. +unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) { + logger::init( + logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace), + ); + + let callback = callback as *mut AIBinder; + if callback.is_null() { + panic!("create_inputflinger_rust cannot be called with a null callback"); + } + + // SAFETY: Our caller guaranteed that `callback` is a valid pointer to an `AIBinder` and its + // reference count has been incremented.. + let Some(callback) = (unsafe { new_spibinder(callback) }) else { + panic!("Failed to get SpAIBinder from raw callback pointer"); + }; + + let callback: Result<Strong<dyn IInputFlingerRustBootstrapCallback>, StatusCode> = + callback.into_interface(); + match callback { + Ok(callback) => { + debug!("Creating InputFlingerRust"); + let service = + BnInputFlingerRust::new_binder(InputFlingerRust {}, BinderFeatures::default()); + callback.onProvideInputFlingerRust(&service).unwrap(); + } + Err(status) => { + panic!("Failed to convert AIBinder into the callback interface: {}", status); + } + } +} + +struct InputFlingerRust {} + +impl Interface for InputFlingerRust {} + +impl IInputFlingerRust for InputFlingerRust {} + +impl Drop for InputFlingerRust { + fn drop(&mut self) { + debug!("Destroying InputFlingerRust"); + } +} diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 78420c0d67..41c98ef9e9 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -158,7 +158,8 @@ const InputReaderConfiguration& FakeInputReaderPolicy::getReaderConfiguration() return mConfig; } -const std::vector<InputDeviceInfo>& FakeInputReaderPolicy::getInputDevices() const { +const std::vector<InputDeviceInfo> FakeInputReaderPolicy::getInputDevices() const { + std::scoped_lock lock(mLock); return mInputDevices; } @@ -228,7 +229,7 @@ std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointer void FakeInputReaderPolicy::notifyInputDevicesChanged( const std::vector<InputDeviceInfo>& inputDevices) { - std::scoped_lock<std::mutex> lock(mLock); + std::scoped_lock lock(mLock); mInputDevices = inputDevices; mInputDevicesChanged = true; mDevicesChangedCondition.notify_all(); @@ -256,7 +257,7 @@ void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> proces } void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) { - std::scoped_lock<std::mutex> lock(mLock); + std::scoped_lock lock(mLock); mStylusGestureNotified = deviceId; } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index e03d28d0ac..48912a6a28 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -64,7 +64,7 @@ public: void removeDisabledDevice(int32_t deviceId); void setPointerController(std::shared_ptr<FakePointerController> controller); const InputReaderConfiguration& getReaderConfiguration() const; - const std::vector<InputDeviceInfo>& getInputDevices() const; + const std::vector<InputDeviceInfo> getInputDevices() const; TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation); void setTouchAffineTransformation(const TouchAffineTransformation t); @@ -91,7 +91,7 @@ private: void waitForInputDevices(std::function<void(bool)> processDevicesChanged); void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override; - std::mutex mLock; + mutable std::mutex mLock; std::condition_variable mDevicesChangedCondition; InputReaderConfiguration mConfig; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 477beaf7f6..72fe2af033 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1343,19 +1343,8 @@ protected: mFakePolicy = sp<FakeInputReaderPolicy>::make(); mFakePointerController = std::make_shared<FakePointerController>(); mFakePolicy->setPointerController(mFakePointerController); - mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms, - /*eventDidNotHappenTimeout=*/30ms); - mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy, - *mTestListener); - ASSERT_EQ(mReader->start(), OK); - - // Since this test is run on a real device, all the input devices connected - // to the test device will show up in mReader. We wait for those input devices to - // show up before beginning the tests. - ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled()); - ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + setupInputReader(); } void TearDown() override { @@ -1376,6 +1365,22 @@ protected: }); return it != inputDevices.end() ? std::make_optional(*it) : std::nullopt; } + + void setupInputReader() { + mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms, + /*eventDidNotHappenTimeout=*/30ms); + + mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy, + *mTestListener); + ASSERT_EQ(mReader->start(), OK); + + // Since this test is run on a real device, all the input devices connected + // to the test device will show up in mReader. We wait for those input devices to + // show up before beginning the tests. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyInputDevicesChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + } }; TEST_F(InputReaderIntegrationTest, TestInvalidDevice) { @@ -1509,7 +1514,7 @@ TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) { // --- TouchIntegrationTest --- -class TouchIntegrationTest : public InputReaderIntegrationTest { +class BaseTouchIntegrationTest : public InputReaderIntegrationTest { protected: const std::string UNIQUE_ID = "local:0"; @@ -1554,7 +1559,55 @@ protected: InputDeviceInfo mDeviceInfo; }; -TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) { +enum class TouchIntegrationTestDisplays { DISPLAY_INTERNAL, DISPLAY_INPUT_PORT, DISPLAY_UNIQUE_ID }; + +class TouchIntegrationTest : public BaseTouchIntegrationTest, + public testing::WithParamInterface<TouchIntegrationTestDisplays> { +protected: + static constexpr std::optional<uint8_t> DISPLAY_PORT = 0; + const std::string INPUT_PORT = "uinput_touch/input0"; + + void SetUp() override { +#if !defined(__ANDROID__) + GTEST_SKIP(); +#endif + if (GetParam() == TouchIntegrationTestDisplays::DISPLAY_INTERNAL) { + BaseTouchIntegrationTest::SetUp(); + return; + } + + // setup policy with a input-port or UniqueId association to the display + bool isInputPortAssociation = + GetParam() == TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT; + + mFakePolicy = sp<FakeInputReaderPolicy>::make(); + if (isInputPortAssociation) { + mFakePolicy->addInputPortAssociation(INPUT_PORT, DISPLAY_PORT.value()); + } else { + mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID); + } + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + + InputReaderIntegrationTest::setupInputReader(); + + mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT), + INPUT_PORT); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + + // Add a display linked to a physical port or UniqueId. + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + UNIQUE_ID, isInputPortAssociation ? DISPLAY_PORT : NO_PORT, + ViewportType::INTERNAL); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto info = findDeviceByName(mDevice->getName()); + ASSERT_TRUE(info); + mDeviceInfo = *info; + } +}; + +TEST_P(TouchIntegrationTest, MultiTouchDeviceSource) { // The UinputTouchScreen is an MT device that supports MT_TOOL_TYPE and also supports stylus // buttons. It should show up as a touchscreen, stylus, and keyboard (for reporting button // presses). @@ -1562,7 +1615,7 @@ TEST_F(TouchIntegrationTest, MultiTouchDeviceSource) { mDeviceInfo.getSources()); } -TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { +TEST_P(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1586,7 +1639,7 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) { ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); } -TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) { +TEST_P(TouchIntegrationTest, InputEvent_ProcessMultiTouch) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1642,7 +1695,7 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) { * palms, and wants to cancel Pointer 1, then it is safe to simply drop POINTER_1_UP event without * losing information about non-palm pointers. */ -TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) { +TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1685,7 +1738,7 @@ TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerUp) { * In this scenario, the movement of the second pointer just prior to liftoff is ignored, and never * gets sent to the listener. */ -TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) { +TEST_P(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1725,7 +1778,7 @@ TEST_F(TouchIntegrationTest, MultiTouch_PointerMoveAndSecondPointerMoveAndUp) { assertReceivedMotion(AMOTION_EVENT_ACTION_MOVE, {centerPoint + Point(5, 5)}); } -TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { +TEST_P(TouchIntegrationTest, InputEvent_ProcessPalm) { NotifyMotionArgs args; const Point centerPoint = mDevice->getCenterPoint(); @@ -1776,7 +1829,39 @@ TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) { ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); } -TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { +/** + * Some drivers historically have reported axis values outside of the range specified in the + * evdev axis info. Ensure we don't crash when this happens. For example, a driver may report a + * pressure value greater than the reported maximum, since it unclear what specific meaning the + * maximum value for pressure has (beyond the maximum value that can be produced by a sensor), + * and no units for pressure (resolution) is specified by the evdev documentation. + */ +TEST_P(TouchIntegrationTest, AcceptsAxisValuesOutsideReportedRange) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Down with pressure outside the reported range + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendDown(centerPoint); + mDevice->sendPressure(UinputTouchScreen::RAW_PRESSURE_MAX + 2); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + // Move to a point outside the reported range + mDevice->sendMove(Point(DISPLAY_WIDTH, DISPLAY_HEIGHT) + Point(1, 1)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); + + // Up + mDevice->sendUp(); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); +} + +TEST_P(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { const Point centerPoint = mDevice->getCenterPoint(); // Send down with the pen tool selected. The policy should be notified of the stylus presence. @@ -1828,19 +1913,69 @@ TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId())); } +TEST_P(TouchIntegrationTest, ExternalStylusConnectedDuringTouchGesture) { + const Point centerPoint = mDevice->getCenterPoint(); + + // Down + mDevice->sendSlot(FIRST_SLOT); + mDevice->sendTrackingId(FIRST_TRACKING_ID); + mDevice->sendDown(centerPoint); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + // Move + mDevice->sendMove(centerPoint + Point(1, 1)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); + + // Connecting an external stylus mid-gesture should not interrupt the ongoing gesture stream. + auto externalStylus = createUinputDevice<UinputExternalStylus>(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + const auto stylusInfo = findDeviceByName(externalStylus->getName()); + ASSERT_TRUE(stylusInfo); + + // Move + mDevice->sendMove(centerPoint + Point(2, 2)); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled( + WithMotionAction(AMOTION_EVENT_ACTION_MOVE))); + + // Disconnecting an external stylus mid-gesture should not interrupt the ongoing gesture stream. + externalStylus.reset(); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); + + // Up + mDevice->sendUp(); + mDevice->sendSync(); + ASSERT_NO_FATAL_FAILURE( + mTestListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP))); + + ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled()); +} + +INSTANTIATE_TEST_SUITE_P(TouchIntegrationTestDisplayVariants, TouchIntegrationTest, + testing::Values(TouchIntegrationTestDisplays::DISPLAY_INTERNAL, + TouchIntegrationTestDisplays::DISPLAY_INPUT_PORT, + TouchIntegrationTestDisplays::DISPLAY_UNIQUE_ID)); + // --- StylusButtonIntegrationTest --- // Verify the behavior of button presses reported by various kinds of styluses, including buttons // reported by the touchscreen's device, by a fused external stylus, and by an un-fused external // stylus. template <typename UinputStylusDevice> -class StylusButtonIntegrationTest : public TouchIntegrationTest { +class StylusButtonIntegrationTest : public BaseTouchIntegrationTest { protected: void SetUp() override { #if !defined(__ANDROID__) GTEST_SKIP(); #endif - TouchIntegrationTest::SetUp(); + BaseTouchIntegrationTest::SetUp(); mTouchscreen = mDevice.get(); mTouchscreenInfo = mDeviceInfo; @@ -1878,8 +2013,8 @@ private: std::unique_ptr<UinputStylusDevice> mStylusDeviceLifecycleTracker{}; // Hide the base class's device to expose it with a different name for readability. - using TouchIntegrationTest::mDevice; - using TouchIntegrationTest::mDeviceInfo; + using BaseTouchIntegrationTest::mDevice; + using BaseTouchIntegrationTest::mDeviceInfo; }; using StylusButtonIntegrationTestTypes = @@ -2131,7 +2266,7 @@ TYPED_TEST(StylusButtonIntegrationTest, StylusButtonMotionEventsDisabled) { // Verify the behavior of an external stylus. An external stylus can report pressure or button // data independently of the touchscreen, which is then sent as a MotionEvent as part of an // ongoing stylus gesture that is being emitted by the touchscreen. -using ExternalStylusIntegrationTest = TouchIntegrationTest; +using ExternalStylusIntegrationTest = BaseTouchIntegrationTest; TEST_F(ExternalStylusIntegrationTest, FusedExternalStylusPressureReported) { const Point centerPoint = mDevice->getCenterPoint(); diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index fc917dd582..41e250f789 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -57,6 +57,18 @@ void TestInputListener::assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* "Expected notifyDeviceReset() to have been called.")); } +void TestInputListener::clearNotifyDeviceResetCalls() { + std::scoped_lock<std::mutex> lock(mLock); + std::get<std::vector<NotifyDeviceResetArgs>>(mQueues).clear(); +} + +void TestInputListener::assertNotifyDeviceResetWasCalled( + const ::testing::Matcher<NotifyDeviceResetArgs>& matcher) { + NotifyDeviceResetArgs outEventArgs; + ASSERT_NO_FATAL_FAILURE(assertNotifyDeviceResetWasCalled(&outEventArgs)); + ASSERT_THAT(outEventArgs, matcher); +} + void TestInputListener::assertNotifyDeviceResetWasNotCalled() { ASSERT_NO_FATAL_FAILURE( assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called.")); diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index deb60483fb..3c5e0146d6 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -43,6 +43,10 @@ public: void assertNotifyConfigurationChangedWasNotCalled(); + void clearNotifyDeviceResetCalls(); + + void assertNotifyDeviceResetWasCalled(const ::testing::Matcher<NotifyDeviceResetArgs>& matcher); + void assertNotifyDeviceResetWasCalled(NotifyDeviceResetArgs* outEventArgs = nullptr); void assertNotifyDeviceResetWasNotCalled(); diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index 020ea86da2..183383f57d 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -139,6 +139,10 @@ public: return mDeviceId == args.deviceId; } + bool MatchAndExplain(const NotifyDeviceResetArgs& args, std::ostream*) const { + return mDeviceId == args.deviceId; + } + bool MatchAndExplain(const InputEvent& event, std::ostream*) const { return mDeviceId == event.getDeviceId(); } diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp index 97a26141e0..19f7bb4409 100644 --- a/services/inputflinger/tests/UinputDevice.cpp +++ b/services/inputflinger/tests/UinputDevice.cpp @@ -159,10 +159,11 @@ void UinputExternalStylusWithPressure::setPressure(int32_t pressure) { // --- UinputTouchScreen --- -UinputTouchScreen::UinputTouchScreen(const Rect& size) +UinputTouchScreen::UinputTouchScreen(const Rect& size, const std::string& physicalPort) : UinputKeyboard(DEVICE_NAME, PRODUCT_ID, {BTN_TOUCH, BTN_TOOL_PEN, BTN_STYLUS, BTN_STYLUS2, BTN_STYLUS3}), - mSize(size) {} + mSize(size), + mPhysicalPort(physicalPort) {} void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { UinputKeyboard::configureDevice(fd, device); @@ -176,7 +177,11 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE); ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + if (!mPhysicalPort.empty()) { + ioctl(fd, UI_SET_PHYS, mPhysicalPort.c_str()); + } device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN; device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX; @@ -190,6 +195,8 @@ void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) { device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX; device->absmin[ABS_MT_TOOL_TYPE] = MT_TOOL_FINGER; device->absmax[ABS_MT_TOOL_TYPE] = MT_TOOL_MAX; + device->absmin[ABS_MT_PRESSURE] = RAW_PRESSURE_MIN; + device->absmax[ABS_MT_PRESSURE] = RAW_PRESSURE_MAX; } void UinputTouchScreen::sendSlot(int32_t slot) { @@ -202,6 +209,7 @@ void UinputTouchScreen::sendTrackingId(int32_t trackingId) { void UinputTouchScreen::sendDown(const Point& point) { injectEvent(EV_KEY, BTN_TOUCH, 1); + injectEvent(EV_ABS, ABS_MT_PRESSURE, RAW_PRESSURE_MAX); injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x); injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); } @@ -211,6 +219,10 @@ void UinputTouchScreen::sendMove(const Point& point) { injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y); } +void UinputTouchScreen::sendPressure(int32_t pressure) { + injectEvent(EV_ABS, ABS_MT_PRESSURE, pressure); +} + void UinputTouchScreen::sendPointerUp() { sendTrackingId(0xffffffff); } diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h index 51e331df8f..e7010c32a5 100644 --- a/services/inputflinger/tests/UinputDevice.h +++ b/services/inputflinger/tests/UinputDevice.h @@ -189,6 +189,7 @@ public: void sendTrackingId(int32_t trackingId); void sendDown(const Point& point); void sendMove(const Point& point); + void sendPressure(int32_t pressure); void sendPointerUp(); void sendUp(); void sendToolType(int32_t toolType); @@ -197,11 +198,12 @@ public: const Point getCenterPoint(); protected: - explicit UinputTouchScreen(const Rect& size); + explicit UinputTouchScreen(const Rect& size, const std::string& physicalPort = ""); private: void configureDevice(int fd, uinput_user_dev* device) override; const Rect mSize; + const std::string mPhysicalPort; }; } // namespace android diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index d7980f564e..9313a89005 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -56,6 +56,15 @@ cc_defaults { ], fuzz_config: { cc: ["android-framework-input@google.com"], + componentid: 155276, + hotlists: [ + "4593311", + ], + description: "The fuzzer targets the APIs of libinputflinger library", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "multi_user", + fuzzed_code_usage: "shipped", }, } diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 2523f3b6a6..8b16890a45 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -9,7 +9,7 @@ package { cc_library_shared { name: "libpowermanager", - + defaults: ["android.hardware.power-ndk_export_shared"], srcs: [ "BatterySaverPolicyConfig.cpp", "CoolingDevice.cpp", @@ -41,7 +41,6 @@ cc_library_shared { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", ], export_shared_lib_headers: [ @@ -49,7 +48,6 @@ cc_library_shared { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", ], cflags: [ diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp index 03fc38d304..2b5ddb1460 100644 --- a/services/powermanager/benchmarks/Android.bp +++ b/services/powermanager/benchmarks/Android.bp @@ -23,6 +23,7 @@ package { cc_benchmark { name: "libpowermanager_benchmarks", + defaults: ["android.hardware.power-ndk_shared"], srcs: [ "main.cpp", "PowerHalAidlBenchmarks.cpp", @@ -41,7 +42,6 @@ cc_benchmark { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", ], static_libs: [ "libtestUtil", diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp index 08fcdc8275..6fc96c0959 100644 --- a/services/powermanager/tests/Android.bp +++ b/services/powermanager/tests/Android.bp @@ -23,6 +23,10 @@ package { cc_test { name: "libpowermanager_test", + defaults: [ + "android.hardware.power-ndk_shared", + "android.hardware.power-ndk_shared", + ], test_suites: ["device-tests"], srcs: [ "IThermalManagerTest.cpp", @@ -52,7 +56,6 @@ cc_test { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", ], static_libs: [ "libgmock", diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 326645e596..0101c1712a 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -26,6 +26,7 @@ cc_defaults { name: "libsurfaceflinger_defaults", defaults: [ "android.hardware.graphics.composer3-ndk_shared", + "android.hardware.power-ndk_shared", "librenderengine_deps", "libtimestats_deps", "surfaceflinger_defaults", @@ -48,7 +49,6 @@ cc_defaults { "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", "android.hardware.graphics.composer@2.4", - "android.hardware.power-V4-ndk", "libbase", "libbinder", "libbinder_ndk", diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h index b56b252d9f..fefc04054d 100644 --- a/services/surfaceflinger/ClientCache.h +++ b/services/surfaceflinger/ClientCache.h @@ -29,7 +29,9 @@ #include <set> #include <unordered_map> -#define BUFFER_CACHE_MAX_SIZE 64 +// 4096 is based on 64 buffers * 64 layers. Once this limit is reached, the least recently used +// buffer is uncached before the new buffer is cached. +#define BUFFER_CACHE_MAX_SIZE 4096 namespace android { diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 3426495297..06c5e4c933 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -11,6 +11,7 @@ cc_defaults { name: "libcompositionengine_defaults", defaults: [ "android.hardware.graphics.composer3-ndk_shared", + "android.hardware.power-ndk_shared", "librenderengine_deps", "libtimestats_deps", "surfaceflinger_defaults", @@ -27,7 +28,6 @@ cc_defaults { "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", "libbase", "libcutils", "libgui", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h index 9f6141a1b1..d607c75325 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/TexturePool.h @@ -66,7 +66,7 @@ public: TexturePool(renderengine::RenderEngine& renderEngine) : mRenderEngine(renderEngine), mEnabled(false) {} - virtual ~TexturePool(); + virtual ~TexturePool() = default; // Sets the display size for the texture pool. // This will trigger a reallocation for all remaining textures in the pool. @@ -83,10 +83,11 @@ public: // be held by the pool. This is useful when the active display changes. void setEnabled(bool enable); - void dump(std::string& out) const EXCLUDES(mMutex); + void dump(std::string& out) const; protected: // Proteted visibility so that they can be used for testing + const static constexpr size_t kMinPoolSize = 3; const static constexpr size_t kMaxPoolSize = 4; struct Entry { @@ -95,20 +96,16 @@ protected: }; std::deque<Entry> mPool; - std::future<std::shared_ptr<renderengine::ExternalTexture>> mGenTextureFuture; private: - std::shared_ptr<renderengine::ExternalTexture> genTexture(ui::Size size); + std::shared_ptr<renderengine::ExternalTexture> genTexture(); // Returns a previously borrowed texture to the pool. void returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& texture, const sp<Fence>& fence); - void genTextureAsyncIfNeeded() REQUIRES(mMutex); - void resetPool() REQUIRES(mMutex); - renderengine::RenderEngine& mRenderEngine GUARDED_BY(mRenderEngineMutex); - ui::Size mSize GUARDED_BY(mMutex); + void allocatePool(); + renderengine::RenderEngine& mRenderEngine; + ui::Size mSize; bool mEnabled; - mutable std::mutex mMutex; - mutable std::mutex mRenderEngineMutex; }; } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp index 7e020ee1c1..bdaa1d0ae1 100644 --- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp @@ -41,12 +41,10 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs, } inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) { - return lhs.textureName == rhs.textureName && - lhs.useTextureFiltering == rhs.useTextureFiltering && + return lhs.useTextureFiltering == rhs.useTextureFiltering && lhs.textureTransform == rhs.textureTransform && lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha && - lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 && - lhs.maxLuminanceNits == rhs.maxLuminanceNits; + lhs.isOpaque == rhs.isOpaque && lhs.maxLuminanceNits == rhs.maxLuminanceNits; } inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs, diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp index 8f67f3683a..97725ea636 100644 --- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp @@ -220,17 +220,6 @@ DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs& minLuminance = minLuminance <= 0.0 ? sDefaultMinLumiance : minLuminance; maxLuminance = maxLuminance <= 0.0 ? sDefaultMaxLumiance : maxLuminance; maxAverageLuminance = maxAverageLuminance <= 0.0 ? sDefaultMaxLumiance : maxAverageLuminance; - if (args.hasWideColorGamut) { - // insert HDR10/HLG as we will force client composition for HDR10/HLG - // layers - if (!hasHDR10Support()) { - types.push_back(ui::Hdr::HDR10); - } - - if (!hasHLGSupport()) { - types.push_back(ui::Hdr::HLG); - } - } mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index d4230f5575..78c23daf48 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -895,13 +895,19 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const { compositionengine::OutputLayer* layerRequestingBgComposition = nullptr; for (auto* layer : getOutputLayersOrderedByZ()) { - auto* compState = layer->getLayerFE().getCompositionState(); + const auto* compState = layer->getLayerFE().getCompositionState(); // If any layer has a sideband stream, we will disable blurs. In that case, we don't // want to force client composition because of the blur. if (compState->sidebandStream != nullptr) { return nullptr; } + + // If RenderEngine cannot render protected content, we cannot blur. + if (compState->hasProtectedContent && + !getCompositionEngine().getRenderEngine().supportsProtectedContent()) { + return nullptr; + } if (compState->isOpaque) { continue; } @@ -1309,17 +1315,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( }); const nsecs_t renderEngineStart = systemTime(); - // Only use the framebuffer cache when rendering to an internal display - // TODO(b/173560331): This is only to help mitigate memory leaks from virtual displays because - // right now we don't have a concrete eviction policy for output buffers: GLESRenderEngine - // bounds its framebuffer cache but Skia RenderEngine has no current policy. The best fix is - // probably to encapsulate the output buffer into a structure that dispatches resource cleanup - // over to RenderEngine, in which case this flag can be removed from the drawLayers interface. - const bool useFramebufferCache = outputState.layerFilter.toInternalDisplay; - auto fenceResult = renderEngine .drawLayers(clientCompositionDisplay, clientRenderEngineLayers, tex, - useFramebufferCache, std::move(fd)) + std::move(fd)) .get(); if (mClientCompositionRequestCache && fenceStatus(fenceResult) != NO_ERROR) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 7547be94e3..579c6ba772 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -275,11 +275,9 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te bufferFence.reset(texture->getReadyFence()->dup()); } - constexpr bool kUseFramebufferCache = false; - auto fenceResult = renderEngine .drawLayers(displaySettings, layerSettings, texture->get(), - kUseFramebufferCache, std::move(bufferFence)) + std::move(bufferFence)) .get(); if (fenceStatus(fenceResult) == NO_ERROR) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index f439caf9e1..8dab6ce61b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp @@ -34,7 +34,7 @@ LayerState::LayerState(compositionengine::OutputLayer* layer) [](const mat4& mat) { using namespace std::string_literals; std::vector<std::string> split = - base::Split(std::string(mat.asString().string()), "\n"s); + base::Split(std::string(mat.asString().c_str()), "\n"s); split.pop_back(); // Strip the last (empty) line return split; }}) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 54133d92b0..5e6cade56f 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -216,32 +216,32 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { base::StringAppendF(&result, "Expected two layer stack hashes, e.g. '--planner %s " "<left_hash> <right_hash>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 4) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <left_hash> " "<right_hash>'\n", - command.string()); + command.c_str()); return; } const String8 leftHashString(args[2]); size_t leftHash = 0; - int fieldsRead = sscanf(leftHashString.string(), "%zx", &leftHash); + int fieldsRead = sscanf(leftHashString.c_str(), "%zx", &leftHash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - leftHashString.string()); + leftHashString.c_str()); return; } const String8 rightHashString(args[3]); size_t rightHash = 0; - fieldsRead = sscanf(rightHashString.string(), "%zx", &rightHash); + fieldsRead = sscanf(rightHashString.c_str(), "%zx", &rightHash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - rightHashString.string()); + rightHashString.c_str()); return; } @@ -252,22 +252,22 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { if (args.size() < 3) { base::StringAppendF(&result, "Expected a layer stack hash, e.g. '--planner %s <hash>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 3) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <hash>'\n", - command.string()); + command.c_str()); return; } const String8 hashString(args[2]); size_t hash = 0; - const int fieldsRead = sscanf(hashString.string(), "%zx", &hash); + const int fieldsRead = sscanf(hashString.c_str(), "%zx", &hash); if (fieldsRead != 1) { base::StringAppendF(&result, "Failed to parse %s as a size_t\n", - hashString.string()); + hashString.c_str()); return; } @@ -279,20 +279,20 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { } else if (command == "--similar" || command == "-s") { if (args.size() < 3) { base::StringAppendF(&result, "Expected a plan string, e.g. '--planner %s <plan>'\n", - command.string()); + command.c_str()); return; } if (args.size() > 3) { base::StringAppendF(&result, "Too many arguments found, expected '--planner %s <plan>'\n", - command.string()); + command.c_str()); return; } const String8 planString(args[2]); - std::optional<Plan> plan = Plan::fromString(std::string(planString.string())); + std::optional<Plan> plan = Plan::fromString(std::string(planString.c_str())); if (!plan) { - base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.string()); + base::StringAppendF(&result, "Failed to parse %s as a Plan\n", planString.c_str()); return; } @@ -302,7 +302,7 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { } else if (command == "--layers" || command == "-l") { mFlattener.dumpLayers(result); } else { - base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string()); + base::StringAppendF(&result, "Unknown command '%s'\n\n", command.c_str()); dumpUsage(result); } return; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp index 10f58cea5d..54ecb5691d 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/TexturePool.cpp @@ -25,61 +25,31 @@ namespace android::compositionengine::impl::planner { -TexturePool::~TexturePool() { - if (mGenTextureFuture.valid()) { - mGenTextureFuture.get(); - } -} - -void TexturePool::resetPool() { - if (mGenTextureFuture.valid()) { - mGenTextureFuture.get(); - } +void TexturePool::allocatePool() { mPool.clear(); - genTextureAsyncIfNeeded(); -} - -// Generate a new texture asynchronously so it will not require allocation on the main -// thread. -void TexturePool::genTextureAsyncIfNeeded() { - if (mEnabled && mSize.isValid() && !mGenTextureFuture.valid()) { - mGenTextureFuture = std::async( - std::launch::async, [&](ui::Size size) { return genTexture(size); }, mSize); + if (mEnabled && mSize.isValid()) { + mPool.resize(kMinPoolSize); + std::generate_n(mPool.begin(), kMinPoolSize, [&]() { + return Entry{genTexture(), nullptr}; + }); } } void TexturePool::setDisplaySize(ui::Size size) { - std::lock_guard lock(mMutex); if (mSize == size) { return; } mSize = size; - resetPool(); + allocatePool(); } std::shared_ptr<TexturePool::AutoTexture> TexturePool::borrowTexture() { if (mPool.empty()) { - std::lock_guard lock(mMutex); - std::shared_ptr<TexturePool::AutoTexture> tex; - if (mGenTextureFuture.valid()) { - tex = std::make_shared<AutoTexture>(*this, mGenTextureFuture.get(), nullptr); - } else { - tex = std::make_shared<AutoTexture>(*this, genTexture(mSize), nullptr); - } - // Speculatively generate a new texture, so that the next call does not need - // to wait for allocation. - genTextureAsyncIfNeeded(); - return tex; + return std::make_shared<AutoTexture>(*this, genTexture(), nullptr); } const auto entry = mPool.front(); mPool.pop_front(); - if (mPool.empty()) { - std::lock_guard lock(mMutex); - // Similiarly generate a new texture when lending out the last entry, so that - // the next call does not need to wait for allocation. - genTextureAsyncIfNeeded(); - } return std::make_shared<AutoTexture>(*this, entry.texture, entry.fence); } @@ -90,8 +60,6 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& return; } - std::lock_guard lock(mMutex); - // Or the texture on the floor if the pool is no longer tracking textures of the same size. if (static_cast<int32_t>(texture->getBuffer()->getWidth()) != mSize.getWidth() || static_cast<int32_t>(texture->getBuffer()->getHeight()) != mSize.getHeight()) { @@ -112,14 +80,13 @@ void TexturePool::returnTexture(std::shared_ptr<renderengine::ExternalTexture>&& mPool.push_back({std::move(texture), fence}); } -std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size size) { - std::lock_guard lock(mRenderEngineMutex); - LOG_ALWAYS_FATAL_IF(!size.isValid(), "Attempted to generate texture with invalid size"); +std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture() { + LOG_ALWAYS_FATAL_IF(!mSize.isValid(), "Attempted to generate texture with invalid size"); return std::make_shared< renderengine::impl:: ExternalTexture>(sp<GraphicBuffer>:: - make(static_cast<uint32_t>(size.getWidth()), - static_cast<uint32_t>(size.getHeight()), + make(static_cast<uint32_t>(mSize.getWidth()), + static_cast<uint32_t>(mSize.getHeight()), HAL_PIXEL_FORMAT_RGBA_8888, 1U, static_cast<uint64_t>( GraphicBuffer::USAGE_HW_RENDER | @@ -133,16 +100,13 @@ std::shared_ptr<renderengine::ExternalTexture> TexturePool::genTexture(ui::Size void TexturePool::setEnabled(bool enabled) { mEnabled = enabled; - - std::lock_guard lock(mMutex); - resetPool(); + allocatePool(); } void TexturePool::dump(std::string& out) const { - std::lock_guard lock(mMutex); base::StringAppendF(&out, "TexturePool (%s) has %zu buffers of size [%" PRId32 ", %" PRId32 "]\n", mEnabled ? "enabled" : "disabled", mPool.size(), mSize.width, mSize.height); } -} // namespace android::compositionengine::impl::planner +} // namespace android::compositionengine::impl::planner
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp index b3ff2ec6a0..03a97dc770 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp @@ -282,39 +282,6 @@ TEST_F(DisplayColorProfileTest, ctorUsesOrDefaultsLuminanceValuesFromInputArgs) } } -TEST_F(DisplayColorProfileTest, ctorSignalsHdrSupportForAnyWideColorGamutDevice) { - { - // If the output does not profile wide color gamut, then no HDR modes - // will be profileed in the generated HDR capabilities. - auto profile = ProfileFactory().setHasWideColorGamut(false).build(); - - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), IsEmpty()); - } - - { - // If the HWC does not show profile for certain HDR modes, then the - // generated HDR capabilities will indicate profile anyway. - auto profile = ProfileFactory().setHasWideColorGamut(true).build(); - - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2)); - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10)); - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG)); - } - - { - // If the HWC profiles the HDR modes, then the generated capabilities - // still has one entry for each HDR type. - auto profile = ProfileFactory() - .setHasWideColorGamut(true) - .addHdrTypes({Hdr::HLG, Hdr::HDR10}) - .build(); - - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), SizeIs(2)); - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HDR10)); - EXPECT_THAT(profile.getHdrCapabilities().getSupportedHdrTypes(), Contains(Hdr::HLG)); - } -} - /* ------------------------------------------------------------------------ * DisplayColorProfile::hasRenderIntent */ diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index ebf9a2b2e2..ee6998aef8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -3421,10 +3421,10 @@ TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) { .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { return ftl::yield<FenceResult>(Fence::NO_FENCE); }); @@ -3452,10 +3452,10 @@ TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { return ftl::yield<FenceResult>(Fence::NO_FENCE); }); @@ -3486,10 +3486,10 @@ TEST_F(OutputComposeSurfacesTest, })); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, true, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { return ftl::yield<FenceResult>(Fence::NO_FENCE); }); @@ -3515,7 +3515,7 @@ TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithou .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .Times(2) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); @@ -3545,7 +3545,7 @@ TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); @@ -3581,10 +3581,10 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)) .WillOnce(Return(mOutputBuffer)) .WillOnce(Return(otherOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { return ftl::yield<FenceResult>(Fence::NO_FENCE); }); @@ -3616,9 +3616,9 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); - EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r3), _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); verify().execute().expectAFenceWasReturned(); @@ -3695,7 +3695,7 @@ struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComp struct ExpectDisplaySettingsState : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> { auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) { - EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, false, _)) + EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); return nextState<ExecuteState>(); } @@ -3948,11 +3948,11 @@ struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeS EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillRepeatedly([&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&) -> ftl::Future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -3987,7 +3987,7 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) { EXPECT_CALL(*mRenderSurface, setProtected(true)); // Must happen after setting the protected content state. EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); base::unique_fd fd; @@ -4046,7 +4046,7 @@ TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDatas InSequence seq; EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); base::unique_fd fd; diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index bd030d090f..d61d7ba574 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -353,7 +353,7 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); @@ -369,7 +369,7 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { .WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(false))) .WillOnce(Return(clientComp2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = false; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); @@ -402,7 +402,7 @@ TEST_F(CachedSetTest, renderSecureOutput) { const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); @@ -419,7 +419,7 @@ TEST_F(CachedSetTest, renderSecureOutput) { .WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, prepareClientComposition(ClientCompositionTargetSettingsSecureEq(true))) .WillOnce(Return(clientComp2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); @@ -452,7 +452,7 @@ TEST_F(CachedSetTest, renderWhitePoint) { const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); return ftl::yield<FenceResult>(Fence::NO_FENCE); @@ -466,7 +466,7 @@ TEST_F(CachedSetTest, renderWhitePoint) { prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) .WillOnce(Return(clientComp2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); @@ -502,7 +502,7 @@ TEST_F(CachedSetTest, renderWhitePointNoColorTransform) { const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); return ftl::yield<FenceResult>(Fence::NO_FENCE); @@ -516,7 +516,7 @@ TEST_F(CachedSetTest, renderWhitePointNoColorTransform) { prepareClientComposition(ClientCompositionTargetSettingsWhitePointEq( mOutputState.displayBrightnessNits))) .WillOnce(Return(clientComp2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false); expectReadyBuffer(cachedSet); @@ -551,7 +551,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(mOutputState.framebufferSpace.getContent(), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.getContent(), displaySettings.clip); @@ -566,7 +566,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { EXPECT_CALL(*layerFE1, prepareClientComposition(_)).WillOnce(Return(clientComp1)); EXPECT_CALL(*layerFE2, prepareClientComposition(_)).WillOnce(Return(clientComp2)); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); @@ -815,7 +815,7 @@ TEST_F(CachedSetTest, addHolePunch) { const auto drawLayers = [&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. @@ -839,7 +839,7 @@ TEST_F(CachedSetTest, addHolePunch) { return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } @@ -875,7 +875,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { const auto drawLayers = [&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. @@ -900,7 +900,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } @@ -1026,7 +1026,7 @@ TEST_F(CachedSetTest, addBlur) { const auto drawLayers = [&](const renderengine::DisplaySettings&, const std::vector<renderengine::LayerSettings>& layers, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> ftl::Future<FenceResult> { // If the highlight layer is enabled, it will increase the size by 1. // We're interested in the third layer either way. @@ -1039,7 +1039,7 @@ TEST_F(CachedSetTest, addBlur) { return ftl::yield<FenceResult>(Fence::NO_FENCE); }; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).WillOnce(Invoke(drawLayers)); cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 778a0a8c93..00590e66cb 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -168,7 +168,7 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) { // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -419,7 +419,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { // caleed for Layer2 and Layer3 layerState1->resetFramesSinceBufferUpdate(); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -442,7 +442,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { layerState1->incrementFramesSinceBufferUpdate(); mTime += 200ms; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), @@ -494,7 +494,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { // called for Layer1 and Layer2 layerState3->resetFramesSinceBufferUpdate(); - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), @@ -508,7 +508,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_EQ(nullptr, overrideBuffer5); // Layers 1 and 2 will be flattened a new drawFrame would be called for Layer4 and Layer5 - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), @@ -537,7 +537,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { layerState3->incrementFramesSinceBufferUpdate(); mTime += 200ms; - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), @@ -592,7 +592,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -603,7 +603,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -656,7 +656,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -667,7 +667,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -728,7 +728,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the // exception that there would be a hole punch above it. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -736,7 +736,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { EXPECT_EQ(nullptr, overrideBuffer0); // This time we merge the CachedSet in and we should still have only two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -798,7 +798,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the // exception that there would be a hole punch above it. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -806,7 +806,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { EXPECT_EQ(nullptr, overrideBuffer0); // This time we merge the CachedSet in and we should still have only two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -849,7 +849,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { layerState3->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -894,7 +894,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { layerState1->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillRepeatedly(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -947,7 +947,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { layerState1->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -995,7 +995,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv layerStateWithBlurBehind->resetFramesSinceBufferUpdate(); // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -1037,7 +1037,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Mark the layers inactive mTime += 200ms; // layers would be flattened but the buffer would not be overridden - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); initializeOverrideBuffer(layers); @@ -1050,7 +1050,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Simulate attempting to render prior to merging the new cached set with the layer stack. // Here we should not try to re-render. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We provide the override buffer now that it's rendered @@ -1097,14 +1097,14 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); for (size_t i = 0; i < kMaxDeferRenderAttempts; i++) { - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - (kCachedSetRenderDuration + 10ms), true); } - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - @@ -1139,7 +1139,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1150,7 +1150,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1189,7 +1189,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1200,7 +1200,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1239,7 +1239,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1250,7 +1250,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1289,7 +1289,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1300,7 +1300,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1342,7 +1342,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1354,7 +1354,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); @@ -1394,7 +1394,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) { mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); // This will render a CachedSet. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)) .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE)))); mFlattener->renderCachedSets(mOutputState, std::nullopt, true); @@ -1405,7 +1405,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) { // This time we merge the CachedSet in, so we have a new hash, and we should // only have two sets. - EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0); initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp index 494a9f4df0..6fc90fe5e5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/TexturePoolTest.cpp @@ -32,9 +32,9 @@ class TestableTexturePool : public TexturePool { public: TestableTexturePool(renderengine::RenderEngine& renderEngine) : TexturePool(renderEngine) {} + size_t getMinPoolSize() const { return kMinPoolSize; } size_t getMaxPoolSize() const { return kMaxPoolSize; } size_t getPoolSize() const { return mPool.size(); } - size_t isGenTextureFutureValid() const { return mGenTextureFuture.valid(); } }; struct TexturePoolTest : public testing::Test { @@ -56,8 +56,16 @@ struct TexturePoolTest : public testing::Test { TestableTexturePool mTexturePool = TestableTexturePool(mRenderEngine); }; -TEST_F(TexturePoolTest, preallocatesZeroSizePool) { - EXPECT_EQ(mTexturePool.getPoolSize(), 0u); +TEST_F(TexturePoolTest, preallocatesMinPool) { + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); +} + +TEST_F(TexturePoolTest, doesNotAllocateBeyondMinPool) { + for (size_t i = 0; i < mTexturePool.getMinPoolSize() + 1; i++) { + auto texture = mTexturePool.borrowTexture(); + } + + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); } TEST_F(TexturePoolTest, cyclesUpToMaxPoolSize) { @@ -111,10 +119,10 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { static_cast<int32_t>(texture->get()->getBuffer()->getHeight())); mTexturePool.setDisplaySize(kDisplaySizeTwo); - EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); texture.reset(); // When the texture is returned to the pool, the pool now destroys it. - EXPECT_EQ(mTexturePool.getPoolSize(), 0u); + EXPECT_EQ(mTexturePool.getMinPoolSize(), mTexturePool.getPoolSize()); texture = mTexturePool.borrowTexture(); EXPECT_EQ(kDisplaySizeTwo.getWidth(), @@ -124,11 +132,14 @@ TEST_F(TexturePoolTest, reallocatesWhenDisplaySizeChanges) { } TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); + std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; - for (size_t i = 0; i < 2; i++) { + for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } + EXPECT_EQ(mTexturePool.getPoolSize(), 1u); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); @@ -137,11 +148,12 @@ TEST_F(TexturePoolTest, freesBuffersWhenDisabled) { } TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); std::deque<std::shared_ptr<TexturePool::AutoTexture>> textures; - for (size_t i = 0; i < 2; i++) { + for (size_t i = 0; i < mTexturePool.getMinPoolSize() - 1; i++) { textures.emplace_back(mTexturePool.borrowTexture()); } @@ -150,13 +162,12 @@ TEST_F(TexturePoolTest, doesNotHoldBuffersWhenDisabled) { EXPECT_EQ(mTexturePool.getPoolSize(), 0u); } -TEST_F(TexturePoolTest, genFutureWhenReEnabled) { +TEST_F(TexturePoolTest, reallocatesWhenReEnabled) { + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); mTexturePool.setEnabled(false); EXPECT_EQ(mTexturePool.getPoolSize(), 0u); - EXPECT_FALSE(mTexturePool.isGenTextureFutureValid()); mTexturePool.setEnabled(true); - EXPECT_EQ(mTexturePool.getPoolSize(), 0u); - EXPECT_TRUE(mTexturePool.isGenTextureFutureValid()); + EXPECT_EQ(mTexturePool.getPoolSize(), mTexturePool.getMinPoolSize()); } } // namespace diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 3c7cbe693f..70ccaf883e 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -213,10 +213,7 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps displayFps, Fps rend ATRACE_INT(mRenderFrameRateFPSTrace.c_str(), renderFps.getIntValue()); mRefreshRateSelector->setActiveMode(modeId, renderFps); - - if (mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps); - } + updateRefreshRateOverlayRate(displayFps, renderFps); } status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, @@ -473,7 +470,7 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool sh mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features); mRefreshRateOverlay->setLayerStack(getLayerStack()); mRefreshRateOverlay->setViewport(getSize()); - updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps); + updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps, setByHwc); } void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index aaf2523338..0c2b77de7d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -311,6 +311,14 @@ bool Display::hasCapability(DisplayCapability capability) const { } Error Display::supportsDoze(bool* outSupport) const { + { + std::scoped_lock lock(mDisplayCapabilitiesMutex); + if (!mDisplayCapabilities) { + // The display has not turned on since boot, so DOZE support is unknown. + ALOGW("%s: haven't queried capabilities yet!", __func__); + return Error::NO_RESOURCES; + } + } *outSupport = hasCapability(DisplayCapability::DOZE); return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3177b33538..a9bb928f59 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -616,19 +616,29 @@ status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mo ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); { bool supportsDoze = false; - auto error = hwcDisplay->supportsDoze(&supportsDoze); - if (error != hal::Error::NONE) { - LOG_HWC_ERROR("supportsDoze", error, displayId); - } + const auto queryDozeError = hwcDisplay->supportsDoze(&supportsDoze); - if (!supportsDoze) { + // queryDozeError might be NO_RESOURCES, in the case of a display that has never + // been turned on. In that case, attempt to set to DOZE anyway. + if (!supportsDoze && queryDozeError == hal::Error::NONE) { mode = hal::PowerMode::ON; } - error = hwcDisplay->setPowerMode(mode); + auto error = hwcDisplay->setPowerMode(mode); if (error != hal::Error::NONE) { LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error, displayId); + // If the display had never been turned on, so its doze + // support was unknown, it may truly not support doze. Try + // switching it to ON instead. + if (queryDozeError == hal::Error::NO_RESOURCES) { + ALOGD("%s: failed to set %s to %s. Trying again with ON", __func__, + to_string(displayId).c_str(), to_string(mode).c_str()); + error = hwcDisplay->setPowerMode(hal::PowerMode::ON); + if (error != hal::Error::NONE) { + LOG_HWC_ERROR("setPowerMode(ON)", error, displayId); + } + } } } break; diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h index c26edb5d22..0788d1abce 100644 --- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h +++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h @@ -54,7 +54,6 @@ struct LayerCreationArgs { gui::LayerMetadata metadata; pid_t ownerPid; uid_t ownerUid; - uint32_t textureName; uint32_t sequence; bool addToRoot = true; wp<IBinder> parentHandle = nullptr; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index d389a799ad..80bedf4a39 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -19,6 +19,7 @@ #define LOG_TAG "SurfaceFlinger" #include "LayerSnapshot.h" +#include "Layer.h" namespace android::surfaceflinger::frontend { @@ -117,7 +118,6 @@ LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, sequence = static_cast<int32_t>(state.id); name = state.name; debugName = state.debugName; - textureName = state.textureName; premultipliedAlpha = state.premultipliedAlpha; inputInfo.name = state.name; inputInfo.id = static_cast<int32_t>(uniqueSequence); @@ -345,10 +345,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate clientChanges = requested.what; changes = requested.changes; contentDirty = requested.what & layer_state_t::CONTENT_DIRTY; - // TODO(b/238781169) scope down the changes to only buffer updates. - hasReadyFrame = requested.hasReadyFrame(); + hasReadyFrame = requested.autoRefresh; sidebandStreamHasFrame = requested.hasSidebandStreamFrame(); - updateSurfaceDamage(requested, hasReadyFrame, forceFullDamage, surfaceDamage); + updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage); if (forceUpdate || requested.what & layer_state_t::eTransparentRegionChanged) { transparentRegionHint = requested.transparentRegion; @@ -364,7 +363,7 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse; } if (forceUpdate || requested.what & layer_state_t::eDataspaceChanged) { - dataspace = requested.dataspace; + dataspace = Layer::translateDataspace(requested.dataspace); } if (forceUpdate || requested.what & layer_state_t::eExtendedRangeBrightnessChanged) { currentHdrSdrRatio = requested.currentHdrSdrRatio; @@ -469,19 +468,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate if (forceUpdate || requested.what & (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | - layer_state_t::eApiChanged)) { - isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ && - requested.api == NATIVE_WINDOW_API_MEDIA && - requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102; - } - - if (forceUpdate || - requested.what & - (layer_state_t::eBufferChanged | layer_state_t::eDataspaceChanged | layer_state_t::eApiChanged | layer_state_t::eShadowRadiusChanged | layer_state_t::eBlurRegionsChanged | layer_state_t::eStretchChanged)) { - forceClientComposition = isHdrY410 || shadowSettings.length > 0 || - requested.blurRegions.size() > 0 || stretchEffect.hasEffect(); + forceClientComposition = shadowSettings.length > 0 || stretchEffect.hasEffect(); } if (forceUpdate || diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 1afcef9e44..7537a39060 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -64,7 +64,6 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { int32_t sequence; std::string name; std::string debugName; - uint32_t textureName; bool contentOpaque; bool layerOpaqueFlagSet; RoundedCornerState roundedCorner; @@ -72,14 +71,13 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { Rect transformedBoundsWithoutTransparentRegion; renderengine::ShadowSettings shadowSettings; bool premultipliedAlpha; - bool isHdrY410; ui::Transform parentTransform; Rect bufferSize; Rect croppedBufferSize; std::shared_ptr<renderengine::ExternalTexture> externalTexture; gui::LayerMetadata layerMetadata; gui::LayerMetadata relativeLayerMetadata; - bool hasReadyFrame; + bool hasReadyFrame; // used in post composition to check if there is another frame ready ui::Transform localTransformInverse; gui::WindowInfo inputInfo; ui::Transform localTransform; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 23cfe928f5..159d0f028d 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -854,8 +854,9 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } if (forceUpdate || snapshot.clientChanges & layer_state_t::eCornerRadiusChanged || - snapshot.changes.any(RequestedLayerState::Changes::Geometry)) { - updateRoundedCorner(snapshot, requested, parentSnapshot); + snapshot.changes.any(RequestedLayerState::Changes::Geometry | + RequestedLayerState::Changes::BufferUsageFlags)) { + updateRoundedCorner(snapshot, requested, parentSnapshot, args); } if (forceUpdate || snapshot.clientChanges & layer_state_t::eShadowRadiusChanged || @@ -870,8 +871,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } // computed snapshot properties - snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 || - requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect(); + snapshot.forceClientComposition = + snapshot.shadowSettings.length > 0 || snapshot.stretchEffect.hasEffect(); snapshot.contentOpaque = snapshot.isContentOpaque(); snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() && snapshot.color.a == 1.f; @@ -886,7 +887,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& requested, - const LayerSnapshot& parentSnapshot) { + const LayerSnapshot& parentSnapshot, + const Args& args) { + if (args.skipRoundCornersWhenProtected && requested.isProtected()) { + snapshot.roundedCorner = RoundedCornerState(); + return; + } snapshot.roundedCorner = RoundedCornerState(); RoundedCornerState parentRoundedCorner; if (parentSnapshot.roundedCorner.hasRoundedCorners()) { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h index d361605875..3d64b362ea 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -54,6 +54,7 @@ public: std::unordered_set<uint32_t> excludeLayerIds; const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata; const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap; + bool skipRoundCornersWhenProtected = false; }; LayerSnapshotBuilder(); @@ -103,7 +104,7 @@ private: bool parentIsRelative, const Args& args); static void resetRelativeState(LayerSnapshot& snapshot); static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState, - const LayerSnapshot& parentSnapshot); + const LayerSnapshot& parentSnapshot, const Args& args); void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState, const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags); static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested, diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index a4777d1148..a5d556328d 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -51,7 +51,6 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) name(args.name + "#" + std::to_string(args.sequence)), canBeRoot(args.addToRoot), layerCreationFlags(args.flags), - textureName(args.textureName), ownerUid(args.ownerUid), ownerPid(args.ownerPid), parentId(args.parentId), @@ -147,6 +146,8 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta const ui::Size oldBufferSize = hadBuffer ? ui::Size(externalTexture->getWidth(), externalTexture->getHeight()) : ui::Size(); + const uint64_t oldUsageFlags = hadBuffer ? externalTexture->getUsage() : 0; + const bool hadSideStream = sidebandStream != nullptr; const layer_state_t& clientState = resolvedComposerState.state; const bool hadBlur = hasBlur(); @@ -177,6 +178,10 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::BufferSize; changes |= RequestedLayerState::Changes::Geometry; } + const uint64_t usageFlags = hasBuffer ? externalTexture->getUsage() : 0; + if (oldUsageFlags != usageFlags) { + changes |= RequestedLayerState::Changes::BufferUsageFlags; + } } if (hasBuffer != hadBuffer) { @@ -570,6 +575,10 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { return true; } +bool RequestedLayerState::isProtected() const { + return externalTexture && externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED; +} + void RequestedLayerState::clearChanges() { what = 0; changes.clear(); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 1c19d6d5fc..8eff22bbe4 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -56,6 +56,7 @@ struct RequestedLayerState : layer_state_t { Animation = 1u << 17, BufferSize = 1u << 18, GameMode = 1u << 19, + BufferUsageFlags = 1u << 20, }; static Rect reduce(const Rect& win, const Region& exclude); RequestedLayerState(const LayerCreationArgs&); @@ -85,6 +86,7 @@ struct RequestedLayerState : layer_state_t { bool willReleaseBufferOnLatch() const; bool backpressureEnabled() const; bool isSimpleBufferUpdate(const layer_state_t&) const; + bool isProtected() const; // Layer serial number. This gives layers an explicit ordering, so we // have a stable sort order when their layer stack and Z-order are @@ -93,7 +95,6 @@ struct RequestedLayerState : layer_state_t { const std::string name; bool canBeRoot = false; const uint32_t layerCreationFlags; - const uint32_t textureName; // The owner of the layer. If created from a non system process, it will be the calling uid. // If created from a system process, the value can be passed in. const gui::Uid ownerUid; diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp index 0d3c6ebd47..ca7c3c25f7 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp @@ -188,21 +188,36 @@ bool TransactionHandler::hasPendingTransactions() { } void TransactionHandler::onTransactionQueueStalled(uint64_t transactionId, - sp<ITransactionCompletedListener>& listener, - const std::string& reason) { - if (std::find(mStalledTransactions.begin(), mStalledTransactions.end(), transactionId) != - mStalledTransactions.end()) { - return; - } + StalledTransactionInfo stalledTransactionInfo) { + std::lock_guard lock{mStalledMutex}; + mStalledTransactions.emplace(transactionId, std::move(stalledTransactionInfo)); +} + +void TransactionHandler::removeFromStalledTransactions(uint64_t transactionId) { + std::lock_guard lock{mStalledMutex}; + mStalledTransactions.erase(transactionId); +} - mStalledTransactions.push_back(transactionId); - listener->onTransactionQueueStalled(String8(reason.c_str())); +std::optional<TransactionHandler::StalledTransactionInfo> +TransactionHandler::getStalledTransactionInfo(pid_t pid) { + std::lock_guard lock{mStalledMutex}; + for (auto [_, stalledTransactionInfo] : mStalledTransactions) { + if (pid == stalledTransactionInfo.pid) { + return stalledTransactionInfo; + } + } + return std::nullopt; } -void TransactionHandler::removeFromStalledTransactions(uint64_t id) { - auto it = std::find(mStalledTransactions.begin(), mStalledTransactions.end(), id); - if (it != mStalledTransactions.end()) { - mStalledTransactions.erase(it); +void TransactionHandler::onLayerDestroyed(uint32_t layerId) { + std::lock_guard lock{mStalledMutex}; + for (auto it = mStalledTransactions.begin(); it != mStalledTransactions.end();) { + if (it->second.layerId == layerId) { + it = mStalledTransactions.erase(it); + } else { + it++; + } } } + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h index 04183bcdb8..00f6bcebe6 100644 --- a/services/surfaceflinger/FrontEnd/TransactionHandler.h +++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h @@ -18,6 +18,7 @@ #include <semaphore.h> #include <cstdint> +#include <optional> #include <vector> #include <LocklessQueue.h> @@ -63,9 +64,18 @@ public: std::vector<TransactionState> flushTransactions(); void addTransactionReadyFilter(TransactionFilter&&); void queueTransaction(TransactionState&&); - void onTransactionQueueStalled(uint64_t transactionId, sp<ITransactionCompletedListener>&, - const std::string& reason); + + struct StalledTransactionInfo { + pid_t pid; + uint32_t layerId; + std::string layerName; + uint64_t bufferId; + uint64_t frameNumber; + }; + void onTransactionQueueStalled(uint64_t transactionId, StalledTransactionInfo); void removeFromStalledTransactions(uint64_t transactionId); + std::optional<StalledTransactionInfo> getStalledTransactionInfo(pid_t pid); + void onLayerDestroyed(uint32_t layerId); private: // For unit tests @@ -81,7 +91,10 @@ private: LocklessQueue<TransactionState> mLocklessTransactionQueue; std::atomic<size_t> mPendingTransactionCount = 0; ftl::SmallVector<TransactionFilter, 2> mTransactionReadyFilters; - std::vector<uint64_t> mStalledTransactions; + + std::mutex mStalledMutex; + std::unordered_map<uint64_t /* transactionId */, StalledTransactionInfo> mStalledTransactions + GUARDED_BY(mStalledMutex); }; } // namespace surfaceflinger::frontend } // namespace android diff --git a/services/surfaceflinger/HdrLayerInfoReporter.cpp b/services/surfaceflinger/HdrLayerInfoReporter.cpp index 9eefbe463d..278833244c 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.cpp +++ b/services/surfaceflinger/HdrLayerInfoReporter.cpp @@ -18,14 +18,22 @@ #define LOG_TAG "HdrLayerInfoReporter" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <android-base/stringprintf.h> +#include <inttypes.h> #include <utils/Trace.h> #include "HdrLayerInfoReporter.h" namespace android { +using base::StringAppendF; + void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) { ATRACE_CALL(); + if (mHdrInfoHistory.size() == 0 || mHdrInfoHistory.back().info != info) { + mHdrInfoHistory.next() = EventHistoryEntry{info}; + } + std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke; { std::scoped_lock lock(mMutex); @@ -62,4 +70,15 @@ void HdrLayerInfoReporter::removeListener(const sp<gui::IHdrLayerInfoListener>& mListeners.erase(wp<IBinder>(IInterface::asBinder(listener))); } +void HdrLayerInfoReporter::dump(std::string& result) const { + for (size_t i = 0; i < mHdrInfoHistory.size(); i++) { + const auto& event = mHdrInfoHistory[i]; + const auto& info = event.info; + StringAppendF(&result, + "%" PRId64 ": numHdrLayers(%d), size(%dx%d), flags(%X), desiredRatio(%.2f)\n", + event.timestamp, info.numberOfHdrLayers, info.maxW, info.maxH, info.flags, + info.maxDesiredHdrSdrRatio); + } +} + } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h index bf7c7753d2..614f33fbce 100644 --- a/services/surfaceflinger/HdrLayerInfoReporter.h +++ b/services/surfaceflinger/HdrLayerInfoReporter.h @@ -19,9 +19,11 @@ #include <android-base/thread_annotations.h> #include <android/gui/IHdrLayerInfoListener.h> #include <binder/IBinder.h> +#include <utils/Timers.h> #include <unordered_map> +#include "Utils/RingBuffer.h" #include "WpHash.h" namespace android { @@ -79,6 +81,8 @@ public: return !mListeners.empty(); } + void dump(std::string& result) const; + private: mutable std::mutex mMutex; @@ -88,6 +92,17 @@ private: }; std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex); + + struct EventHistoryEntry { + nsecs_t timestamp = -1; + HdrLayerInfo info; + + EventHistoryEntry() {} + + EventHistoryEntry(const HdrLayerInfo& info) : info(info) { timestamp = systemTime(); } + }; + + utils::RingBuffer<EventHistoryEntry, 32> mHdrInfoHistory; }; } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 38a36fc6bc..30dce114d9 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -149,7 +149,6 @@ Layer::Layer(const LayerCreationArgs& args) args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), mLayerCreationFlags(args.flags), mBorderEnabled(false), - mTextureName(args.textureName), mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) { ALOGV("Creating Layer %s", getDebugName()); @@ -214,7 +213,6 @@ Layer::Layer(const LayerCreationArgs& args) mSnapshot->sequence = sequence; mSnapshot->name = getDebugName(); - mSnapshot->textureName = mTextureName; mSnapshot->premultipliedAlpha = mPremultipliedAlpha; mSnapshot->parentTransform = {}; } @@ -236,13 +234,6 @@ Layer::~Layer() { mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, mBufferInfo.mFence); } - if (!isClone()) { - // The original layer and the clone layer share the same texture. Therefore, only one of - // the layers, in this case the original layer, needs to handle the deletion. The original - // layer and the clone should be removed at the same time so there shouldn't be any issue - // with the clone layer trying to use the deleted texture. - mFlinger->deleteTextureAsync(mTextureName); - } const int32_t layerId = getSequence(); mFlinger->mTimeStats->onDestroy(layerId); mFlinger->mFrameTracer->onDestroy(layerId); @@ -659,8 +650,7 @@ void Layer::preparePerFrameCompositionState() { // Force client composition for special cases known only to the front-end. // Rounded corners no longer force client composition, since we may use a // hole punch so that the layer will appear to have rounded corners. - if (isHdrY410() || drawShadows() || drawingState.blurRegions.size() > 0 || - snapshot->stretchEffect.hasEffect()) { + if (drawShadows() || snapshot->stretchEffect.hasEffect()) { snapshot->forceClientComposition = true; } // If there are no visible region changes, we still need to update blur parameters. @@ -836,12 +826,12 @@ uint32_t Layer::doTransaction(uint32_t flags) { mFlinger->mUpdateInputInfo = true; } - commitTransaction(mDrawingState); + commitTransaction(); return flags; } -void Layer::commitTransaction(State&) { +void Layer::commitTransaction() { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { @@ -2111,6 +2101,13 @@ const std::vector<BlurRegion> Layer::getBlurRegions() const { } RoundedCornerState Layer::getRoundedCornerState() const { + // Today's DPUs cannot do rounded corners. If RenderEngine cannot render + // protected content, remove rounded corners from protected content so it + // can be rendered by the DPU. + if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) { + return {}; + } + // Get parent settings RoundedCornerState parentSettings; const auto& parent = mDrawingParent.promote(); @@ -3618,7 +3615,6 @@ Rect Layer::computeBufferCrop(const State& s) { sp<Layer> Layer::createClone(uint32_t mirrorRootId) { LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()); - args.textureName = mTextureName; sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args); layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId); return layer; @@ -3881,13 +3877,6 @@ bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { return true; } -bool Layer::isHdrY410() const { - // pixel format is HDR Y410 masquerading as RGBA_1010102 - return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ && - mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA && - mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102); -} - sp<LayerFE> Layer::getCompositionEngineLayerFE() const { // There's no need to get a CE Layer if the layer isn't going to draw anything. return hasSomethingToDraw() ? mLegacyLayerFE : nullptr; @@ -4289,7 +4278,6 @@ void Layer::updateSnapshot(bool updateGeometry) { snapshot->contentOpaque = isOpaque(mDrawingState); snapshot->layerOpaqueFlagSet = (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; - snapshot->isHdrY410 = isHdrY410(); sp<Layer> p = mDrawingParent.promote(); if (p != nullptr) { snapshot->parentTransform = p->getTransform(); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 5d77657415..8a65d9dba6 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -23,8 +23,6 @@ #include <gui/WindowInfo.h> #include <layerproto/LayerProtoHeader.h> #include <math/vec4.h> -#include <renderengine/Mesh.h> -#include <renderengine/Texture.h> #include <sys/types.h> #include <ui/BlurRegion.h> #include <ui/FloatRect.h> @@ -427,12 +425,10 @@ public: bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const; // from graphics API - ui::Dataspace translateDataspace(ui::Dataspace dataspace); + static ui::Dataspace translateDataspace(ui::Dataspace dataspace); void updateCloneBufferInfo(); uint64_t mPreviousFrameNumber = 0; - bool isHdrY410() const; - /* * called after composition. * returns true if the layer latched a new buffer this frame. @@ -912,6 +908,7 @@ public: void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) { mTransformHint = transformHint; } + void commitTransaction(); // Keeps track of the previously presented layer stacks. This is used to get // the release fences from the correct displays when we release the last buffer // from the layer. @@ -932,7 +929,6 @@ protected: void preparePerFrameCompositionState(); void preparePerFrameBufferCompositionState(); void preparePerFrameEffectsCompositionState(); - virtual void commitTransaction(State& stateToCommit); void gatherBufferInfo(); void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); @@ -1195,8 +1191,6 @@ private: void setTransformHintLegacy(ui::Transform::RotationFlags); void resetDrawingStateBufferInfo(); - const uint32_t mTextureName; - // Transform hint provided to the producer. This must be accessed holding // the mStateLock. ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0; diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index 4d3e04861b..a0024d52d4 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -223,9 +223,7 @@ void LayerFE::prepareBufferStateClientComposition( layerSettings.source.buffer.buffer = mSnapshot->externalTexture; layerSettings.source.buffer.isOpaque = mSnapshot->contentOpaque; layerSettings.source.buffer.fence = mSnapshot->acquireFence; - layerSettings.source.buffer.textureName = mSnapshot->textureName; layerSettings.source.buffer.usePremultipliedAlpha = mSnapshot->premultipliedAlpha; - layerSettings.source.buffer.isY410BT2020 = mSnapshot->isHdrY410; bool hasSmpte2086 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::SMPTE2086; bool hasCta861_3 = mSnapshot->hdrMetadata.validTypes & HdrMetadata::CTA861_3; float maxLuminance = 0.f; diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 473409715f..3270e4c95c 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1075131 + adyabr@google.com alecmouri@google.com chaviw@google.com diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index fb985f7c5b..2bb8c3ff41 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -148,8 +148,8 @@ std::string toString(const RefreshRateSelector::PolicyVariant& policy) { } // namespace auto RefreshRateSelector::createFrameRateModes( - std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange& renderRange) const - -> std::vector<FrameRateMode> { + const Policy& policy, std::function<bool(const DisplayMode&)>&& filterModes, + const FpsRange& renderRange) const -> std::vector<FrameRateMode> { struct Key { Fps fps; int32_t group; @@ -202,11 +202,25 @@ auto RefreshRateSelector::createFrameRateModes( ALOGV("%s: including %s (%s)", __func__, to_string(fps).c_str(), to_string(mode->getFps()).c_str()); } else { - // We might need to update the map as we found a lower refresh rate - if (isStrictlyLess(mode->getFps(), existingIter->second->second->getFps())) { + // If the primary physical range is a single rate, prefer to stay in that rate + // even if there is a lower physical refresh rate available. This would cause more + // cases to stay within the primary physical range + const Fps existingModeFps = existingIter->second->second->getFps(); + const bool existingModeIsPrimaryRange = policy.primaryRangeIsSingleRate() && + policy.primaryRanges.physical.includes(existingModeFps); + const bool newModeIsPrimaryRange = policy.primaryRangeIsSingleRate() && + policy.primaryRanges.physical.includes(mode->getFps()); + if (newModeIsPrimaryRange == existingModeIsPrimaryRange) { + // We might need to update the map as we found a lower refresh rate + if (isStrictlyLess(mode->getFps(), existingModeFps)) { + existingIter->second = it; + ALOGV("%s: changing %s (%s) as we found a lower physical rate", __func__, + to_string(fps).c_str(), to_string(mode->getFps()).c_str()); + } + } else if (newModeIsPrimaryRange) { existingIter->second = it; - ALOGV("%s: changing %s (%s)", __func__, to_string(fps).c_str(), - to_string(mode->getFps()).c_str()); + ALOGV("%s: changing %s (%s) to stay in the primary range", __func__, + to_string(fps).c_str(), to_string(mode->getFps()).c_str()); } } } @@ -500,10 +514,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi // If the primary range consists of a single refresh rate then we can only // move out the of range if layers explicitly request a different refresh // rate. - const bool primaryRangeIsSingleRate = - isApproxEqual(policy->primaryRanges.physical.min, policy->primaryRanges.physical.max); - - if (!signals.touch && signals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { + if (!signals.touch && signals.idle && + !(policy->primaryRangeIsSingleRate() && hasExplicitVoteLayers)) { ALOGV("Idle"); const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Ascending); ATRACE_FORMAT_INSTANT("%s (Idle)", to_string(ranking.front().frameRateMode.fps).c_str()); @@ -577,8 +589,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi continue; } - const bool inPrimaryRange = policy->primaryRanges.render.includes(fps); - if ((primaryRangeIsSingleRate || !inPrimaryRange) && + const bool inPrimaryPhysicalRange = + policy->primaryRanges.physical.includes(modePtr->getFps()); + const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps); + if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) || + !inPrimaryRenderRange) && !(layer.focused && (layer.vote == LayerVoteType::ExplicitDefault || layer.vote == LayerVoteType::ExplicitExact))) { @@ -689,7 +704,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return score.overallScore == 0; }); - if (primaryRangeIsSingleRate) { + if (policy->primaryRangeIsSingleRate()) { // If we never scored any layers, then choose the rate from the primary // range instead of picking a random score from the app range. if (noLayerScore) { @@ -1236,14 +1251,14 @@ void RefreshRateSelector::constructAvailableRefreshRates() { (supportsFrameRateOverride() || ranges.render.includes(mode.getFps())); }; - auto frameRateModes = createFrameRateModes(filterModes, ranges.render); + auto frameRateModes = createFrameRateModes(*policy, filterModes, ranges.render); if (frameRateModes.empty()) { ALOGW("No matching frame rate modes for %s range. policy: %s", rangeName, policy->toString().c_str()); // TODO(b/292105422): Ideally DisplayManager should not send render ranges smaller than // the min supported. See b/292047939. // For not we just ignore the render ranges. - frameRateModes = createFrameRateModes(filterModes, {}); + frameRateModes = createFrameRateModes(*policy, filterModes, {}); } LOG_ALWAYS_FATAL_IF(frameRateModes.empty(), "No matching frame rate modes for %s range even after ignoring the " diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 7af8d0397f..b25919e7a0 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -101,6 +101,11 @@ public: } bool operator!=(const Policy& other) const { return !(*this == other); } + + bool primaryRangeIsSingleRate() const { + return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max); + } + std::string toString() const; }; @@ -468,8 +473,8 @@ private: } std::vector<FrameRateMode> createFrameRateModes( - std::function<bool(const DisplayMode&)>&& filterModes, const FpsRange&) const - REQUIRES(mLock); + const Policy&, std::function<bool(const DisplayMode&)>&& filterModes, + const FpsRange&) const REQUIRES(mLock); // The display modes of the active display. The DisplayModeIterators below are pointers into // this container, so must be invalidated whenever the DisplayModes change. The Policy below diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 01038432de..ef9b457fc9 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -45,10 +45,9 @@ Rect getOrientedDisplaySpaceRect(ui::Rotation orientation, int reqWidth, int req std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs args) { std::shared_ptr<ScreenCaptureOutput> output = compositionengine::impl::createOutputTemplated< ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&, - const compositionengine::Output::ColorProfile&, bool>(args.compositionEngine, - args.renderArea, - args.colorProfile, - args.regionSampling); + const compositionengine::Output::ColorProfile&, + bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling, + args.dimInGammaSpaceForEnhancedScreenshots); output->editState().isSecure = args.renderArea.isSecure(); output->setCompositionEnabled(true); output->setLayerFilter({args.layerStack}); @@ -81,8 +80,11 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp ScreenCaptureOutput::ScreenCaptureOutput( const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, - bool regionSampling) - : mRenderArea(renderArea), mColorProfile(colorProfile), mRegionSampling(regionSampling) {} + bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots) + : mRenderArea(renderArea), + mColorProfile(colorProfile), + mRegionSampling(regionSampling), + mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {} void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) { auto& outputState = editState(); @@ -95,6 +97,14 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp auto clientCompositionDisplay = compositionengine::impl::Output::generateClientCompositionDisplaySettings(); clientCompositionDisplay.clip = mRenderArea.getSourceCrop(); + + auto renderIntent = static_cast<ui::RenderIntent>(clientCompositionDisplay.renderIntent); + if (mDimInGammaSpaceForEnhancedScreenshots && renderIntent != ui::RenderIntent::COLORIMETRIC && + renderIntent != ui::RenderIntent::TONE_MAP_COLORIMETRIC) { + clientCompositionDisplay.dimmingStage = + aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; + } + return clientCompositionDisplay; } diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h index 159c2bf903..fc095def99 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.h +++ b/services/surfaceflinger/ScreenCaptureOutput.h @@ -37,6 +37,7 @@ struct ScreenCaptureOutputArgs { float targetBrightness; bool regionSampling; bool treat170mAsSrgb; + bool dimInGammaSpaceForEnhancedScreenshots; }; // ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer. @@ -47,7 +48,7 @@ class ScreenCaptureOutput : public compositionengine::impl::Output { public: ScreenCaptureOutput(const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile, - bool regionSampling); + bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots); void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; @@ -63,6 +64,7 @@ private: const RenderArea& mRenderArea; const compositionengine::Output::ColorProfile& mColorProfile; const bool mRegionSampling; + const bool mDimInGammaSpaceForEnhancedScreenshots; }; std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 13a457ed7d..1ef381c417 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -452,6 +452,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI property_get("debug.sf.treat_170m_as_sRGB", value, "0"); mTreat170mAsSrgb = atoi(value); + property_get("debug.sf.dim_in_gamma_in_enhanced_screenshots", value, 0); + mDimInGammaSpaceForEnhancedScreenshots = atoi(value); + mIgnoreHwcPhysicalDisplayOrientation = base::GetBoolProperty("debug.sf.ignore_hwc_physical_display_orientation"s, false); @@ -644,14 +647,6 @@ sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) return getPhysicalDisplayTokenLocked(displayId); } -status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const { - if (!outGetColorManagement) { - return BAD_VALUE; - } - *outGetColorManagement = useColorManagement; - return NO_ERROR; -} - HWComposer& SurfaceFlinger::getHwComposer() const { return mCompositionEngine->getHwComposer(); } @@ -738,52 +733,12 @@ void SurfaceFlinger::bootFinished() { })); } -uint32_t SurfaceFlinger::getNewTexture() { - { - std::lock_guard lock(mTexturePoolMutex); - if (!mTexturePool.empty()) { - uint32_t name = mTexturePool.back(); - mTexturePool.pop_back(); - ATRACE_INT("TexturePoolSize", mTexturePool.size()); - return name; - } - - // The pool was too small, so increase it for the future - ++mTexturePoolSize; - } - - // The pool was empty, so we need to get a new texture name directly using a - // blocking call to the main thread - auto genTextures = [this] { - uint32_t name = 0; - getRenderEngine().genTextures(1, &name); - return name; - }; - if (std::this_thread::get_id() == mMainThreadId) { - return genTextures(); - } else { - return mScheduler->schedule(genTextures).get(); - } -} - -void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { - std::lock_guard lock(mTexturePoolMutex); - // We don't change the pool size, so the fix-up logic in postComposition will decide whether - // to actually delete this or not based on mTexturePoolSize - mTexturePool.push_back(texture); - ATRACE_INT("TexturePoolSize", mTexturePool.size()); -} - static std::optional<renderengine::RenderEngine::RenderEngineType> chooseRenderEngineTypeViaSysProp() { char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded"); - if (strcmp(prop, "gles") == 0) { - return renderengine::RenderEngine::RenderEngineType::GLES; - } else if (strcmp(prop, "threaded") == 0) { - return renderengine::RenderEngine::RenderEngineType::THREADED; - } else if (strcmp(prop, "skiagl") == 0) { + if (strcmp(prop, "skiagl") == 0) { return renderengine::RenderEngine::RenderEngineType::SKIA_GL; } else if (strcmp(prop, "skiaglthreaded") == 0) { return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED; @@ -812,7 +767,6 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { auto builder = renderengine::RenderEngineCreationArgs::Builder() .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat)) .setImageCacheSize(maxFrameBufferAcquiredBuffers) - .setUseColorManagerment(useColorManagement) .setEnableProtectedContext(enable_protected_contents(false)) .setPrecacheToneMapperShaderOnly(false) .setSupportsBackgroundBlur(mSupportsBlur) @@ -915,7 +869,7 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { auto writeFn = [&]() { const std::string filename = TransactionTracing::DIR_NAME + prefix + TransactionTracing::FILE_NAME; - if (overwrite && access(filename.c_str(), F_OK) == 0) { + if (!overwrite && access(filename.c_str(), F_OK) == 0) { ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str()); return; } @@ -2311,7 +2265,9 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, .forceFullDamage = mForceFullDamage, .supportedLayerGenericMetadata = getHwComposer().getSupportedLayerGenericMetadata(), - .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()}; + .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(), + .skipRoundCornersWhenProtected = + !getRenderEngine().supportsProtectedContent()}; mLayerSnapshotBuilder.update(args); } @@ -2320,7 +2276,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, mUpdateInputInfo = true; } if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy | - Changes::Visibility)) { + Changes::Visibility | Changes::Geometry)) { mVisibleRegionsDirty = true; } if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Hierarchy | Changes::FrameRate)) { @@ -2335,6 +2291,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, if (!mLegacyFrontEndEnabled) { ATRACE_NAME("DisplayCallbackAndStatsUpdates"); applyTransactions(update.transactions, vsyncId); + traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); }); const nsecs_t latchTime = systemTime(); bool unused = false; @@ -2359,19 +2316,21 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, mLayersWithBuffersRemoved.emplace(it->second); } it->second->latchBufferImpl(unused, latchTime, bgColorOnly); + newDataLatched = true; + mLayersWithQueuedFrames.emplace(it->second); + mLayersIdsWithQueuedFrames.emplace(it->second->sequence); } - for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { - updateLayerHistory(*snapshot); - if (!snapshot->hasReadyFrame) continue; - newDataLatched = true; - if (!snapshot->isVisible) break; - + mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) { + updateLayerHistory(snapshot); + if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == + mLayersIdsWithQueuedFrames.end()) + return; Region visibleReg; - visibleReg.set(snapshot->transformedBoundsWithoutTransparentRegion); - invalidateLayerStack(snapshot->outputFilter, visibleReg); - } + visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); + invalidateLayerStack(snapshot.outputFilter, visibleReg); + }); for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) { mLegacyLayers.erase(destroyedLayer->id); @@ -2617,9 +2576,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.layersWithQueuedFrames.push_back(layerFE); } - refreshArgs.outputColorSetting = useColorManagement - ? mDisplayColorSetting - : compositionengine::OutputColorSetting::kUnmanaged; + refreshArgs.outputColorSetting = mDisplayColorSetting; refreshArgs.forceOutputColorMode = mForceColorMode; refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; @@ -2741,6 +2698,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse); mLayersWithQueuedFrames.clear(); + mLayersIdsWithQueuedFrames.clear(); if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { // This will block and should only be used for debugging. addToLayerTracing(mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId); @@ -3070,23 +3028,6 @@ void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId, // Cleanup any outstanding resources due to rendering a prior frame. getRenderEngine().cleanupPostRender(); - { - std::lock_guard lock(mTexturePoolMutex); - if (mTexturePool.size() < mTexturePoolSize) { - const size_t refillCount = mTexturePoolSize - mTexturePool.size(); - const size_t offset = mTexturePool.size(); - mTexturePool.resize(mTexturePoolSize); - getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset); - ATRACE_INT("TexturePoolSize", mTexturePool.size()); - } else if (mTexturePool.size() > mTexturePoolSize) { - const size_t deleteCount = mTexturePool.size() - mTexturePoolSize; - const size_t offset = mTexturePoolSize; - getRenderEngine().deleteTextures(deleteCount, mTexturePool.data() + offset); - mTexturePool.resize(mTexturePoolSize); - ATRACE_INT("TexturePoolSize", mTexturePool.size()); - } - } - if (mNumTrustedPresentationListeners > 0) { // We avoid any reverse traversal upwards so this shouldn't be too expensive traverseLegacyLayers([&](Layer* layer) { @@ -3390,18 +3331,16 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked(); - if (useColorManagement) { - mPhysicalDisplays.get(physical->id) - .transform(&PhysicalDisplay::snapshotRef) - .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { - for (const auto mode : snapshot.colorModes()) { - creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode); - creationArgs.hwcColorModes - .emplace(mode, - getHwComposer().getRenderIntents(physical->id, mode)); - } - })); - } + mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::snapshotRef) + .transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) { + for (const auto mode : snapshot.colorModes()) { + creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode); + creationArgs.hwcColorModes + .emplace(mode, + getHwComposer().getRenderIntents(physical->id, mode)); + } + })); } if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) { @@ -3586,8 +3525,6 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, // Recreate the DisplayDevice if the surface or sequence ID changed. if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) { - getRenderEngine().cleanFramebufferCache(); - if (const auto display = getDisplayDeviceLocked(displayToken)) { display->disconnect(); if (display->isVirtual()) { @@ -4458,9 +4395,13 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC (flushState.queueProcessTime - transaction.postTime) > std::chrono::nanoseconds(4s).count()) { mTransactionHandler - .onTransactionQueueStalled(transaction.id, listener, - "Buffer processing hung up due to stuck " - "fence. Indicates GPU hang"); + .onTransactionQueueStalled(transaction.id, + {.pid = layer->getOwnerPid(), + .layerId = static_cast<uint32_t>( + layer->getSequence()), + .layerName = layer->getDebugName(), + .bufferId = s.bufferData->getId(), + .frameNumber = s.bufferData->frameNumber}); } ATRACE_FORMAT("fence unsignaled %s", layer->getDebugName()); return TraverseBuffersReturnValues::STOP_TRAVERSAL; @@ -4553,9 +4494,12 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC (flushState.queueProcessTime - transaction.postTime) > std::chrono::nanoseconds(4s).count()) { mTransactionHandler - .onTransactionQueueStalled(transaction.id, listener, - "Buffer processing hung up due to stuck " - "fence. Indicates GPU hang"); + .onTransactionQueueStalled(transaction.id, + {.pid = layer->ownerPid.val(), + .layerId = layer->id, + .layerName = layer->name, + .bufferId = s.bufferData->getId(), + .frameNumber = s.bufferData->frameNumber}); } ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str()); return TraverseBuffersReturnValues::STOP_TRAVERSAL; @@ -5383,6 +5327,9 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f if (what & layer_state_t::eSidebandStreamChanged) { if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eDataspaceChanged) { + if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eBufferChanged) { std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt; frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence); @@ -5557,7 +5504,6 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurface status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle, sp<Layer>* outLayer) { - args.textureName = getNewTexture(); *outLayer = getFactory().createBufferStateLayer(args); *handle = (*outLayer)->getHandle(); return NO_ERROR; @@ -5582,6 +5528,8 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32 mDestroyedHandles.emplace_back(layerId); } + mTransactionHandler.onLayerDestroyed(layerId); + Mutex::Autolock lock(mStateLock); markLayerPendingRemovalLocked(layer); layer->onHandleDestroyed(); @@ -5709,18 +5657,22 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: // Turn off the display if (displayId == mActiveDisplayId) { - if (setSchedFifo(false) != NO_ERROR) { - ALOGW("Failed to set SCHED_OTHER after powering off active display: %s", - strerror(errno)); - } - if (setSchedAttr(false) != NO_ERROR) { - ALOGW("Failed set uclamp.min after powering off active display: %s", - strerror(errno)); - } + if (const auto display = getActivatableDisplay()) { + onActiveDisplayChangedLocked(activeDisplay.get(), *display); + } else { + if (setSchedFifo(false) != NO_ERROR) { + ALOGW("Failed to set SCHED_OTHER after powering off active display: %s", + strerror(errno)); + } + if (setSchedAttr(false) != NO_ERROR) { + ALOGW("Failed set uclamp.min after powering off active display: %s", + strerror(errno)); + } - if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { - mScheduler->disableHardwareVsync(displayId, true); - mScheduler->enableSyntheticVsync(); + if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { + mScheduler->disableHardwareVsync(displayId, true); + mScheduler->enableSyntheticVsync(); + } } } @@ -5798,6 +5750,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, {"--events"s, dumper(&SurfaceFlinger::dumpEvents)}, {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, + {"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)}, {"--hwclayers"s, dumper(&SurfaceFlinger::dumpHwcLayersMinidumpLockedLegacy)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, @@ -5923,7 +5876,7 @@ void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const auto name = String8(args[1]); mCurrentState.traverseInZOrder([&](Layer* layer) { - if (layer->getName() == name.string()) { + if (layer->getName() == name.c_str()) { layer->dumpFrameStats(result); } }); @@ -5934,7 +5887,7 @@ void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) { const auto name = clearAll ? String8() : String8(args[1]); mCurrentState.traverse([&](Layer* layer) { - if (clearAll || layer->getName() == name.string()) { + if (clearAll || layer->getName() == name.c_str()) { layer->clearFrameStats(); } }); @@ -6086,7 +6039,6 @@ void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args, void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { StringAppendF(&result, "Device supports wide color: %d\n", mSupportsWideColor); - StringAppendF(&result, "Device uses color management: %d\n", useColorManagement); StringAppendF(&result, "DisplayColorSetting: %s\n", decodeDisplayColorSetting(mDisplayColorSetting).c_str()); @@ -6107,6 +6059,14 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { result.append("\n"); } +void SurfaceFlinger::dumpHdrInfo(std::string& result) const { + for (const auto& [displayId, listener] : mHdrLayerInfoListeners) { + StringAppendF(&result, "HDR events for display %" PRIu64 "\n", displayId.value); + listener->dump(result); + result.append("\n"); + } +} + LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { std::unordered_set<uint64_t> stackIdsToSkip; @@ -6271,6 +6231,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& comp result.append("\nWide-Color information:\n"); dumpWideColorInfo(result); + dumpHdrInfo(result); + colorizer.bold(result); result.append("Sync configuration: "); colorizer.reset(result); @@ -6761,8 +6723,6 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32()); switch (setting) { case DisplayColorSetting::kManaged: - reply->writeBool(useColorManagement); - break; case DisplayColorSetting::kUnmanaged: reply->writeBool(true); break; @@ -6795,7 +6755,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } // Is device color managed? case 1030: { - reply->writeBool(useColorManagement); + // ColorDisplayManager stil calls this + reply->writeBool(true); return NO_ERROR; } // Override default composition data space @@ -7333,16 +7294,27 @@ ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayD } // namespace -status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) { +static void invokeScreenCaptureError(const status_t status, + const sp<IScreenCaptureListener>& captureListener) { + ScreenCaptureResults captureResults; + captureResults.fenceResult = base::unexpected(status); + captureListener->onScreenCaptureCompleted(captureResults); +} + +void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); status_t validate = validateScreenshotPermissions(args); if (validate != OK) { - return validate; + invokeScreenCaptureError(validate, captureListener); + return; } - if (!args.displayToken) return BAD_VALUE; + if (!args.displayToken) { + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; + } wp<const DisplayDevice> displayWeak; ui::LayerStack layerStack; @@ -7351,7 +7323,10 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, { Mutex::Autolock lock(mStateLock); sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken); - if (!display) return NAME_NOT_FOUND; + if (!display) { + invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); + return; + } displayWeak = display; layerStack = display->getLayerStack(); @@ -7366,7 +7341,8 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, excludeLayerIds.emplace(excludeLayer); } else { ALOGW("Invalid layer handle passed as excludeLayer to captureDisplay"); - return NAME_NOT_FOUND; + invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); + return; } } } @@ -7389,14 +7365,12 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); } - auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, - args.pixelFormat, args.allowProtected, args.grayscale, - captureListener); - return fenceStatus(future.get()); + captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat, + args.allowProtected, args.grayscale, captureListener); } -status_t SurfaceFlinger::captureDisplay(DisplayId displayId, - const sp<IScreenCaptureListener>& captureListener) { +void SurfaceFlinger::captureDisplay(DisplayId displayId, + const sp<IScreenCaptureListener>& captureListener) { ui::LayerStack layerStack; wp<const DisplayDevice> displayWeak; ui::Size size; @@ -7405,7 +7379,8 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId, const auto display = getDisplayDeviceLocked(displayId); if (!display) { - return NAME_NOT_FOUND; + invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); + return; } displayWeak = display; @@ -7433,25 +7408,25 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId, if (captureListener == nullptr) { ALOGE("capture screen must provide a capture listener callback"); - return BAD_VALUE; + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; } constexpr bool kAllowProtected = false; constexpr bool kGrayscale = false; - auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, - ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, - captureListener); - return fenceStatus(future.get()); + captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, + ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale, captureListener); } -status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, - const sp<IScreenCaptureListener>& captureListener) { +void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); status_t validate = validateScreenshotPermissions(args); if (validate != OK) { - return validate; + invokeScreenCaptureError(validate, captureListener); + return; } ui::Size reqSize; @@ -7469,13 +7444,15 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, parent = LayerHandle::getLayer(args.layerHandle); if (parent == nullptr) { ALOGE("captureLayers called with an invalid or removed parent"); - return NAME_NOT_FOUND; + invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); + return; } if (!canCaptureBlackoutContent && parent->getDrawingState().flags & layer_state_t::eLayerSecure) { ALOGW("Attempting to capture secure layer: PERMISSION_DENIED"); - return PERMISSION_DENIED; + invokeScreenCaptureError(PERMISSION_DENIED, captureListener); + return; } Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState()); @@ -7492,7 +7469,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) { // Error out if the layer has no source bounds (i.e. they are boundless) and a source // crop was not specified, or an invalid frame scale was provided. - return BAD_VALUE; + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; } reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); @@ -7502,7 +7480,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, excludeLayerIds.emplace(excludeLayer); } else { ALOGW("Invalid layer handle passed as excludeLayer to captureLayers"); - return NAME_NOT_FOUND; + invokeScreenCaptureError(NAME_NOT_FOUND, captureListener); + return; } } } // mStateLock @@ -7510,7 +7489,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, // really small crop or frameScale if (reqSize.width <= 0 || reqSize.height <= 0) { ALOGW("Failed to captureLayes: crop or scale too small"); - return BAD_VALUE; + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; } bool childrenOnly = args.childrenOnly; @@ -7574,26 +7554,27 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, if (captureListener == nullptr) { ALOGE("capture screen must provide a capture listener callback"); - return BAD_VALUE; + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; } - auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, - args.pixelFormat, args.allowProtected, args.grayscale, - captureListener); - return fenceStatus(future.get()); + captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat, + args.allowProtected, args.grayscale, captureListener); } -ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( - RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots, - ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale, - const sp<IScreenCaptureListener>& captureListener) { +void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, + GetLayerSnapshotsFunction getLayerSnapshots, + ui::Size bufferSize, ui::PixelFormat reqPixelFormat, + bool allowProtected, bool grayscale, + const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); if (exceedsMaxRenderTargetSize(bufferSize.getWidth(), bufferSize.getHeight())) { ALOGE("Attempted to capture screen with size (%" PRId32 ", %" PRId32 ") that exceeds render target size limit.", bufferSize.getWidth(), bufferSize.getHeight()); - return ftl::yield<FenceResult>(base::unexpected(BAD_VALUE)).share(); + invokeScreenCaptureError(BAD_VALUE, captureListener); + return; } // Loop over all visible layers to see whether there's any protected layer. A protected layer is @@ -7633,14 +7614,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( // Otherwise an irreponsible process may cause an SF crash by allocating // too much. ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share(); + invokeScreenCaptureError(bufferStatus, captureListener); + return; } const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), renderengine::impl::ExternalTexture::Usage:: WRITEABLE); - return captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture, - false /* regionSampling */, grayscale, captureListener); + auto fence = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture, + false /* regionSampling */, grayscale, captureListener); + fence.get(); } ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( @@ -7819,7 +7802,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( .displayBrightnessNits = displayBrightnessNits, .targetBrightness = targetBrightness, .regionSampling = regionSampling, - .treat170mAsSrgb = mTreat170mAsSrgb}); + .treat170mAsSrgb = mTreat170mAsSrgb, + .dimInGammaSpaceForEnhancedScreenshots = + mDimInGammaSpaceForEnhancedScreenshots}); const float colorSaturation = grayscale ? 0 : 1; compositionengine::CompositionRefreshArgs refreshArgs{ @@ -7844,7 +7829,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( const bool renderEngineIsThreaded = [&]() { using Type = renderengine::RenderEngine::RenderEngineType; const auto type = mRenderEngine->getRenderEngineType(); - return type == Type::THREADED || type == Type::SKIA_GL_THREADED; + return type == Type::SKIA_GL_THREADED; }(); auto presentFuture = renderEngineIsThreaded ? ftl::defer(std::move(present)).share() : ftl::yield(present()).share(); @@ -8182,19 +8167,22 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG); for (const auto& [id, display] : mPhysicalDisplays) { if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { - if (setByHwc) { - const auto status = - getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable); - if (status != NO_ERROR) { - ALOGE("Error updating the refresh rate changed callback debug enabled"); - return; - } - } - if (const auto device = getDisplayDeviceLocked(id)) { - device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner, - mRefreshRateOverlayRenderRate, - mRefreshRateOverlayShowInMiddle); + const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD( + kMainThreadContext) { + device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner, + mRefreshRateOverlayRenderRate, + mRefreshRateOverlayShowInMiddle); + }; + enableOverlay(setByHwc); + if (setByHwc) { + const auto status = + getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable); + if (status != NO_ERROR) { + ALOGE("Error updating the refresh rate changed callback debug enabled"); + enableOverlay(/*setByHwc*/ false); + } + } } } } @@ -8314,6 +8302,20 @@ void SurfaceFlinger::onActiveDisplaySizeChanged(const DisplayDevice& activeDispl getRenderEngine().onActiveDisplaySizeChanged(activeDisplay.getSize()); } +sp<DisplayDevice> SurfaceFlinger::getActivatableDisplay() const { + if (mPhysicalDisplays.size() == 1) return nullptr; + + // TODO(b/255635821): Choose the pacesetter display, considering both internal and external + // displays. For now, pick the other internal display, assuming a dual-display foldable. + return findDisplay([this](const DisplayDevice& display) REQUIRES(mStateLock) { + const auto idOpt = PhysicalDisplayId::tryCast(display.getId()); + return idOpt && *idOpt != mActiveDisplayId && display.isPoweredOn() && + mPhysicalDisplays.get(*idOpt) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + }); +} + void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr, const DisplayDevice& activeDisplay) { ATRACE_CALL(); @@ -8362,6 +8364,12 @@ status_t SurfaceFlinger::removeWindowInfosListener( return NO_ERROR; } +status_t SurfaceFlinger::getStalledTransactionInfo( + int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) { + result = mTransactionHandler.getStalledTransactionInfo(pid); + return NO_ERROR; +} + std::shared_ptr<renderengine::ExternalTexture> SurfaceFlinger::getExternalTextureFromBufferData( BufferData& bufferData, const char* layerName, uint64_t transactionId) { if (bufferData.buffer && @@ -8637,7 +8645,9 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> la .excludeLayerIds = std::move(excludeLayerIds), .supportedLayerGenericMetadata = getHwComposer().getSupportedLayerGenericMetadata(), - .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()}; + .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(), + .skipRoundCornersWhenProtected = + !getRenderEngine().supportsProtectedContent()}; mLayerSnapshotBuilder.update(args); auto getLayerSnapshotsFn = @@ -8672,7 +8682,9 @@ SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t u .excludeLayerIds = std::move(excludeLayerIds), .supportedLayerGenericMetadata = getHwComposer().getSupportedLayerGenericMetadata(), - .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()}; + .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap(), + .skipRoundCornersWhenProtected = + !getRenderEngine().supportsProtectedContent()}; mLayerSnapshotBuilder.update(args); auto getLayerSnapshotsFn = @@ -9080,28 +9092,28 @@ binder::Status SurfaceComposerAIDL::setGameContentType(const sp<IBinder>& displa binder::Status SurfaceComposerAIDL::captureDisplay( const DisplayCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { - status_t status = mFlinger->captureDisplay(args, captureListener); - return binderStatusFromStatusT(status); + mFlinger->captureDisplay(args, captureListener); + return binderStatusFromStatusT(NO_ERROR); } binder::Status SurfaceComposerAIDL::captureDisplayById( int64_t displayId, const sp<IScreenCaptureListener>& captureListener) { - status_t status; + // status_t status; IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) { std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId)); - status = mFlinger->captureDisplay(*id, captureListener); + mFlinger->captureDisplay(*id, captureListener); } else { - status = PERMISSION_DENIED; + invokeScreenCaptureError(PERMISSION_DENIED, captureListener); } - return binderStatusFromStatusT(status); + return binderStatusFromStatusT(NO_ERROR); } binder::Status SurfaceComposerAIDL::captureLayers( const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { - status_t status = mFlinger->captureLayers(args, captureListener); - return binderStatusFromStatusT(status); + mFlinger->captureLayers(args, captureListener); + return binderStatusFromStatusT(NO_ERROR); } binder::Status SurfaceComposerAIDL::overrideHdrTypes(const sp<IBinder>& display, @@ -9148,11 +9160,6 @@ binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebu return binderStatusFromStatusT(status); } -binder::Status SurfaceComposerAIDL::getColorManagement(bool* outGetColorManagement) { - status_t status = mFlinger->getColorManagement(outGetColorManagement); - return binderStatusFromStatusT(status); -} - binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) { ui::Dataspace dataspace; ui::PixelFormat pixelFormat; @@ -9467,6 +9474,28 @@ binder::Status SurfaceComposerAIDL::removeWindowInfosListener( return binderStatusFromStatusT(status); } +binder::Status SurfaceComposerAIDL::getStalledTransactionInfo( + int pid, std::optional<gui::StalledTransactionInfo>* outInfo) { + const int callingPid = IPCThreadState::self()->getCallingPid(); + const int callingUid = IPCThreadState::self()->getCallingUid(); + if (!checkPermission(sAccessSurfaceFlinger, callingPid, callingUid)) { + return binderStatusFromStatusT(PERMISSION_DENIED); + } + + std::optional<TransactionHandler::StalledTransactionInfo> stalledTransactionInfo; + status_t status = mFlinger->getStalledTransactionInfo(pid, stalledTransactionInfo); + if (stalledTransactionInfo) { + gui::StalledTransactionInfo result; + result.layerName = String16{stalledTransactionInfo->layerName.c_str()}, + result.bufferId = stalledTransactionInfo->bufferId, + result.frameNumber = stalledTransactionInfo->frameNumber, + outInfo->emplace(std::move(result)); + } else { + outInfo->reset(); + } + return binderStatusFromStatusT(status); +} + status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) { if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f61f9a2345..4d17fa7d2a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -236,9 +236,6 @@ public: static uint32_t maxGraphicsWidth; static uint32_t maxGraphicsHeight; - // Indicate if device wants color management on its display. - static const constexpr bool useColorManagement = true; - static bool useContextPriority; // The data space and pixel format that SurfaceFlinger expects hardware composer @@ -280,13 +277,6 @@ public: // The CompositionEngine encapsulates all composition related interfaces and actions. compositionengine::CompositionEngine& getCompositionEngine() const; - // Obtains a name from the texture pool, or, if the pool is empty, posts a - // synchronous message to the main thread to obtain one on the fly - uint32_t getNewTexture(); - - // utility function to delete a texture on the main thread - void deleteTextureAsync(uint32_t texture); - renderengine::RenderEngine& getRenderEngine() const; void onLayerFirstRef(Layer*); @@ -327,6 +317,11 @@ public: // on this behavior to increase contrast for some media sources. bool mTreat170mAsSrgb = false; + // If true, then screenshots with an enhanced render intent will dim in gamma space. + // The purpose is to ensure that screenshots appear correct during system animations for devices + // that require that dimming must occur in gamma space. + bool mDimInGammaSpaceForEnhancedScreenshots = false; + // Allows to ignore physical orientation provided through hwc API in favour of // 'ro.surface_flinger.primary_display_orientation'. // TODO(b/246793311): Clean up a temporary property @@ -535,9 +530,9 @@ private: EventRegistrationFlags eventRegistration = {}, const sp<IBinder>& layerHandle = nullptr); - status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); - status_t captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); - status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); + void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); + void captureDisplay(DisplayId, const sp<IScreenCaptureListener>&); + void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) @@ -567,7 +562,6 @@ private: const std::vector<ui::Hdr>& hdrTypes); status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success); status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers); - status_t getColorManagement(bool* outGetColorManagement) const; status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, ui::Dataspace* outWideColorGamutDataspace, ui::PixelFormat* outWideColorGamutPixelFormat) const; @@ -622,6 +616,9 @@ private: status_t removeWindowInfosListener( const sp<gui::IWindowInfosListener>& windowInfosListener) const; + status_t getStalledTransactionInfo( + int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result); + // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; @@ -836,10 +833,9 @@ private: // Boot animation, on/off animations and screen capture void startBootAnim(); - ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, - ui::Size bufferSize, ui::PixelFormat, - bool allowProtected, bool grayscale, - const sp<IScreenCaptureListener>&); + void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize, + ui::PixelFormat, bool allowProtected, bool grayscale, + const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> captureScreenCommon( RenderAreaFuture, GetLayerSnapshotsFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, @@ -942,7 +938,8 @@ private: template <typename Predicate> sp<DisplayDevice> findDisplay(Predicate p) const REQUIRES(mStateLock) { const auto it = std::find_if(mDisplays.begin(), mDisplays.end(), - [&](const auto& pair) { return p(*pair.second); }); + [&](const auto& pair) + REQUIRES(mStateLock) { return p(*pair.second); }); return it == mDisplays.end() ? nullptr : it->second; } @@ -1055,6 +1052,9 @@ private: VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock); void releaseVirtualDisplay(VirtualDisplayId); + // Returns a display other than `mActiveDisplayId` that can be activated, if any. + sp<DisplayDevice> getActivatableDisplay() const REQUIRES(mStateLock, kMainThreadContext); + void onActiveDisplayChangedLocked(const DisplayDevice* inactiveDisplayPtr, const DisplayDevice& activeDisplay) REQUIRES(mStateLock, kMainThreadContext); @@ -1086,6 +1086,7 @@ private: void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock); void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const; void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock); + void dumpHdrInfo(std::string& result) const REQUIRES(mStateLock); LayersProto dumpDrawingStateProto(uint32_t traceFlags) const; void dumpOffscreenLayersProto(LayersProto& layersProto, @@ -1208,6 +1209,8 @@ private: // latched. std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames; std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved; + std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames; + // Tracks layers that need to update a display's dirty region. std::vector<sp<Layer>> mLayersPendingRefresh; // Sorted list of layers that were composed during previous frame. This is used to @@ -1270,13 +1273,6 @@ private: TransactionCallbackInvoker mTransactionCallbackInvoker; - // We maintain a pool of pre-generated texture names to hand out to avoid - // layer creation needing to run on the main thread (which it would - // otherwise need to do to access RenderEngine). - std::mutex mTexturePoolMutex; - uint32_t mTexturePoolSize = 0; - std::vector<uint32_t> mTexturePool; - std::atomic<size_t> mNumLayers = 0; // to linkToDeath @@ -1507,7 +1503,6 @@ public: const std::vector<int32_t>& hdrTypes) override; binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override; binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override; - binder::Status getColorManagement(bool* outGetColorManagement) override; binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override; binder::Status getDisplayedContentSamplingAttributes( const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override; @@ -1559,6 +1554,8 @@ public: gui::WindowInfosListenerInfo* outInfo) override; binder::Status removeWindowInfosListener( const sp<gui::IWindowInfosListener>& windowInfosListener) override; + binder::Status getStalledTransactionInfo(int pid, + std::optional<gui::StalledTransactionInfo>* outInfo); private: static const constexpr bool kUsePermissionCache = true; diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp index ecdeabe528..b92d50b423 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.cpp +++ b/services/surfaceflinger/Tracing/LayerTracing.cpp @@ -27,12 +27,13 @@ #include <utils/Trace.h> #include "LayerTracing.h" -#include "RingBuffer.h" +#include "TransactionRingBuffer.h" namespace android { LayerTracing::LayerTracing() - : mBuffer(std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>()) {} + : mBuffer(std::make_unique<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>>()) { +} LayerTracing::~LayerTracing() = default; diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h index 40b0fbee18..7c0d23d5b6 100644 --- a/services/surfaceflinger/Tracing/LayerTracing.h +++ b/services/surfaceflinger/Tracing/LayerTracing.h @@ -30,7 +30,7 @@ using namespace android::surfaceflinger; namespace android { template <typename FileProto, typename EntryProto> -class RingBuffer; +class TransactionRingBuffer; class SurfaceFlinger; @@ -71,7 +71,7 @@ private: uint32_t mFlags = TRACE_INPUT; mutable std::mutex mTraceLock; bool mEnabled GUARDED_BY(mTraceLock) = false; - std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer + std::unique_ptr<TransactionRingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer GUARDED_BY(mTraceLock); size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024; }; diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/TransactionRingBuffer.h index b41c65b10e..e6f85ca4a5 100644 --- a/services/surfaceflinger/Tracing/RingBuffer.h +++ b/services/surfaceflinger/Tracing/TransactionRingBuffer.h @@ -32,7 +32,7 @@ namespace android { class SurfaceFlinger; template <typename FileProto, typename EntryProto> -class RingBuffer { +class TransactionRingBuffer { public: size_t size() const { return mSizeInBytes; } size_t used() const { return mUsedInBytes; } diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h index a59dc6e7dc..31bca5fc66 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.h +++ b/services/surfaceflinger/Tracing/TransactionTracing.h @@ -30,8 +30,8 @@ #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/Update.h" #include "LocklessStack.h" -#include "RingBuffer.h" #include "TransactionProtoParser.h" +#include "TransactionRingBuffer.h" using namespace android::surfaceflinger; @@ -81,7 +81,7 @@ private: static constexpr auto FILE_PATH = "/data/misc/wmtrace/transactions_trace.winscope"; mutable std::mutex mTraceLock; - RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer + TransactionRingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry> mBuffer GUARDED_BY(mTraceLock); size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE; std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h new file mode 100644 index 0000000000..198e7b2cf1 --- /dev/null +++ b/services/surfaceflinger/Utils/RingBuffer.h @@ -0,0 +1,68 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stddef.h> +#include <array> + +namespace android::utils { + +template <class T, size_t SIZE> +class RingBuffer { + RingBuffer(const RingBuffer&) = delete; + void operator=(const RingBuffer&) = delete; + +public: + RingBuffer() = default; + ~RingBuffer() = default; + + constexpr size_t capacity() const { return SIZE; } + + size_t size() const { return mCount; } + + T& next() { + mHead = static_cast<size_t>(mHead + 1) % SIZE; + if (mCount < SIZE) { + mCount++; + } + return mBuffer[static_cast<size_t>(mHead)]; + } + + T& front() { return (*this)[0]; } + + T& back() { return (*this)[size() - 1]; } + + T& operator[](size_t index) { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + const T& operator[](size_t index) const { + return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount]; + } + + void clear() { + mCount = 0; + mHead = -1; + } + +private: + std::array<T, SIZE> mBuffer; + int mHead = -1; + size_t mCount = 0; +}; + +} // namespace android::utils diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp index b2d4131b5d..ed8bb7fc93 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp @@ -144,9 +144,6 @@ void SurfaceFlingerFuzzer::invokeFlinger() { mFlinger->scheduleRepaint(); mFlinger->scheduleSample(); - uint32_t texture = mFlinger->getNewTexture(); - mFlinger->deleteTextureAsync(texture); - sp<IBinder> handle = defaultServiceManager()->checkService( String16(mFdp.ConsumeRandomLengthString().c_str())); LayerHandle::getLayer(handle); diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index b8068f79a4..2b1834d8e4 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -1479,15 +1479,11 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) { matrix[2][2] = 0.11; // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } + ColorTransformHelper::DegammaColor(expected); ColorTransformHelper::applyMatrix(expected, matrix); - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } + ColorTransformHelper::GammaColor(expected); const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), uint8_t(expected.b * 255), 255}; @@ -1537,15 +1533,11 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) { matrix[2][2] = 0.11; // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } + ColorTransformHelper::DegammaColor(expected); ColorTransformHelper::applyMatrix(expected, matrix); - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } + ColorTransformHelper::GammaColor(expected); const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), uint8_t(expected.b * 255), 255}; @@ -1608,16 +1600,12 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) { matrixParent[2][2] = 0.10; // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } + ColorTransformHelper::DegammaColor(expected); ColorTransformHelper::applyMatrix(expected, matrixChild); ColorTransformHelper::applyMatrix(expected, matrixParent); - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } + ColorTransformHelper::GammaColor(expected); const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), uint8_t(expected.b * 255), 255}; diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index badd5bebbc..2bdb8a452d 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -47,9 +47,6 @@ protected: ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); - binder::Status status = sf->getColorManagement(&mColorManagementUsed); - ASSERT_NO_FATAL_FAILURE(gui::aidl_utils::statusTFromBinderStatus(status)); - mCaptureArgs.displayToken = mDisplay; } @@ -282,7 +279,6 @@ protected: const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256; sp<SurfaceControl> mBlackBgSurface; - bool mColorManagementUsed; DisplayCaptureArgs mCaptureArgs; ScreenCaptureResults mCaptureResults; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index fa5fa956ed..3dd33b9957 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -148,6 +148,7 @@ cc_defaults { defaults: [ "android.hardware.graphics.common-ndk_static", "android.hardware.graphics.composer3-ndk_static", + "android.hardware.power-ndk_static", "librenderengine_deps", ], static_libs: [ @@ -161,7 +162,6 @@ cc_defaults { "android.hardware.power@1.1", "android.hardware.power@1.2", "android.hardware.power@1.3", - "android.hardware.power-V4-ndk", "libaidlcommonsupport", "libcompositionengine_mocks", "libcompositionengine", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index e8a9cfe9d7..8e13c0d9df 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -31,8 +31,6 @@ #include <gui/LayerMetadata.h> #include <log/log.h> #include <renderengine/mock/FakeExternalTexture.h> -#include <renderengine/mock/Framebuffer.h> -#include <renderengine/mock/Image.h> #include <renderengine/mock/RenderEngine.h> #include <system/window.h> #include <utils/String8.h> @@ -330,7 +328,7 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -381,7 +379,7 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - const bool, base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -580,7 +578,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -599,8 +597,6 @@ struct BaseLayerProperties { const renderengine::LayerSettings layer = layerSettings.back(); EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull())); EXPECT_THAT(layer.source.buffer.fence, Not(IsNull())); - EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName); - EXPECT_EQ(false, layer.source.buffer.isY410BT2020); EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha); EXPECT_EQ(false, layer.source.buffer.isOpaque); EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius.x); @@ -631,7 +627,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -713,7 +709,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + const std::shared_ptr<renderengine::ExternalTexture>&, base::unique_fd&&) -> std::future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -876,15 +872,11 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { using FlingerLayerType = sp<Layer>; static FlingerLayerType createLayer(CompositionTest* test) { - test->mFlinger.mutableTexturePool().push_back(DEFAULT_TEXTURE_ID); - - FlingerLayerType layer = - Base::template createLayerWithFactory<Layer>(test, [test]() { - LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", - LayerProperties::LAYER_FLAGS, LayerMetadata()); - args.textureName = test->mFlinger.mutableTexturePool().back(); - return sp<Layer>::make(args); - }); + FlingerLayerType layer = Base::template createLayerWithFactory<Layer>(test, [test]() { + LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", + LayerProperties::LAYER_FLAGS, LayerMetadata()); + return sp<Layer>::make(args); + }); LayerProperties::setupLayerState(test, layer); diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp index 60ad7a3a03..2d87ddd488 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp @@ -78,7 +78,7 @@ TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) { mDisplay->setDesiredActiveMode( {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); // Setting another mode should be cached but return None @@ -86,7 +86,7 @@ TEST_F(InitiateModeChangeTest, setDesiredActiveMode_setNewMode) { mDisplay->setDesiredActiveMode( {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); } @@ -105,7 +105,7 @@ TEST_F(InitiateModeChangeTest, initiateModeChange) NO_THREAD_SAFETY_ANALYSIS { mDisplay->setDesiredActiveMode( {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -136,7 +136,7 @@ NO_THREAD_SAFETY_ANALYSIS { mDisplay->setDesiredActiveMode( {scheduler::FrameRateMode{90_Hz, kMode90}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, mDisplay->getDesiredActiveMode()->modeOpt); + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); hal::VsyncPeriodChangeConstraints constraints{ @@ -154,7 +154,7 @@ NO_THREAD_SAFETY_ANALYSIS { mDisplay->setDesiredActiveMode( {scheduler::FrameRateMode{120_Hz, kMode120}, Event::None})); ASSERT_NE(std::nullopt, mDisplay->getDesiredActiveMode()); - EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, mDisplay->getDesiredActiveMode()->modeOpt); + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, *mDisplay->getDesiredActiveMode()->modeOpt); EXPECT_EQ(Event::None, mDisplay->getDesiredActiveMode()->event); EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, *mDisplay->getUpcomingActiveMode().modeOpt); diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp index 1c9aee7443..d30d5b8223 100644 --- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp @@ -99,8 +99,7 @@ void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) { } void RefreshRateSelectionTest::commitTransaction(Layer* layer) { - auto c = layer->getDrawingState(); - layer->commitTransaction(c); + layer->commitTransaction(); } namespace { diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index e475b843c9..f64ba2a439 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -17,6 +17,8 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <gui/fake/BufferData.h> + #include "Client.h" // temporarily needed for LayerCreationArgs #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/LayerHierarchy.h" @@ -333,6 +335,43 @@ protected: mLifecycleManager.applyTransactions(transactions); } + void setRoundedCorners(uint32_t id, float radius) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eCornerRadiusChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.cornerRadius = radius; + mLifecycleManager.applyTransactions(transactions); + } + + void setBuffer(uint32_t id, std::shared_ptr<renderengine::ExternalTexture> texture) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eBufferChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().externalTexture = texture; + transactions.back().states.front().state.bufferData = + std::make_shared<fake::BufferData>(texture->getId(), texture->getWidth(), + texture->getHeight(), texture->getPixelFormat(), + texture->getUsage()); + mLifecycleManager.applyTransactions(transactions); + } + + void setDataspace(uint32_t id, ui::Dataspace dataspace) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eDataspaceChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.dataspace = dataspace; + mLifecycleManager.applyTransactions(transactions); + } + LayerLifecycleManager mLifecycleManager; }; diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index a581d5b6ff..84c37759ba 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -17,11 +17,14 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <renderengine/mock/FakeExternalTexture.h> + #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" #include "FrontEnd/LayerSnapshotBuilder.h" #include "Layer.h" #include "LayerHierarchyTest.h" +#include "ui/GraphicTypes.h" #define UPDATE_AND_VERIFY(BUILDER, ...) \ ({ \ @@ -68,12 +71,17 @@ protected: setColor(id); } - void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges, - const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) { + void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) { if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) { mHierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); } + args.root = mHierarchyBuilder.getHierarchy(); + actualBuilder.update(args); + } + + void updateAndVerify(LayerSnapshotBuilder& actualBuilder, bool hasDisplayChanges, + const std::vector<uint32_t> expectedVisibleLayerIdsInZOrder) { LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), .layerLifecycleManager = mLifecycleManager, .includeMetadata = false, @@ -83,7 +91,7 @@ protected: .supportsBlur = true, .supportedLayerGenericMetadata = {}, .genericLayerMetadataKeyMap = {}}; - actualBuilder.update(args); + update(actualBuilder, args); // rebuild layer snapshots from scratch and verify that it matches the updated state. LayerSnapshotBuilder expectedBuilder(args); @@ -596,4 +604,56 @@ TEST_F(LayerSnapshotTest, framerate) { scheduler::LayerInfo::FrameRateCompatibility::Default); } +TEST_F(LayerSnapshotTest, translateDataspace) { + setDataspace(1, ui::Dataspace::UNKNOWN); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1})->dataspace, ui::Dataspace::V0_SRGB); +} + +TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) { + setRoundedCorners(1, 42.f); + setRoundedCorners(2, 42.f); + setCrop(1, Rect{1000, 1000}); + setCrop(2, Rect{1000, 1000}); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners()); + EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f); + EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners()); + + // add a buffer with the protected bit, check rounded corners are not set when + // skipRoundCornersWhenProtected == true + setBuffer(1, + std::make_shared< + renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 1ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_PROTECTED /*usage*/)); + + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = false, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}, + .skipRoundCornersWhenProtected = true}; + update(mSnapshotBuilder, args); + EXPECT_FALSE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners()); + // layer 2 doesn't have a buffer and should be unaffected + EXPECT_TRUE(getSnapshot({.id = 2})->roundedCorner.hasRoundedCorners()); + + // remove protected bit, check rounded corners are set + setBuffer(1, + std::make_shared<renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 2ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + update(mSnapshotBuilder, args); + EXPECT_TRUE(getSnapshot({.id = 1})->roundedCorner.hasRoundedCorners()); + EXPECT_EQ(getSnapshot({.id = 1})->roundedCorner.radius.x, 42.f); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index aaf55fbeae..0397b9936f 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -3125,5 +3125,69 @@ TEST_P(RefreshRateSelectorTest, frameRateIsLowerThanMinSupported) { {DisplayModeId(kModeId60), kLowerThanMin, kLowerThanMin})); } +// b/296079213 +TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_120) { + auto selector = createSelector(kModes_60_120, kModeId120); + + const FpsRange only120 = {120_Hz, 120_Hz}; + const FpsRange allRange = {0_Hz, 120_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId120, {only120, allRange}, {allRange, allRange}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "30Hz ExplicitExactOrMultiple"; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + + if (GetParam() != Config::FrameRateOverride::Enabled) { + EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } else { + EXPECT_FRAME_RATE_MODE(kMode120, 30_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } +} + +TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90) { + auto selector = createSelector(kModes_60_90, kModeId90); + + const FpsRange only90 = {90_Hz, 90_Hz}; + const FpsRange allRange = {0_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId90, {only90, allRange}, {allRange, allRange}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "30Hz ExplicitExactOrMultiple"; + layers[0].desiredRefreshRate = 30_Hz; + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + + if (GetParam() != Config::FrameRateOverride::Enabled) { + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } else { + EXPECT_FRAME_RATE_MODE(kMode90, 30_Hz, + selector.getBestScoredFrameRate(layers).frameRateMode); + } +} + +TEST_P(RefreshRateSelectorTest, frameRateOverrideInBlockingZone60_90_NonDivisor) { + auto selector = createSelector(kModes_60_90, kModeId90); + + const FpsRange only90 = {90_Hz, 90_Hz}; + const FpsRange allRange = {0_Hz, 90_Hz}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {kModeId90, {only90, allRange}, {allRange, allRange}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}}; + layers[0].name = "60Hz ExplicitExactOrMultiple"; + layers[0].desiredRefreshRate = 60_Hz; + layers[0].vote = LayerVoteType::ExplicitExactOrMultiple; + + EXPECT_FRAME_RATE_MODE(kMode90, 90_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); +} + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 44ab569bd1..a1e4e25c06 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -85,8 +85,7 @@ void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) { void SetFrameRateTest::commitTransaction() { for (auto layer : mLayers) { - auto c = layer->getDrawingState(); - layer->commitTransaction(c); + layer->commitTransaction(); } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp index 1cc9ba40bd..dbf0cd8772 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -91,7 +91,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { const auto& display = getCurrentDisplayState(displayToken); EXPECT_TRUE(display.isVirtual()); EXPECT_FALSE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); + EXPECT_EQ(name.c_str(), display.displayName); // -------------------------------------------------------------------- // Cleanup conditions @@ -123,7 +123,7 @@ TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { const auto& display = getCurrentDisplayState(displayToken); EXPECT_TRUE(display.isVirtual()); EXPECT_TRUE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); + EXPECT_EQ(name.c_str(), display.displayName); // -------------------------------------------------------------------- // Cleanup conditions diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index bd2344c60d..ed8d909e63 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -55,13 +55,17 @@ struct FoldableTest : DisplayTransactionTest { sp<DisplayDevice> mInnerDisplay, mOuterDisplay; }; -TEST_F(FoldableTest, foldUnfold) { +TEST_F(FoldableTest, promotesPacesetterOnBoot) { // When the device boots, the inner display should be the pacesetter. ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); // ...and should still be after powering on. mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); +} + +TEST_F(FoldableTest, promotesPacesetterOnFoldUnfold) { + mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); // The outer display should become the pacesetter after folding. mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); @@ -72,6 +76,10 @@ TEST_F(FoldableTest, foldUnfold) { mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF); mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); +} + +TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOn) { + mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); // The inner display should stay the pacesetter if both are powered on. // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates. @@ -81,6 +89,28 @@ TEST_F(FoldableTest, foldUnfold) { // The outer display should become the pacesetter if designated. mFlinger.scheduler()->setPacesetterDisplay(kOuterDisplayId); ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); + + // The inner display should become the pacesetter if designated. + mFlinger.scheduler()->setPacesetterDisplay(kInnerDisplayId); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); +} + +TEST_F(FoldableTest, promotesPacesetterOnConcurrentPowerOff) { + mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON); + + // The outer display should become the pacesetter if the inner display powers off. + mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); + + // The outer display should stay the pacesetter if both are powered on. + // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates. + mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId); + + // The inner display should become the pacesetter if the outer display powers off. + mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::OFF); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); } TEST_F(FoldableTest, doesNotRequestHardwareVsyncIfPoweredOff) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index e59d44d745..02fa4153ed 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -631,7 +631,6 @@ public: auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } - auto& mutableTexturePool() { return mFlinger->mTexturePool; } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; } diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 764d19be0b..00b5bf0506 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -61,10 +61,7 @@ public: return sp<Layer>::make(args); } - void commitTransaction(Layer* layer) { - auto c = layer->getDrawingState(); - layer->commitTransaction(c); - } + void commitTransaction(Layer* layer) { layer->commitTransaction(); } TestableSurfaceFlinger mFlinger; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index e2c64917dc..caa265fcd9 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -60,10 +60,7 @@ public: return sp<Layer>::make(args); } - void commitTransaction(Layer* layer) { - auto c = layer->getDrawingState(); - layer->commitTransaction(c); - } + void commitTransaction(Layer* layer) { layer->commitTransaction(); } TestableSurfaceFlinger mFlinger; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 809966f9d6..71a2d2b9a6 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -25,7 +25,6 @@ #include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/Update.h" #include "Tracing/LayerTracing.h" -#include "Tracing/RingBuffer.h" #include "Tracing/TransactionTracing.h" using namespace android::surfaceflinger; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h index 2b9520fca7..364618d61a 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h @@ -23,6 +23,7 @@ using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::SessionHint; +using aidl::android::hardware::power::SessionMode; using android::binder::Status; using namespace aidl::android::hardware::power; @@ -45,6 +46,7 @@ public: (override)); MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override)); MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override)); + MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h index ef9cd9bc43..4cfdd58c70 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameRateMode.h @@ -19,5 +19,7 @@ #include <scheduler/FrameRateMode.h> // Use a C style macro to keep the line numbers printed in gtest -#define EXPECT_FRAME_RATE_MODE(modePtr, fps, mode) \ - EXPECT_EQ((scheduler::FrameRateMode{(fps), (modePtr)}), (mode)) +#define EXPECT_FRAME_RATE_MODE(_modePtr, _fps, _mode) \ + EXPECT_EQ((scheduler::FrameRateMode{(_fps), (_modePtr)}), (_mode)) \ + << "Expected " << (_fps) << " (" << (_modePtr)->getFps() << ") but was " \ + << (_mode).fps << " (" << (_mode).modePtr->getFps() << ")" diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index f297da5511..1675584f5a 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -170,7 +170,7 @@ public: String8 err(String8::format("pixel @ (%3d, %3d): " "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", x, y, r, g, b, pixel[0], pixel[1], pixel[2])); - EXPECT_EQ(String8(), err) << err.string(); + EXPECT_EQ(String8(), err) << err.c_str(); } } diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp index f2870b021d..7eda9ef0c7 100644 --- a/services/vibratorservice/VibratorCallbackScheduler.cpp +++ b/services/vibratorservice/VibratorCallbackScheduler.cpp @@ -29,8 +29,11 @@ bool DelayedCallback::isExpired() const { return mExpiration <= std::chrono::steady_clock::now(); } -DelayedCallback::Timestamp DelayedCallback::getExpiration() const { - return mExpiration; +std::chrono::milliseconds DelayedCallback::getWaitForExpirationDuration() const { + std::chrono::milliseconds delta = std::chrono::duration_cast<std::chrono::milliseconds>( + mExpiration - std::chrono::steady_clock::now()); + // Return zero if this is already expired. + return delta > delta.zero() ? delta : delta.zero(); } void DelayedCallback::run() const { @@ -88,7 +91,9 @@ void CallbackScheduler::loop() { mCondition.wait(mMutex); } else { // Wait until next callback expires, or a new one is scheduled. - mCondition.wait_until(mMutex, mQueue.top().getExpiration()); + // Use the monotonic steady clock to wait for the measured delay interval via wait_for + // instead of using a wall clock via wait_until. + mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration()); } } } diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h index 2c194b5526..c8ec15d414 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h +++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h @@ -30,15 +30,13 @@ namespace vibrator { // Wrapper for a callback to be executed after a delay. class DelayedCallback { public: - using Timestamp = std::chrono::time_point<std::chrono::steady_clock>; - DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay) : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {} ~DelayedCallback() = default; void run() const; bool isExpired() const; - Timestamp getExpiration() const; + std::chrono::milliseconds getWaitForExpirationDuration() const; // Compare by expiration time, where A < B when A expires first. bool operator<(const DelayedCallback& other) const; @@ -46,7 +44,9 @@ public: private: std::function<void()> mCallback; - Timestamp mExpiration; + // Use a steady monotonic clock to calculate the duration until expiration. + // This clock is not related to wall clock time and is most suitable for measuring intervals. + std::chrono::time_point<std::chrono::steady_clock> mExpiration; }; // Schedules callbacks to be executed after a delay. diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp index 4c0910a75e..426cd426f7 100644 --- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp +++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp @@ -38,6 +38,9 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- +// Delay allowed for the scheduler to process callbacks during this test. +static const auto TEST_TIMEOUT = 50ms; + class VibratorCallbackSchedulerTest : public Test { public: void SetUp() override { @@ -67,46 +70,57 @@ protected: return std::vector<int32_t>(mExpiredCallbacks); } - bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) { - time_point<steady_clock> expiration = steady_clock::now() + timeout; - while (steady_clock::now() < expiration) { + int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) { + time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT; + int32_t expiredCallbackCount = 0; + while (steady_clock::now() < expirationTime) { std::lock_guard<std::mutex> lock(mMutex); - if (callbackCount <= mExpiredCallbacks.size()) { - return true; + expiredCallbackCount = mExpiredCallbacks.size(); + if (callbackCount <= expiredCallbackCount) { + return expiredCallbackCount; + } + auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>( + expirationTime - steady_clock::now()); + if (currentTimeout > currentTimeout.zero()) { + // Use the monotonic steady clock to wait for the requested timeout via wait_for + // instead of using a wall clock via wait_until. + mCondition.wait_for(mMutex, currentTimeout); } - mCondition.wait_until(mMutex, expiration); } - return false; + return expiredCallbackCount; } }; // ------------------------------------------------------------------------------------------------- TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) { - mScheduler->schedule(createCallback(1), 15ms); + time_point<steady_clock> startTime = steady_clock::now(); + mScheduler->schedule(createCallback(1), 50ms); - // Not triggered before delay. - ASSERT_FALSE(waitForCallbacks(1, 10ms)); - ASSERT_TRUE(getExpiredCallbacks().empty()); + ASSERT_EQ(1, waitForCallbacks(1, 50ms)); + time_point<steady_clock> callbackTime = steady_clock::now(); - ASSERT_TRUE(waitForCallbacks(1, 10ms)); + // Callback happened at least 50ms after the beginning of the test. + ASSERT_TRUE(startTime + 50ms <= callbackTime); ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1)); } TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) { - mScheduler->schedule(createCallback(1), 10ms); - mScheduler->schedule(createCallback(2), 5ms); - mScheduler->schedule(createCallback(3), 1ms); + // Schedule first callbacks long enough that all 3 will be scheduled together and run in order. + mScheduler->schedule(createCallback(1), 50ms); + mScheduler->schedule(createCallback(2), 40ms); + mScheduler->schedule(createCallback(3), 10ms); - ASSERT_TRUE(waitForCallbacks(3, 15ms)); + ASSERT_EQ(3, waitForCallbacks(3, 50ms)); ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1)); } TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) { - mScheduler->schedule(createCallback(1), 5ms); + // Schedule callback long enough that scheduler will be destroyed while it's still scheduled. + mScheduler->schedule(createCallback(1), 50ms); mScheduler.reset(nullptr); - // Should time out waiting for callback to run. - ASSERT_FALSE(waitForCallbacks(1, 10ms)); + // Should timeout waiting for callback to run. + ASSERT_EQ(0, waitForCallbacks(1, 50ms)); ASSERT_TRUE(getExpiredCallbacks().empty()); } diff --git a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp index 523f890ce7..d0a9da1ff5 100644 --- a/services/vr/virtual_touchpad/VirtualTouchpadService.cpp +++ b/services/vr/virtual_touchpad/VirtualTouchpadService.cpp @@ -113,7 +113,7 @@ status_t VirtualTouchpadService::dump( static_cast<long>(client_pid_)); touchpad_->dumpInternal(result); } - write(fd, result.string(), result.size()); + write(fd, result.c_str(), result.size()); return OK; } diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index 40cf9fbd67..e78f47003b 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -55,7 +55,12 @@ extern "C" { * This version of the extension is largely designed to clean up the mix of * GrallocUsage and GrallocUsage2 */ -#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 9 +/* + * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10 + * + * This version of the extension cleans up a bug introduced in version 9 + */ +#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" #define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \ @@ -69,6 +74,8 @@ extern "C" { VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2) #define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID \ VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 3) +#define VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 4) /* clang-format off */ typedef enum VkSwapchainImageUsageFlagBitsANDROID { @@ -152,6 +159,23 @@ typedef struct { VkImageUsageFlags imageUsage; } VkGrallocUsageInfoANDROID; +/* + * struct VkGrallocUsageInfo2ANDROID + * + * sType: VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * format: value specifying the format the image will be created with + * imageUsage: bitmask of VkImageUsageFlagBits describing intended usage + * swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID + */ +typedef struct { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageUsageFlags imageUsage; + VkSwapchainImageUsageFlagsANDROID swapchainImageUsage; +} VkGrallocUsageInfo2ANDROID; + /* DEPRECATED in SPEC_VERSION 6 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( VkDevice device, @@ -168,12 +192,18 @@ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); -/* ADDED in SPEC_VERSION 9 */ +/* DEPRECATED in SPEC_VERSION 10 */ typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage3ANDROID)( VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage); +/* ADDED in SPEC_VERSION 10 */ +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)( + VkDevice device, + const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, + uint64_t* grallocUsage); + typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)( VkDevice device, VkImage image, @@ -208,13 +238,20 @@ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( uint64_t* grallocProducerUsage ); -/* ADDED in SPEC_VERSION 9 */ +/* DEPRECATED in SPEC_VERSION 10 */ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage3ANDROID( VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage ); +/* ADDED in SPEC_VERSION 10 */ +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID( + VkDevice device, + const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, + uint64_t* grallocUsage +); + VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID( VkDevice device, VkImage image, diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 273cdd547e..bdba27e6a1 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -763,6 +763,17 @@ void CreateInfoWrapper::FilterExtension(const char* name) { continue; } + // Ignore duplicate extensions (see: b/288929054) + bool duplicate_entry = false; + for (uint32_t j = 0; j < filter.name_count; j++) { + if (strcmp(name, filter.names[j]) == 0) { + duplicate_entry = true; + break; + } + } + if (duplicate_entry == true) + continue; + filter.names[filter.name_count++] = name; if (ext_bit != ProcHook::EXTENSION_UNKNOWN) { if (ext_bit == ProcHook::ANDROID_native_buffer) @@ -1422,13 +1433,15 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, if ((wrapper.GetHalExtensions()[ProcHook::ANDROID_native_buffer]) && !data->driver.GetSwapchainGrallocUsageANDROID && !data->driver.GetSwapchainGrallocUsage2ANDROID && - !data->driver.GetSwapchainGrallocUsage3ANDROID) { + !data->driver.GetSwapchainGrallocUsage3ANDROID && + !data->driver.GetSwapchainGrallocUsage4ANDROID) { ALOGE( "Driver's implementation of ANDROID_native_buffer is broken;" " must expose at least one of " "vkGetSwapchainGrallocUsageANDROID or " "vkGetSwapchainGrallocUsage2ANDROID or " - "vkGetSwapchainGrallocUsage3ANDROID"); + "vkGetSwapchainGrallocUsage3ANDROID or " + "vkGetSwapchainGrallocUsage4ANDROID"); data->driver.DestroyDevice(dev, pAllocator); FreeDeviceData(data, data_allocator); diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 798af5c6a6..8f090083f8 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -512,6 +512,13 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkGetSwapchainGrallocUsage4ANDROID", + ProcHook::DEVICE, + ProcHook::ANDROID_native_buffer, + nullptr, + nullptr, + }, + { "vkGetSwapchainGrallocUsageANDROID", ProcHook::DEVICE, ProcHook::ANDROID_native_buffer, @@ -692,6 +699,7 @@ bool InitDriverTable(VkDevice dev, INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage3ANDROID); + INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage4ANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID); // clang-format on diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 31ba04ba1f..4527214c3f 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -128,6 +128,7 @@ struct DeviceDriverTable { PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID; PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID; PFN_vkGetSwapchainGrallocUsage3ANDROID GetSwapchainGrallocUsage3ANDROID; + PFN_vkGetSwapchainGrallocUsage4ANDROID GetSwapchainGrallocUsage4ANDROID; PFN_vkAcquireImageANDROID AcquireImageANDROID; PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID; // clang-format on diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp index a14fed222a..d059f8fe4c 100644 --- a/vulkan/libvulkan/layers_extensions.cpp +++ b/vulkan/libvulkan/layers_extensions.cpp @@ -23,6 +23,7 @@ #include <dlfcn.h> #include <string.h> #include <sys/prctl.h> +#include <unistd.h> #include <mutex> #include <string> @@ -362,6 +363,7 @@ template <typename Functor> void ForEachFileInZip(const std::string& zipname, const std::string& dir_in_zip, Functor functor) { + static const size_t kPageSize = getpagesize(); int32_t err; ZipArchiveHandle zip = nullptr; if ((err = OpenArchive(zipname.c_str(), &zip)) != 0) { @@ -389,7 +391,7 @@ void ForEachFileInZip(const std::string& zipname, // the APK. Loading still may fail for other reasons, but this at least // lets us avoid failed-to-load log messages in the typical case of // compressed and/or unaligned libraries. - if (entry.method != kCompressStored || entry.offset % PAGE_SIZE != 0) + if (entry.method != kCompressStored || entry.offset % kPageSize != 0) continue; functor(filename); } diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 07b95694d5..bffbe9d8d5 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1576,7 +1576,47 @@ VkResult CreateSwapchainKHR(VkDevice device, void* usage_info_pNext = nullptr; VkImageCompressionControlEXT image_compression = {}; uint64_t native_usage = 0; - if (dispatch.GetSwapchainGrallocUsage3ANDROID) { + if (dispatch.GetSwapchainGrallocUsage4ANDROID) { + ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID"); + VkGrallocUsageInfo2ANDROID gralloc_usage_info = {}; + gralloc_usage_info.sType = + VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID; + gralloc_usage_info.format = create_info->imageFormat; + gralloc_usage_info.imageUsage = create_info->imageUsage; + gralloc_usage_info.swapchainImageUsage = swapchain_image_usage; + + // Look through the pNext chain for an image compression control struct + // if one is found AND the appropriate extensions are enabled, + // append it to be the gralloc usage pNext chain + const VkSwapchainCreateInfoKHR* create_infos = create_info; + while (create_infos->pNext) { + create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>( + create_infos->pNext); + switch (create_infos->sType) { + case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: { + const VkImageCompressionControlEXT* compression_infos = + reinterpret_cast<const VkImageCompressionControlEXT*>( + create_infos); + image_compression = *compression_infos; + image_compression.pNext = nullptr; + usage_info_pNext = &image_compression; + } break; + + default: + // Ignore all other info structs + break; + } + } + gralloc_usage_info.pNext = usage_info_pNext; + + result = dispatch.GetSwapchainGrallocUsage4ANDROID( + device, &gralloc_usage_info, &native_usage); + ATRACE_END(); + if (result != VK_SUCCESS) { + ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result); + return VK_ERROR_SURFACE_LOST_KHR; + } + } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) { ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID"); VkGrallocUsageInfoANDROID gralloc_usage_info = {}; gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID; diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp index f998b1ad18..2e87f1718b 100644 --- a/vulkan/nulldrv/null_driver.cpp +++ b/vulkan/nulldrv/null_driver.cpp @@ -959,6 +959,17 @@ VkResult GetSwapchainGrallocUsage3ANDROID( return VK_SUCCESS; } +VkResult GetSwapchainGrallocUsage4ANDROID( + VkDevice, + const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, + uint64_t* grallocUsage) { + // The null driver never reads or writes the gralloc buffer + ALOGV("TODO: vk%s - grallocUsageInfo->format:%i", __FUNCTION__, + grallocUsageInfo->format); + *grallocUsage = 0; + return VK_SUCCESS; +} + VkResult AcquireImageANDROID(VkDevice, VkImage, int fence, diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index 0cb7bd3185..935535f5bf 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -262,6 +262,7 @@ const NameProc kInstanceProcs[] = { {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))}, {"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))}, {"vkGetSwapchainGrallocUsage3ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage3ANDROID>(GetSwapchainGrallocUsage3ANDROID))}, + {"vkGetSwapchainGrallocUsage4ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage4ANDROID>(GetSwapchainGrallocUsage4ANDROID))}, {"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))}, {"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))}, {"vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkMapMemory>(MapMemory))}, diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index 5c7fea0fa8..fb3bd05e07 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -210,6 +210,7 @@ VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescripto VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); VKAPI_ATTR VkResult GetSwapchainGrallocUsage3ANDROID(VkDevice device, const VkGrallocUsageInfoANDROID* grallocUsageInfo, uint64_t* grallocUsage); +VKAPI_ATTR VkResult GetSwapchainGrallocUsage4ANDROID(VkDevice device, const VkGrallocUsageInfo2ANDROID* grallocUsageInfo, uint64_t* grallocUsage); VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index c25c6cbda0..866c1b7b75 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -70,6 +70,7 @@ _OPTIONAL_COMMANDS = [ 'vkGetSwapchainGrallocUsageANDROID', 'vkGetSwapchainGrallocUsage2ANDROID', 'vkGetSwapchainGrallocUsage3ANDROID', + 'vkGetSwapchainGrallocUsage4ANDROID', ] # Dict for mapping dispatch table to a type. |